//------------------------------------------------------------
// $Id: volume.h,v 1.2.2.1 2000/06/14 15:00:10 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 define volume class
// 	Volume Rendeing 用
// 1998年1月20日(火)
// 1998年2月16日(月) volume の内部表現は grid だったことに気がつき、書
// き直しをはじめる。Lovoy の論文 
// @Article{Levoy1988,
//   author = 	 "Marc Levoy",
//   title = 	 "Display of Surfaces from Volume Data",
//   journal =	 "IEEE Computer Graphics and Applications",
//   year =	 1988,
//   volume =	 8,
//   number =	 5,
//   pages =	 "29--37",
//   month =	 "May",
// }
// を読んでいながら (Figure 2) 体積の代表点として書いてしまったが、サ
// ンプルする時には grid として計算するというモデルの一貫性を保たない
// プログラムを書いてしまったため、レイキャスティング時に取れないバグ
// を作ってしまった。 
//
// この論文の Figure 2 に合致するように実装する。
// 
//     |   |   |   |   |   slice 数 = 5
// 
//     +---+---+---+---+   grid point 5
//     |   |   |   |   |   volume 数  4 (ここに注意する)
//     +---+---+---+---+   情報は grid で持つ
//     |<------------->|   したがって部分分割されると端はコピーされる
// volume,grid min    volume,grid max
//
// セルの範囲は [cellmin_i, cellmax_i) であることに注意すること
//
// 1998年4月12日(日) 
//	grid はサンプリングポイントであり、スライスなどは grid に属する。
//	volume はこのサンプリングポイントから構成されるものであって、
//	grid との対応というのは条件による。例を下に示す。
//
// *** volume の実体は grid である。
//     -> 内部構造の grid を意識しない場合には volume という名前でアクセス
//
// 1.  g---g
//     |   | voxel(voxel 値はこれらの値から補間する。lower left を代表
//     g---g       値にするなどいろいろ考えられる)
//     |<->|
// 	voxel length
//   g 点が grid 位置
// 
//	Copyright (C) 1997--1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#ifndef _volume_h
#define _volume_h 1

#include <stdio.h>
#include <iostream.h>
#include "GridColor.h"
#include "IntVec3.h"
#include "Vector3.h"
#include "YorN.h"
#include "array3d.h"
#include "pnmReader.h"
#include "ray.h"
#include "volumeGridInfo.h"

//------------------------------------------------------------
// 分割されたボリューム情報を含むファイルには最低 5 行のコメントがある．
// この数字で分割ボリュームかどうかを判断することにする．
//------------------------------------------------------------
const int AT_LEAST_COMMENTS_WITHSUBDIVIDED_VOL = 5;


class VolDistClass;
//------------------------------------------------------------
// volume を示す class 
// 	volume は global volume と sub volume を考えた。
// 	global volume とは分割前の volume のことで global volume の分
// 	割されたそれぞれが sub volume である。同じ global volume から
// 	分割された volume は同一の ID を持つ。このようにすることで分配
// 	された volume の統合が簡単になる。
//	
//	アクセスの方針 : global volume としてアクセスする
//------------------------------------------------------------
// このクラスの初期化は
//	1.initGlobalGridInfo   
//	2.initSubGridInfo	: grid の array 内容の alloc も行う
// 	3.grid そのものの初期化
//	4.calcEffectiveGridRange	
// で完全になる．分割前には 4 と 2 の一部(空間位置, subgrid の範囲)な
// どが不明なことなどから分離されている．が，上手く書けんものかな
//------------------------------
class VolumeClass { //  : public DividableIF, public SerializableIF{ 
protected:
    array3dClass < GridColorClass > mGrid; // grid data
    GlobalGridInfoClass    mGlobalGrid;	   // 分割前の global な volume の情報
    SubGridInfoClass       mSubGrid;	   // 分割後の部分 volume の情報
    EffectiveGridInfoClass mEffiGrid;	   // この空間で有効な Grid 範囲
    
protected:
    YorN checkCanAddPNM(PNMReader *pPNMBuf) const;
    void keepConsistentWithPNM(PNMReader *pPNMBuf);
    YorN checkCanAddVolume(VolumeClass *pAddVolume) const;

public:
    // constructors (不正な値を入れておく)
    VolumeClass(void);
    VolumeClass(const VolumeClass& rhs);
    const VolumeClass& operator=(const VolumeClass& rhs);
    ~VolumeClass(void);

    friend int operator == (const VolumeClass& a, const VolumeClass& b);
    
    // null volume としての初期化 
    void initAsNullVolume(void);
    YorN isNullVolume(void) const { return(this->mGlobalGrid.isNullVolume());}

    // initializer 
    void initSize(const int *pGridMinIdx, const int *pGridMaxIdx);
    YorN calcEffectiveGridRange(const Vector3& cellMinPos, 
  				const Vector3& cellMaxPos);
    YorN isInited(void) const;	// already initialized or not 

    //------------------------------
    // 常に global volume としてアクセスする 
    // GridColorClass の色は [0.0, 1.0] の値
    // grid          index は [gridmin index, gridmax index] の範囲
    // effectiveGrid index は [effectiveMin, effectiveMax]   の範囲
    //------------------------------
    void setGlobalGridIdxColor(int globalIdx[], const GridColorClass& aCol)
    {
 	this->mGrid.set(globalIdx, aCol);
    }
    GridColorClass getGlobalGridIdxColor(int globalIdx[]) const
    {
 	return(this->mGrid.get(globalIdx));
    }
    GridColorClass getGlobalEffiGridIdxColor(int volumeGlobalIdx[]) const;
    int getGridResSize(int dim) const 	{ return(this->mGrid.getSize(dim));}
    int getGridMinIdx(int dim)  const 	{ return(this->mGrid.getMinIdx(dim));}
    int getGridMaxIdx(int dim)  const 	{ return(this->mGrid.getMaxIdx(dim));}
    int getTotalGridNum(void)   const	{ return(this->mGrid.getTotalSize());}
    int getGlobalGridResSize(int dim) const
    {
 	return((this->mGlobalGrid.getResolSize()).get(dim));
    }

    //------------------------------
    // global grid info
    //------------------------------
    // ID (set しないと初期化されたことにならない．)
    int  getID(void) const		{ return(this->mGlobalGrid.getID());}
    void initGlobalGridInfo(const GlobalGridInfoClass &gginfo);	// init gginfo
    const GlobalGridInfoClass &getGlobalGrid(void) const 
    {
	return(this->mGlobalGrid);
    }
    double getVoxelEdgeLen(void) const
    {
 	return(this->mGlobalGrid.getVoxelEdgeLen()); 
    }
    int getGridRGBDepth(void) const { return(this->mGlobalGrid.getRGBDepth());}

    //------------------------------
    // sub grid info manipulation
    //------------------------------
    void initSubGridInfo(const SubGridInfoClass &sginfo);// subinfoによる初期化
    const SubGridInfoClass &getSubGrid(void) const 
    {
	return(this->mSubGrid);
    }
    IntVec3 getSubVolumeSpcPos(void) const
    {
 	return(this->mSubGrid.getSpcPos());	
    }

    //------------------------------
    // effective grid info manipulation
    //------------------------------
    void initEffiGridInfo(const EffectiveGridInfoClass& eginfo);
    const EffectiveGridInfoClass& getEffiGrid(void) const
    {
	return(this->mEffiGrid);
    }
    void invalidateEffiGridInfo(void);

    //------------------------------
    // subvolume manipulation
    //------------------------------
    // add subvolume to this volume 
    void addPNM(PNMReader *pPNMBuf);
    PNMReader *getNewPNM(int zpos);
    PNMReader *getNewPNM(void);
    void addSubVolume(VolumeClass *pGrid);

    // get physical position from volume index
    YorN    checkGridIdxRangeForDEBUG(int idx[]) const;
    YorN    checkGridIdxRangeForDEBUG(const IntVec3& idx) const;
    Vector3 getGlobalGridPosFromIdx(int idx[]) const; //with check
    Vector3 getGlobalGridPosFromIdxNoRangeCheck(int idx[]) const;

    // global grid info と sub grid info 間の一貫性を check する
    YorN isConsistentGlobalAndSubGridInfo(void) const;
     
    void print(void) const;	// print this volume property
    YorN isAllFill(void);	// check not initialized voxel

public:				
    //------------------------------------------------------------
    // interface DividableIF となる予定
    //------------------------------------------------------------
    VolumeClass *newSubVolume(int minGridIdx[], int maxGridIdx[], 
 			      const IntVec3& spcpos) const;

public:				
    //------------------------------------------------------------
    // casting a ray to this volume supporting methods 
    // ref. volumeRayCast.cc
    //------------------------------------------------------------
    IntVec3 getGlobalVolumeIndexFromPos(const Vector3& pos);
    YorN castRay(Ray *pRay, const VolDistClass* const pVolDist, 
		 Vector3 *pRetColor);

public:				
    //------------------------------------------------------------
    // SerializableIF : double の表現が同じ計算機でないと上手くいかない    
    // ref. volumeSerializable.cc
    //------------------------------------------------------------
    int getSerializedSize(void) const;
    double *newSerializedArray(void);
    void deserialize(double *pBuf);
};

#endif				// #ifndef _volume_h 
