/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : TGAtom.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.scenegraphelements;

import java.util.Enumeration;
import java.util.Stack;
import java.util.Vector;

import javax.media.j3d.Behavior;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Node;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Matrix3d;
import javax.vecmath.Vector3d;

import org.apache.log4j.Logger;

import ciss.phase_viewer.acviewer.ChaseTransformGroup;
import ciss.phase_viewer.acviewer.ConfigData;
import ciss.phase_viewer.acviewer.ConfigDataManager;
import ciss.phase_viewer.acviewer.ConfigDataUpdateEvent;
import ciss.phase_viewer.acviewer.J3DDataManager;
import ciss.phase_viewer.acviewer.MainPanel;
import ciss.phase_viewer.acviewer.measure.Measurable;
import ciss.phase_viewer.acviewer.measure.Measure;
import ciss.phase_viewer.acviewer.measure.MeasureBranchGroup;
import ciss.phase_viewer.acviewer.measure.MeasureListener;
import ciss.phase_viewer.acviewer.mouselistener.MyMouseRotate;
import ciss.phase_viewer.acviewer.mouselistener.MyMouseTranslate;
import ciss.phase_viewer.acviewer.operations.BasicOperations;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomObject;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomObjectBG;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomPicker;
import ciss.phase_viewer.acviewer.scenegraphelements.atom.AtomSelectionCanvas;
import ciss.phase_viewer.acviewer.scenegraphelements.bond.BondCalculator;
import ciss.phase_viewer.acviewer.scenegraphelements.bond.BondInfo;
import ciss.phase_viewer.acviewer.scenegraphelements.bond.BondObject;
import ciss.phase_viewer.atomcoord.AtomCoords;
import ciss.phase_viewer.atomcoord.AtomState;
import ciss.phase_viewer.atomcoord.AtomStateList;
import ciss.phase_viewer.atomcoord.VolumetricData;
import ciss.phase_viewer.common.TaggedString;
import ciss.phase_viewer.common.VectorOperations;
import ciss.phase_viewer.settings.GlobalProperties;
import ciss.phase_viewer.settings.PropertiesManager;

public class TGAtom extends ChaseTransformGroup implements AtomSelectionCanvas,
        Measurable, ConfigData {
    private static Logger logger = Logger.getLogger(TGAtom.class.getName());

    private SceneGraphElementCreator creator;

    private MainPanel mp;

    private double lenmax;

    private double[] jusin;

    private Measure measure;

    private boolean measureAtWork = false;

    private Stack selectedAtoms = new Stack();

    private ConfigDataManager mCD;

    private J3DDataManager mACVD;

    private double[][] Dpos;

    private double[][] Dforce;

    private int NumAt;

    private double[][] cellvec;

    private BondCalculator clcbnd;

    private BondCalculator hbondCalculator;

    private boolean rescaleOnUpdate = true;

    private Transform3D buffer = new Transform3D();

    private Vector measures;

    private Vector measureListeners;

    private GlobalProperties props = PropertiesManager
            .getGlobalProperties(PropertiesManager.PROPERTIES_ACV);

    public static int ANIM = 0;

    public static int GENERIC = 1;

    private int updateMode = GENERIC;

    private BranchGroup atomBranchGroup;

    private TransformGroup atomTransformGroup;

    private Vector3d zero = new Vector3d(0, 0, 0);

    private Vector3d atomOffset = zero; // 原子の座標を"並進"させるのに使う. -0.5から0.5の値で,

    // a,b,c軸に対する並進量を指定する.

    private Vector atomSelectionCanvas;

    private BasicOperations basicOperations;

    public TGAtom() {
        super();
    }

    public void setParentFrame(MainPanel mp) {
        this.mp = mp;
    }

    /**
     * 原子配置に変更を施すためのクラスである, BasicOperationsのインスタンスを取得する
     * 
     * @return 上記の通り
     */
    public BasicOperations getBasicOperations() {
        if (basicOperations == null)
            basicOperations = new BasicOperations(this.mp);
        return this.basicOperations;
    }

    /**
     * 原子配置ビューアーの一番大元のパネルへの参照を返す.
     * 
     * @return ビューアーの一番大元のパネル
     */
    public MainPanel getParentFrame() {
        return mp;
    }

    /**
     * 「原子選択オブジェクト」を登録する.
     * 
     * @param picker
     *            登録したい原子選択用オブジェクト.
     */
    public void setPicker(AtomPicker picker) {
        picker.setParent((TransformGroup) this);
        BranchGroup bg = new BranchGroup();
        bg.addChild(picker);
        this.addChild(bg);
    }

    /**
     * 原子選択/非選択後に何かしてほしいクラスを登録しておく
     */
    public void addAtomSelectionCanvas(AtomSelectionCanvas foo) {
        if (atomSelectionCanvas == null)
            atomSelectionCanvas = new Vector();
        atomSelectionCanvas.addElement(foo);
    }

    /**
     * 原子選択/非選択後に何かしてほしいクラスを登録削除する
     */
    public void removeAtomSelectionCanvas(AtomSelectionCanvas foo) {
        if (atomSelectionCanvas == null)
            return;
        atomSelectionCanvas.remove(foo);
    }

    /**
     * 座標などの更新を行う際の方針をセット. ANIMの場合, 原子数や原子種は変わらないことを仮定する. GENERICの場合, 原子数,
     * 原子種なども変化しうる
     * 
     * @param updateMode
     *            更新を行う際の方針
     */
    public void setUpdateMode(int updateMode) {
        this.updateMode = updateMode;
    }

    /**
     * 測定終了後になんらかの処理を施したいクラスはこのメソッドを介して自身を登録する.
     * 
     * @param list
     *            測定終了を通知したいリスナー
     */
    public void addMeasureListener(MeasureListener list) {
        if (measureListeners == null) {
            measureListeners = new Vector();
        }
        measureListeners.addElement(list);
    }

    /**
     * MeasureListenerを登録削除する
     * 
     * @param list
     *            削除したいMeasureListener
     */
    public void removeMeasureListener(MeasureListener list) {
        if (measureListeners == null) {
            return;
        }
        measureListeners.remove(list);
    }

    /**
     * 原子の座標のオフセットを指定する. 配列の各要素はそれぞれa,b,c軸に対してどのくらい 並進するかを, -0.5から0.5の値で指定する.
     * それら以外の値だった場合何も起こらないので注意
     * 
     * @param off
     *            並進量; a,b,c軸に対する値
     */
    public void setAtomOffset(Vector3d off) {
        if (Math.abs(off.x) > 0.5 || Math.abs(off.y) > 0.5
                || Math.abs(off.z) > 0.5)
            return;
        this.atomOffset = off;
    }

    private Vector3d offset;

    /**
     * オフセットを, セルに対して-0.5から0.5の値でセット.
     * 
     * @param offset
     *            オフセット量
     */
    public void setOffset(Vector3d offset) {
        this.offset = offset;
    }

    /**
     * -0.5から0.5の間の数字のオフセットを取得.
     * 
     * @return
     */
    public Vector3d getOffset() {
        return this.offset;
    }

    /**
     * セルのオフセットなどもふくめ重心をゲット.
     * 
     * @return 系の重心.
     */
    public double[] getJusin() {
        double[] jusin_buff = getConfigData().getCellOriginVector();
        double[] celloff = getCellOffset();
        for (int i = 0; i < 3; i++)
            jusin_buff[i] = -(jusin_buff[i] + celloff[i] * lenmax);

        return jusin_buff;
    }

    /**
     * 原子の座標のオフセット量をゲットする. a,b,c軸のfractionでの値で, -0.5から0.5の値であることに 注意が必要
     * 
     * @return 上記の通り
     */
    public Vector3d getAtomOffset() {
        return this.atomOffset;
    }

    /**
     * @return セルベクトル; ただしnullな場合もある. また, Sceneに併せてscaleしたものを返す.
     */
    public float[][] getEffectiveBounds() {
        double[][] ce = mCD.getCell();
        double len = mCD.getLenMax();
        logger.debug("lenmax: " + len);
        if (ce == null)
            return null;
        float[][] ret = new float[3][3];
        for (int i = 0; i < 3; i++) {
            // logger.debug("cell"+i+": "+ce[i][0]+", "+ce[i][1]+", "+ce[i][2]);
            for (int j = 0; j < 3; j++) {
                ret[i][j] = (float) (ce[i][j] / len);
            }
        }
        return ret;
    }

    /**
     * Measureを登録する.
     * 
     * @param measure
     *            登録したいmeasureオブジェクト
     */
    public void registerMeasure(Measure measure) {
        registerMeasure(measure, true);
    }

    /**
     * Measureを登録する.
     * 
     * @param measure
     *            登録したいmeasureオブジェクト
     * @param deselctAll
     *            "選択中の原子はすべて選択解除する"という操作を行わないならfalse(defaultはtrue)
     */
    public void registerMeasure(Measure measure, boolean deselctAll) {
        if (this.measure != null && measure != null) {
            if (this.measure.getClass().getName()
                    .equals(measure.getClass().getName())) {
                endMeasure();
            }
        }
        this.measure = measure;
        if (measures == null) {
            measures = new Vector();
        }
        measures.add(measure);
        this.startMeasure(deselctAll);
    }

    public MeasureBranchGroup[] getExistingMeasures() {
        Node[] bgroups = getChildren(MeasureBranchGroup.class);
        if (bgroups == null || bgroups.length == 0)
            return null;
        MeasureBranchGroup[] ret = new MeasureBranchGroup[bgroups.length];
        for (int i = 0; i < bgroups.length; i++) {
            ret[i] = (MeasureBranchGroup) bgroups[i];
        }
        return ret;
    }

    /**
     * 現在登録されているMeasureを, 測定状態/非測定状態の間でトグルする.
     */
    public void toggleMeasure() {
        if (measureAtWork && measure != null) {
            endMeasure();
        } else {
            startMeasure();
        }
    }

    public void startMeasure() {
        startMeasure(true);
    }

    /**
     * 「測定(距離・ボンド角など)」開始後に呼ばれる.
     */
    public void startMeasure(boolean deselectAll) {
        if (deselectAll) {
            deselectAll();
        }
        measureAtWork = true;
        logger.debug("start measure");
    }

    /**
     * 「測定(距離・ボンド角など)」終了後に呼ばれる.
     */
    public void endMeasure() {
        deselectAll();
        measureAtWork = false;
        measure = null;
        logger.debug("end measure");
    }

    /**
     * Measureを一時的に止める
     */
    public void stopMeasure() {
        measureAtWork = false;
    }

    /**
     * 一旦止めたMeasureを再開
     */
    public void restartMeasure() {
        measureAtWork = true;
    }

    /**
     * 引数で指定された原子が選択された後に呼ばれるメソッド.
     * 
     * @param obj
     *            選択された原子
     */
    public void selected(AtomObject obj) {
        if (atomSelectionCanvas != null) {
            for (int i = 0; i < atomSelectionCanvas.size(); i++) {
                ((AtomSelectionCanvas) atomSelectionCanvas.get(i))
                        .selected(obj);
            }
        }
        // BranchGroup root = mp.getRootBranch();
        // Enumeration enu = root.getAllChildren();
        // while (enu.hasMoreElements()) {
        // Object object = enu.nextElement();
        // if (object instanceof MouseTranslate) {
        // ((Behavior) object).setEnable(false);
        // }
        // }

        selectedAtoms.push(obj);
        // doBond();

        if (measure == null || measure.getNumberOfNecessaryAtoms() < 0) {
            return;
        }
        if (measureAtWork) {
            if (selectedAtoms.size() >= measure.getNumberOfNecessaryAtoms()) {
                measure.measure(selectedAtoms);
                measure.draw();
                deselectAll();
                if (measureListeners != null) {
                    for (int i = 0; i < measureListeners.size(); i++) {
                        MeasureListener list = (MeasureListener) measureListeners
                                .get(i);
                        list.measured(measure);
                    }
                }
            }
        }

    }

    private void doBond() {
        // for ( int i=0 ; i<bonds.size() ; i++ ) {
        // ((BondInfo)bonds.get(i)).getBond().createBuffer();
        // }
        // int [] satoms = getSelectedAtomsIndex();
        //
        // if ( satoms == null || satoms.length == 0 ){
        // return;
        // }
        //
        // for ( int i=0 ; i<bonds.size() ; i++ ) {
        // BondInfo inf = (BondInfo) bonds.get(i);
        // inf.getBond().nullifyBuffer();
        // }
        //
        // for ( int j=0 ; j<satoms.length-1 ; j++ ) {
        // for ( int k=j+1 ; k<satoms.length ; k++ ) {
        // iloop:
        // for ( int i=0 ; i<bonds.size() ; i++ ) {
        // BondInfo inf = (BondInfo) bonds.get(i);
        // int [] indpair = inf.getIndexPair();
        // if ( (satoms[j]==indpair[0] && satoms[k]==indpair[1]) ||
        // (satoms[j]==indpair[1] && satoms[k]==indpair[0]) ) {
        // inf.getBond().createBuffer();
        // break iloop;
        // }
        // }
        // }
        // }
    }

    /**
     * 全てのMeasureオブジェクトが削除される.
     */
    public void removeMeasureObjects() {
        Enumeration enumeration = getAllChildren();
        while (enumeration.hasMoreElements()) {
            Object obj = enumeration.nextElement();
            if (obj instanceof MeasureBranchGroup) {
                ((MeasureBranchGroup) obj).detach();
            }
        }
        if (measures != null) {
            for (int i = measures.size() - 1; i <= 0; i--) {
                Object obj = measures.get(i);
                measures.remove(i);
                obj = null;
            }
        }
    }

    /**
     * 全てのMeasureオブジェクトを更新する. 動画再生時につかう.
     */
    public void updateMeasureObjects() {
        if (measures == null)
            return;
        for (int i = 0; i < measures.size(); i++) {
            ((Measure) measures.get(i)).update(getAtomObjects());
            if (measureListeners != null) {
                for (int j = 0; j < measureListeners.size(); j++) {
                    ((MeasureListener) measureListeners.get(j))
                            .measureUpdated((Measure) measures.get(i));
                }
            }
        }
    }

    /**
     * Measureが終わったあとに足されるオブジェクト
     * 
     * @param shape
     *            足しこまれるべきオブジェクト.
     */
    public void addShape(BranchGroup shape) {
        this.addChild(shape);
    }

    /**
     * このクラスに関連付けられたSceneGraphElementCreatorオブジェクトを返す.
     * 
     * @return このクラスに関連付けられたSceneGraphElementCreator
     */
    public SceneGraphElementCreator getCreator() {
        return this.creator;
    }

    /**
     * 引数で与えられた原子が選択解除された後に呼ばれる.
     * 
     * @param obj
     *            選択解除されたAtomObject
     */
    public void deselected(AtomObject obj) {
        if (selectedAtoms.remove(obj)) {
            logger.debug("removed atom " + obj.getID()
                    + " from 'selected' list");
        }

        // doBond();

        // if (selectedAtoms.empty()) {
        // BranchGroup root = mp.getRootBranch();
        // Enumeration enu = root.getAllChildren();
        // while (enu.hasMoreElements()) {
        // Object object = enu.nextElement();
        // if (object instanceof MyMouseTranslate) {
        // ((MyMouseTranslate) object).clear();
        // ((Behavior) object).setEnable(true);
        // }
        // }
        // }

        if (atomSelectionCanvas != null) {
            for (int i = 0; i < atomSelectionCanvas.size(); i++) {
                ((AtomSelectionCanvas) atomSelectionCanvas.get(i))
                        .deselected(obj);
            }
        }
    }

    /**
     * 「選択中」の全原子を非選択状態にする.
     */
    public void deselectAll() {
        AtomObject[] selected = getSelectedAtoms();
        if (selected == null) {
            return;
        }
        logger.debug("removing all atoms.");
        int n = selected.length;
        for (int i = 0; i < n; i++) {
            selected[i].unSelect();
            if (atomSelectionCanvas != null) {
                for (int j = 0; j < atomSelectionCanvas.size(); j++) {
                    ((AtomSelectionCanvas) atomSelectionCanvas.get(j))
                            .deselected(selected[i]);
                }
            }
        }
        BranchGroup root = mp.getRootBranch();
        Enumeration enu = root.getAllChildren();

        selectedAtoms = new Stack();
        while (enu.hasMoreElements()) {
            Object object = enu.nextElement();
            if (object instanceof MyMouseTranslate) {
                ((MyMouseTranslate) object).clear();
                ((Behavior) object).setEnable(true);
            }
        }
    }

    /**
     * 「全体の右クリック→ドラッグによる回転」を制御
     * 
     * @param enable
     *            trueの時回転可能, falseの時回転不可となる.
     */
    public void setRotationEnabled(boolean enable) {
        BranchGroup root = mp.getRootBranch();
        Enumeration enu = root.getAllChildren();
        while (enu.hasMoreElements()) {
            Object object = enu.nextElement();
            if (object instanceof MyMouseRotate) {
                ((MyMouseRotate) object).init();
                ((Behavior) object).setEnable(enable);
            }
        }
    }

    /**
     * 原子配置の差し渡しの長さを返す.
     * 
     * @param 原子配置の
     *            , 差し渡しの長さ.
     */
    public double getLenMax() {
        return mCD.getLenMax();
    }

    /**
     * 原子配置の重心を返す.
     * 
     * @return 原子配置の重心
     */
    public double[] getCOM() {
        return mCD.getCOM();
    }

    public void cleanUp() {
        if (bonds != null) {
            for (int i = 0; i < bonds.size(); i++) {
                ((BondInfo) bonds.get(i)).nullify();
            }
            bonds.clear();
            bonds = null;
        }
        if (mCD != null && mCD.getAtomCoords() != null) {
            mCD.getAtomCoords().nullify();
        }
        Dpos = null;
        if (mp != null && mp.getFramesAC() != null) {
            Vector vec = mp.getFramesAC();
            for (int i = 0; i < vec.size(); i++) {
                AtomCoords co = (AtomCoords) vec.get(i);
                co.nullify();
                co = null;
            }
            vec.clear();
        }
    }

    /**
     * 「シーングラフを作成するクラス」をセット.
     * 
     * @param creator
     *            シーングラフ作成用オブジェクト.
     */
    public void setSceneGraphElementCreator(SceneGraphElementCreator creator) {
        this.creator = creator;
        mCD = creator.getConfigData();
        mACVD = creator.getACVData();
        mCD.register((ConfigData) this);
    }

    /**
     * このオブジェクトに関連付けられた原子配置のデータを取得.
     * 
     * @return 原子配置データ.
     */
    public ConfigDataManager getConfigData() {
        return creator.getConfigData();
    }

    /**
     * このオブジェクトに関連付けられたACVDataMangerオブジェクトへの参照を取得.
     * 
     * @return このオブジェクトに関連付けられたACVDataManagerオブジェクト.
     */
    public J3DDataManager getACVData() {
        return creator.getACVData();
    }

    /**
     * 原子, ボンド, セルを作成し足しこむ.
     */
    public void create() {
        prepare(true);
        addAtoms();
        addBond();
        addHbonds();
        addCell();
    }

    /**
     * 選択中だった原子を, 再度選択中状態にする.
     * 
     * @param last
     *            前回の原子数(原子数の増減があった場合は何もしない).
     * @param selectedBuffer
     *            選択中だった原子のインデックス配列
     */
    public void restoreSelection(int last, int[] selectedBuffer) {
        logger.debug("restoreing selection...");
        logger.debug("numat now: " + mCD.getNumAt() + ", previous numat: "
                + last);
        if (selectedBuffer == null || selectedBuffer.length == 0) {
            return;
        }
        logger.debug("size of 'selected' buffer: " + selectedBuffer.length);
        if (mCD.getNumAt() == last) {
            AtomObject[] atoms = getAtomObjects();
            if (atoms == null || selectedBuffer == null) {
                return;
            }
            for (int i = 0; i < atoms.length; i++) {
                for (int j = 0; j < selectedBuffer.length; j++) {
                    if (Integer.parseInt(atoms[i].getID()) - 1 == selectedBuffer[j]) {
                        atoms[i].setSelected();
                    }
                }
            }
        }
    }

    private void prepare() {
        prepare(false);
    }

    public void prepare(boolean rescaleOnUpdate) {
        NumAt = mCD.getNumAt();

        double[][] DposOrig = mCD.getPos();
        if (DposOrig == null)
            return;
        Dpos = new double[NumAt][3];
        for (int i = 0; i < NumAt; i++) {
            // logger.debug("DposOrig: "+DposOrig[i][0]+" "+DposOrig[i][1]+"
            // "+DposOrig[i][2]);
            for (int j = 0; j < 3; j++) {
                Dpos[i][j] = DposOrig[i][j];
            }
        }

        if (rescaleOnUpdate) {
            mCD.doShiftScale();
        }

        double lenmax = mCD.getLenMax();
        double[] COM = mCD.getCOM();
        for (int i = 0; i < NumAt; i++) {
            for (int j = 0; j < 3; j++) {
                double foo = Dpos[i][j];
                foo -= COM[j];
                foo /= lenmax;
                Dpos[i][j] = foo;
            }
        }
        Dforce = mCD.getForce();
    }

    private double[] cellOffset = { 0, 0, 0 };

    /**
     * J3D座標でセルがどれくらい並進したかを返す.
     * 
     * @return 上記の通り
     */
    public double[] getCellOffset() {
        return cellOffset;
        // CellObject co = getCellObject();
        // if ( co==null )
        // return new double []{0,0,0};
        //
        // TransformGroup[] tg = co.getTransforms();
        // Transform3D t3d = new Transform3D();
        // tg[0].getTransform(t3d);
        // Vector3d v3 = new Vector3d();
        // t3d.get(v3);
        // cellOffset = new double[] { v3.x, v3.y, v3.z };
        // return cellOffset;
    }

    private boolean[] applyPBC(double[][] coords) {
        float[][] ce = getEffectiveBounds();
        double[][] ced = new double[3][3];
        double[][] sced = new double[3][3];
        double[][] plane01 = new double[2][3];
        double[][] plane02 = new double[2][3];
        double[][] plane12 = new double[2][3];
        lenmax = mCD.getLenMax();
        double[] foo = mCD.getCOM();
        double[] orig = new double[3];
        double[] coff = getCellOffset();
        double[] corig = mCD.getCellOriginVector();
        for (int i = 0; i < foo.length; i++) {
            orig[i] = corig[i] + coff[i] * lenmax;
            // orig[i] = coff[i]*lenmax;
            logger.debug("cell offset, cell origin, jusin: " + coff[i] + ", "
                    + corig[i] / lenmax + ", " + foo[i] / lenmax);
        }

        for (int i = 0; i < 3; i++) {
            ced[0][i] = (double) ce[0][i] * lenmax;
            ced[1][i] = (double) ce[1][i] * lenmax;
            ced[2][i] = (double) ce[2][i] * lenmax;
            sced[0][i] = ced[0][i] + orig[i];
            sced[1][i] = ced[1][i] + orig[i];
            sced[2][i] = ced[2][i] + orig[i];
            plane01[0][i] = ced[0][i];
            plane01[1][i] = ced[1][i];
            plane02[0][i] = ced[2][i];
            plane02[1][i] = ced[0][i];
            plane12[0][i] = ced[1][i];
            plane12[1][i] = ced[2][i];
        }

        int shifted = 0;

        boolean[] bshifted = new boolean[coords.length];
        for (int i = 0; i < coords.length; i++) {
            double[] pos = coords[i];
            for (int j = 0; j < 3; j++)
                pos[j] -= foo[j];
            double r01 = VectorOperations.getDistanceFromPlane(pos, plane01[0],
                    plane01[1], orig, true);
            double r10 = VectorOperations.getDistanceFromPlane(pos, plane01[1],
                    plane01[0], sced[2], true);

            double r02 = VectorOperations.getDistanceFromPlane(pos, plane02[0],
                    plane02[1], orig, true);
            double r20 = VectorOperations.getDistanceFromPlane(pos, plane02[1],
                    plane02[0], sced[1], true);

            double r12 = VectorOperations.getDistanceFromPlane(pos, plane12[0],
                    plane12[1], orig, true);
            double r21 = VectorOperations.getDistanceFromPlane(pos, plane12[1],
                    plane12[0], sced[0], true);

            if (r01 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] += ced[2][j];
                }
            } else if (r10 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] -= ced[2][j];
                }
            }

            if (r02 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] += ced[1][j];
                }
            } else if (r20 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] -= ced[1][j];
                }
            }

            if (r12 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] += ced[0][j];
                }
            } else if (r21 < 0) {
                shifted++;
                bshifted[i] = true;
                for (int j = 0; j < 3; j++) {
                    pos[j] -= ced[0][j];
                }
            }

            for (int j = 0; j < 3; j++)
                pos[j] += foo[j];
            coords[i] = pos;
        }
        logger.debug("shifted atoms count: " + shifted);
        return bshifted;
    }

    /**
     * 周期境界条件に従い必要に応じて原子の座標をシフトする
     * 
     * @return シフトされた原子はfalseとなるboolean配列を返す
     */
    private boolean[] doShift() {

        float[][] ce = getEffectiveBounds();
        if (ce == null)
            return null;

        // double [][] coords = mCD.getPos();
        VolumetricData[] vdata = mp.getAssociatedVolumetricData();
        if (vdata != null) {
            logger.debug("vdata update start");
            int n1 = (int) (vdata[0].getNumDiv()[0] * atomOffset.x);
            int n2 = (int) (vdata[0].getNumDiv()[1] * atomOffset.y);
            int n3 = (int) (vdata[0].getNumDiv()[2] * atomOffset.z);
            atomOffset.x = (double) n1 / ((double) vdata[0].getNumDiv()[0] - 1); // 電荷のメッシュと整合するようにシフト量を調整
            atomOffset.y = (double) n2 / ((double) vdata[0].getNumDiv()[1] - 1);
            atomOffset.z = (double) n3 / ((double) vdata[0].getNumDiv()[2] - 1);
            logger.debug("vdata update end");
        }
        double xoff = atomOffset.x * ce[0][0] + atomOffset.y * ce[1][0]
                + atomOffset.z * ce[2][0];
        double yoff = atomOffset.x * ce[0][1] + atomOffset.y * ce[1][1]
                + atomOffset.z * ce[2][1];
        double zoff = atomOffset.x * ce[0][2] + atomOffset.y * ce[1][2]
                + atomOffset.z * ce[2][2];
        // for ( int i=0 ; i<coords.length ; i++ ) {
        // coords[i][0] += xoff;
        // coords[i][1] += yoff;
        // coords[i][2] += zoff;
        // }
        logger.debug("offset: " + xoff + ", " + yoff + ", " + zoff);

        Transform3D currtrans = new Transform3D();
        getTransform(currtrans);
        Vector3d tra = new Vector3d();
        currtrans.get(tra);
        Matrix3d rot = new Matrix3d();
        currtrans.get(rot);
        rot.invert();
        rot.transform(tra);
        tra.add(new Vector3d(xoff, yoff, zoff));
        rot.invert();
        rot.transform(tra);

        currtrans.setTranslation(new Vector3d(tra));
        setTransform(currtrans);

        CellObject co = getCellObject(); // セルは元に戻す
        if (co != null) {
            logger.debug("shifting cell back.");
            TransformGroup[] cs = co.getTransforms();
            co.detach();
            addChild(co);
            for (int i = 0; i < cs.length; i++) {
                cs[i].getTransform(currtrans);
                currtrans.get(tra);
                tra.sub(new Vector3d(xoff, yoff, zoff));
                currtrans.setTranslation(tra);
                cs[i].setTransform(currtrans);
            }
        }

        cellOffset[0] -= xoff;
        cellOffset[1] -= yoff;
        cellOffset[2] -= zoff;

        AtomObject[] as = getAtomObjects();
        double[] com = mCD.getCOM();
        double len = mCD.getLenMax();
        double[][] newcoords = mCD.getPos();
        // for ( int i=0 ; i<as.length ; i++ ) {
        // double [] pos = as[i].getPosDouble();
        // double [] newpos = new double[]{pos[0],pos[1],pos[2]};
        // // as[i].setPos(newpos);
        // newcoords[i] = new
        // double[]{newpos[0]*len+com[0],newpos[1]*len+com[1],newpos[2]*len+com[2]};
        // }

        boolean[] valid = applyPBC(newcoords);
        // for ( int i=0 ; i<newcoords.length ; i++ ) {
        // double [] newpos = new
        // double[]{(newcoords[i][0]-com[0])/len,(newcoords[i][1]-com[1])/len,(newcoords[i][2]-com[2])/len};
        // as[i].setPos(newpos);
        // }
        getConfigData().getAtomCoords().setPosDouble(newcoords);

        // getConfigData().getAtomCoords().setPosDouble(newcoords);
        atomOffset = zero;
        return valid;
    }

    /**
     * 原子の状態を更新する.
     */
    public void atomUpdate() {
        logger.debug("updating atom...");
        boolean[] valid = new boolean[getAtomObjects().length];
        for (int i = 0; i < valid.length; i++) {
            valid[i] = true;
        }

        /* オフセットが定義されている場合, まずシフト操作 */
        if (!atomOffset.epsilonEquals(zero, 0)) {
            valid = doShift();
            if (valid == null) {
                logger.error("invalid cell...");
                return;
            }
        }

        AtomStateList state = getConfigData().getAtomCoords().getAtomState();
        if (state == null) { // この場合, 原子の数に増減はない, はず
            if (mCD.forceUpdate())
                mCD.setScaleAtom(mACVD.getScaleAtom()); // スケールし直す
            AtomObject[] aobj = getAtomObjects();
            for (int i = 0; i < aobj.length; i++) {
                if (valid[i])
                    aobj[i].configDataUpdate();
            }
            logger.debug("done atom update");
            return;
        }

        /* 原子の数が変化し得る場合の更新 */
        logger.debug("num. atoms may have changed...");
        AtomObjectBG[] obj = getAtomObjectBGs();
        elements = mCD.getAtomCoords().getElements();
        auxils = mCD.getAtomCoords().getAuxils();

        AtomState[] res = state.getResults();
        for (int i = 0; i < res.length; i++) {
            if (res[i].origIndex >= obj.length)
                continue;
            if (res[i].mode == AtomState.REMOVED) {
                obj[res[i].origIndex].detach();
                obj[res[i].origIndex] = null;
            }
            if (res[i].mode == AtomState.MODIFIED) {
                obj[res[i].origIndex].getAssociatedAtomObject().setID(
                        res[i].index + 1);
                obj[res[i].origIndex].getAssociatedAtomObject()
                        .configDataUpdate();
            }
            if (res[i].mode == AtomState.UNCHANGED) {
                obj[res[i].origIndex].getAssociatedAtomObject().setID(
                        res[i].index + 1);
            }
        }

        for (int i = 0; i < res.length; i++) {
            if (res[i].mode == AtomState.ADDED) {
                int ind = res[i].index;
                logger.debug("adding atom : " + ind);
                BranchGroup bg = createAtom(ind);
                if (bg == null) {
                    logger.debug("null atom at : " + ind);
                    continue;
                }
                if (ind >= atomBranchGroup.numChildren()) {
                    atomBranchGroup.addChild(bg);
                } else {
                    atomBranchGroup.insertChild(bg, ind);
                }
            }
        }
        //
        // AtomObject [] aobjs = getAtomObjects();
        // for ( int i=0 ; i<aobjs.length ; i++ ) {
        // int aind = aobjs[i].getIDint()-1;
        // logger.debug("index of branchgroup & atom obj: "+i+", "+aind);
        // }

        int[] map = state.getIndexMap();
        /*
         * ボンドが持っている原子のインデックスの付け替え
         */
        for (int i = 0; i < bonds.size(); i++) {
            BondInfo inf = (BondInfo) bonds.get(i);
            int[] pair = inf.getIndexPair();
            logger.debug("old index pair & new index pair: " + pair[0] + ", "
                    + pair[1] + "  :  " + map[pair[0]] + ", " + map[pair[1]]);
            pair[0] = map[pair[0]];
            pair[1] = map[pair[1]];
            inf.setIndexPair(pair[0], pair[1]);
        }
        logger.debug("done atom update");
    }

    private BranchGroup createAtom(int i) {
        int atomicNumber = 1;
        if (i >= elements.length || elements[i] == null
                || elements[i].length() == 0)
            return null;
        for (int j = 0; j < mACVD.getNumEl(); j++) {
            if (elements[i].equals(mACVD.getElement()[j])) {
                atomicNumber = j;
            }
        }
        int[] mw = getMobileAndWeight(auxils, i);
        int mobile = mw[0];
        int weight = mw[1];

        String ID = (new Integer(i + 1)).toString();
        BranchGroup at = creator.getSceneGraphElement(ID, atomicNumber,
                Dpos[i], Dforce[i], mobile, weight);
        // シーングラフを, mCDの変更とともに変更する... がうまくいかない. pending.
        ((AtomObjectBG) at).getAssociatedAtomObject()
                .register(mCD, mACVD, this);
        // mCD.registerAtoms(((AtomObjectBG)at).getAssociatedAtomObject());
        return at;
    }

    /**
     * mobileやweightパラメターの処理の仕方はいずれ変更する必要があるが, とりあえずやっつけ.
     * 
     * @param TaggedStringなるダサいオブジェクト
     * @param i
     *            原子のID.
     * 
     * @return returnValue[0]がmobile, [1]がweight.
     */
    public static int[] getMobileAndWeight(TaggedString[][] auxs, int i) {
        int mobile = 0;
        int weight = 1;

        try {
            for (int au = 0; au < auxs[0].length; au++) {
                TaggedString ts = auxs[i][au];
                if (ts.getTag().trim().equalsIgnoreCase("mobile")) {
                    try {
                        mobile = Integer.parseInt(ts.getValue());
                    } catch (NumberFormatException nfe) {
                    }
                } else if (ts.getTag().trim().equalsIgnoreCase("weight")) {
                    try {
                        weight = Integer.parseInt(ts.getValue());
                    } catch (NumberFormatException nfe) {
                    }
                }
            }
        } catch (NullPointerException npe) {
            mobile = 0;
            weight = 1;
        }
        return new int[] { mobile, weight };
    }

    private String[] elements;

    private TaggedString[][] auxils;

    private void addAtoms() {
        atomBranchGroup = new BranchGroup();
        atomBranchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
        atomBranchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
        atomBranchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);

        atomTransformGroup = new TransformGroup();
        atomTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        atomTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        atomTransformGroup.addChild(atomBranchGroup);

        elements = mCD.getElements();
        auxils = mCD.getAuxils();
        mCD.setScaleAtom(mACVD.getScaleAtom());

        for (int i = 0; i < NumAt; i++) {
            BranchGroup bg = createAtom(i);
            if (bg != null)
                atomBranchGroup.addChild(bg);
        }
        // this.addChild(atomBranchGroup);
        this.addChild(atomTransformGroup);
    }

    //
    // public void redoBond() {
    // BondObject [] obj = getBondObjects();
    // if ( obj != null ) {
    // for ( int i=0 ; i<obj.length ; i++ ) {
    // obj[i].detach();
    // }
    // }
    // addBond();
    // }
    //
    private void HbondUpdate() {
        if (!(new Boolean(props.getProperty("hbond_draw_hbonds"))
                .booleanValue())) {
            if (Hbonds != null) {
                for (int i = Hbonds.size() - 1; i >= 0; i--) {
                    BondInfo binf = (BondInfo) Hbonds.get(i);
                    int[] indpair = binf.getIndexPair();
                    double crtdst = binf.getCriticalDistanceSquared();
                    double[] at1 = Dpos[indpair[0]];
                    double[] at2 = Dpos[indpair[1]];
                    BondObject oj = binf.getBond();
                    binf.getBond().detach();
                    Hbonds.removeElementAt(i);
                    oj = null;
                    binf = null;
                }
                Hbonds = null;
            }
            return;
        }

        if (Hbonds == null) {
            addHbonds();
            return;
        }

        boolean[][] ignore = new boolean[NumAt][NumAt];
        double[][] postmp = getScaledPos();
        // 既存のボンドを処理
        for (int i = Hbonds.size() - 1; i >= 0; i--) {
            BondInfo binf = (BondInfo) Hbonds.get(i);
            int[] indpair = binf.getIndexPair();
            if (indpair[0] >= postmp.length || indpair[1] >= postmp.length
                    || indpair[0] < 0 || indpair[1] < 0) {
                remBond(binf, i);
                continue;
            }
            double crtdst = binf.getCriticalDistanceSquared();
            double[] at1 = postmp[indpair[0]];
            double[] at2 = postmp[indpair[1]];
            if (crtdst < Math.pow(at1[0] - at2[0], 2)
                    + Math.pow(at1[1] - at2[1], 2)
                    + Math.pow(at1[2] - at2[2], 2)) {
                BondObject oj = binf.getBond();
                binf.getBond().detach();
                Hbonds.removeElementAt(i);
                oj = null;
                binf = null;
            } else {
                binf.getBond().recreate(
                        new double[] { at1[0], at1[1], at1[2], at2[0], at2[1],
                                at2[2] });
            }
            ignore[indpair[0]][indpair[1]] = true;
        }
        hbondCalculator.setIgnore(ignore);
        hbondCalculator.doHbond(postmp);
        Vector foo = hbondCalculator.getBonds();

        for (int i = 0; i < foo.size(); i++) {
            BondInfo binf = (BondInfo) foo.get(i);
            double[] NearestNeighbor_ = binf.getCoordinates();
            SceneGraphElementBG bond = creator.getSceneGraphElement(
                    NearestNeighbor_, BondObject.HBOND);
            binf.setBond((BondObject) bond);
            this.addChild(bond);
            Hbonds.addElement(binf);
        }

    }

    private void remBond(BondInfo binf, int i) {
        BondObject obj = binf.getBond();
        binf.getBond().detach();
        if (i < bonds.size())
            bonds.removeElementAt(i);
        obj = null;
        binf = null;
    }

    private void bondUpdate() {
        if (bonds == null)
            return;
        boolean[][] ignore = new boolean[NumAt][NumAt];
        AtomObject[] atomObjects = getAtomObjects();

        if (mCD.forceUpdate()) { // この場合, 全部やり直し
            for (int i = bonds.size() - 1; i >= 0; i--) {
                BondInfo binf = (BondInfo) bonds.get(i);
                remBond(binf, i);
            }
        }

        double[][] postmp = getScaledPos();
        // 既存のボンドを処理
        for (int i = bonds.size() - 1; i >= 0; i--) {
            BondInfo binf = (BondInfo) bonds.get(i);
            int[] indpair = binf.getIndexPair();

            double crtdst = binf.getCriticalDistanceSquared();
            if (indpair[0] >= postmp.length || indpair[1] >= postmp.length
                    || indpair[0] < 0 || indpair[1] < 0) {
                remBond(binf, i);
                continue;
            }

            double[] at1 = postmp[indpair[0]];
            double[] at2 = postmp[indpair[1]];
            if (updateMode == GENERIC) {
                int atnum1 = mCD.getAtomicNumber(indpair[0]) - 1;
                int atnum2 = mCD.getAtomicNumber(indpair[1]) - 1;
                if (atnum1 < 0 || atnum2 < 0) {
                    remBond(binf, i);
                    continue;
                }
                crtdst = Math
                        .pow(mACVD.getBondFactor()
                                * (mACVD.getCovRad()[atnum1] + mACVD
                                        .getCovRad()[atnum2]) / mCD.getLenMax(),
                                2);
            }
            if (crtdst < Math.pow(at1[0] - at2[0], 2)
                    + Math.pow(at1[1] - at2[1], 2)
                    + Math.pow(at1[2] - at2[2], 2)) {
                remBond(binf, i);
            } else {
                binf.getBond().recreate(
                        new double[] { at1[0], at1[1], at1[2], at2[0], at2[1],
                                at2[2] });
                ignore[indpair[0]][indpair[1]] = true;
            }
        }
        clcbnd.setIgnore(ignore);
        clcbnd.doIt(postmp);
        Vector foo = clcbnd.getBonds();

        for (int i = 0; i < foo.size(); i++) {
            BondInfo binf = (BondInfo) foo.get(i);
            double[] NearestNeighbor_ = binf.getCoordinates();
            SceneGraphElementBG bond = creator
                    .getSceneGraphElement(NearestNeighbor_);
            binf.setBond((BondObject) bond);
            // ((BondObject)bond).setAssociatedAtoms(atomObjects[binf.getIndexPair()[0]],atomObjects[binf.getIndexPair()[1]]);
            this.addChild(bond);
            bonds.addElement(binf);
        }

    }

    private Vector bonds;

    private void addBond() {
        clcbnd = new BondCalculator(mACVD, mCD);
        double[][] dp = mCD.getPos();
        double[][] postmp = getScaledPos();
        clcbnd.doIt(postmp);
        bonds = clcbnd.getBonds();
        for (int i = 0; i < bonds.size(); i++) {
            BondInfo binf = (BondInfo) bonds.get(i);
            double[] NearestNeighbor_ = binf.getCoordinates();
            SceneGraphElementBG bond = creator
                    .getSceneGraphElement(NearestNeighbor_);
            binf.setBond((BondObject) bond);
            this.addChild(bond);
        }
    }

    private double[][] getScaledPos() {
        double[][] dp = mCD.getPos();
        double[][] postmp = new double[dp.length][3];
        for (int i = 0; i < postmp.length; i++) {
            for (int j = 0; j < 3; j++) {
                postmp[i][j] = (dp[i][j] - mCD.getCOM()[j]) / mCD.getLenMax();
            }
        }
        return postmp;
    }

    private Vector Hbonds;

    private void addHbonds() {
        if (!(new Boolean(props.getProperty("hbond_draw_hbonds"))
                .booleanValue())) {
            return;
        }
        hbondCalculator = new BondCalculator(mACVD, mCD);
        hbondCalculator.doHbond(Dpos);
        Hbonds = hbondCalculator.getBonds();
        if (Hbonds == null) {
            return;
        }
        logger.debug("num hbonds: " + Hbonds.size());
        for (int i = 0; i < Hbonds.size(); i++) {
            BondInfo binf = (BondInfo) Hbonds.get(i);
            logger.debug("bond info; bondlength: " + binf.getBondLength()
                    * mCD.getLenMax());
            double[] NearestNeighbor_ = binf.getCoordinates();
            SceneGraphElementBG bond = creator.getSceneGraphElement(
                    NearestNeighbor_, BondObject.HBOND);
            binf.setBond((BondObject) bond);
            this.addChild(bond);
        }
    }

    private SceneGraphElementBG cellObject;

    private void addCell() {
        logger.debug("at addCell()");
        if (cellObject != null)
            cellObject.detach();
        if (mACVD.getDrawCell()) {
            cellObject = creator.getSceneGraphElementBG(SceneGraphElement.CELL);
            this.addChild(cellObject);
        }
    }

    /**
     * 「選択中の原子」の個数を調べる.
     * 
     * @return 選択中の原子の個数
     */
    public int getSelectedAtomCount() {
        int ret = 0;
        AtomObject[] aobjs = getAtomObjects();
        if (aobjs == null)
            return ret;

        for (int i = 0; i < aobjs.length; i++) {
            if (aobjs[i].isSelected())
                ret++;
        }
        return ret;
    }

    /**
     * 「選択中」な原子のインデックスの配列を取得する
     * 
     * @return インデックス配列.
     */
    public int[] getSelectedAtomsIndex() {
        int[] ret;
        Vector vret = new Vector();
        AtomObject[] aobjs = getAtomObjects();
        if (aobjs == null)
            return null;
        for (int i = 0; i < aobjs.length; i++) {
            if (aobjs[i].isSelected()) {
                vret.addElement(aobjs[i].getID());
            }
        }

        if (vret.size() == 0) {
            return null;
        }

        ret = new int[vret.size()];
        for (int i = 0; i < vret.size(); i++) {
            ret[i] = Integer.parseInt((String) vret.elementAt(i)) - 1;
        }
        return ret;
    }

    public void setRescaleOnUpdate(boolean rescaleOnUpdate) {
        this.rescaleOnUpdate = rescaleOnUpdate;
    }

    public boolean getRescaleOnUpdate() {
        return this.rescaleOnUpdate;
    }

    public void configDataUpdate(boolean rescaleOnUpdate,
            ConfigDataUpdateEvent e) {
        // if ( mp.doRedraw() ) {
        // return;
        // }
        // prepare(rescaleOnUpdate);
        // prepare(rescaleOnUpdate);
        configDataUpdate();
    }

    public void configDataUpdate() {
        // if ( mp.doRedraw() ) {
        // return;
        // }
        // prepare();
        logger.debug("start bond update");
        bondUpdate();
        HbondUpdate();
        doBond();
        logger.debug("end bond update");
        updateMeasureObjects();
        if (mCD.forceUpdate()) {
            addCell();
        }
    }

    public int getLastNumAt() {
        int last = mCD.getNumAt();
        logger.debug("stored selection; numat now: " + last);
        return last;
    }

    public boolean needsUpdate() {
        return true;
    }

    public DynamicallyEditable[] getDynamicallyEditableElements() {
        Vector retvec = new Vector();
        AtomObject[] aobjs = getAtomObjects();
        BondObject[] bobjs = getBondObjects();
        for (int i = 0; i < aobjs.length; i++) {
            retvec.addElement(aobjs[i]);
        }
        if (bobjs != null) {
            for (int i = 0; i < bobjs.length; i++) {
                retvec.addElement(bobjs[i]);
            }
        }

        // for ( int i=0 ; i<numChildren() ; i++ ) {
        // if ( getChild(i) instanceof DynamicallyEditable ) {
        // retvec.addElement(getChild(i));
        // }
        // }
        if (retvec.size() == 0) {
            return null;
        }

        DynamicallyEditable[] rets = new DynamicallyEditable[retvec.size()];
        retvec.copyInto(rets);

        return rets;
    }

    public CellObject getCellObject() {
        for (int i = 0; i < numChildren(); i++) {
            if (getChild(i) instanceof CellObject) {
                return (CellObject) getChild(i);
            }
        }
        return null;
    }

    private BondObject[] getBondObjects() {
        Vector retvec = new Vector();
        for (int i = 0; i < numChildren(); i++) {
            if (getChild(i) instanceof BondObject) {
                retvec.addElement(getChild(i));
            }
        }
        if (retvec.size() == 0) {
            return null;
        }

        BondObject[] rets = new BondObject[retvec.size()];
        retvec.copyInto(rets);
        return rets;
    }

    public AtomObjectBG[] getAtomObjectBGs() {
        Vector retvec = new Vector();
        for (int i = 0; i < atomBranchGroup.numChildren(); i++) {
            retvec.addElement(atomBranchGroup.getChild(i));
        }
        if (retvec.size() == 0) {
            return null;
        }

        AtomObjectBG[] rets = new AtomObjectBG[retvec.size()];
        retvec.copyInto(rets);
        return rets;
    }

    public AtomObject[] getAtomObjects() {
        Vector retvec = new Vector();
        for (int i = 0; i < atomBranchGroup.numChildren(); i++) {
            retvec.addElement(atomBranchGroup.getChild(i));
        }
        if (retvec.size() == 0) {
            return null;
        }

        AtomObject[] rets = new AtomObject[retvec.size()];
        for (int i = 0; i < rets.length; i++) {
            rets[i] = ((AtomObjectBG) retvec.get(i)).getAssociatedAtomObject();
        }
        return rets;
    }

    public AtomObject[] getSelectedAtoms() {
        AtomObject[] all = getAtomObjects();
        if (all == null) {
            return null;
        }
        Vector retvec = new Vector();
        for (int i = 0; i < all.length; i++) {
            if (all[i].isSelected()) {
                retvec.addElement(all[i]);
            }
        }
        if (retvec.size() == 0) {
            return null;
        }

        AtomObject[] rets = new AtomObject[retvec.size()];
        retvec.copyInto(rets);

        return rets;
    }

}
