#ifndef gra_Photo_H__
#define gra_Photo_H__
#include "Bitmap.h"
#include "../Self.h"
#include "../geo/Vectors.h"
#include "../math/utility.h"
#include "../math/Matrix.h"
#include "../math/Transformations.h"
#include "../oo/ObjBase.h"
#include <vector>
#include <cmath>
#include <string>
#include <typeinfo>
#include <cstdlib>
namespace meow {
/*!
* @brief 底片
*
* 基本上就是一個 \c Bitmap 加上 \c focal
*
* @author cat_leopard
*/
template<class Pixel>
class Photo: public ObjBase {
private:
struct Myself {
Bitmap<Pixel> bmp_;
Vector2D<double> c_;
PhotoProjection<double> proj_;
Myself(): proj_(3) {
}
Myself(Myself const& b): bmp_(b.bmp_), c_(b.c_), proj_(b.proj_) {
}
~Myself() {
}
};
Self<Myself> const self;
/*!
* @brief 取得bitmap座標
*/
Vector2D<double> bitmapCoord(Vector2D<double> const& yx) const {
return Vector2D<double>(yx.x() + center().x(), -yx.y() + center().y());
}
public:
/*!
* @brief constructor
*
* focal 預設為 1
*/
Photo(): self() {
self()->proj_.focal(1.0);
}
/*!
* @brief constructor
*
* 複製資料
*
* @param [in] b 資料來源
*/
Photo(Photo const& b): self(b.self, Self<Myself>::COPY_FROM) {
}
/*!
* @brief constructor
*
* 直接給定圖片, 焦距用猜的
*
* @param [in] bmp 給定的圖片
*/
Photo(Bitmap<Pixel> const& bmp): self() {
reset(bmp);
}
/*!
* @brief constructor
*
* 直接給定圖片與焦距
*
* @param [in] bmp 給定的圖片
* @param [in] f 給定的焦距
*/
Photo(Bitmap<Pixel> const& bmp, double f): self() {
reset(bmp, f);
}
/*!
* @brief constructor
*
* 直接給定圖片, 焦距與中心點位置
*
* @param [in] bmp 給定的圖片
* @param [in] f 給定的焦距
* @param [in] c 中心點作標
*/
Photo(Bitmap<Pixel> const& bmp, double f, Vector2D<double> const& c): self() {
reset(bmp, f, c);
}
/*!
* @brief destructor
*/
~Photo() {
}
/*!
* @brief 複製資料
*
* @param [in] b 資料來源
*/
Photo& copyFrom(Photo const& b) {
self().copyFrom(b.self);
return *this;
}
/*!
* @brief 參照
*
* @param [in] b 參照來源
*/
Photo& referneceFrom(Photo const& b) {
self().referenceFrom(b.self);
return *this;
}
/*!
* @brief 重設bitmap, focal 用猜的
*
* focal直接代對角線, center代bitmap中心點
*
* @param [in] bmp 新的 \c bitmap
*/
void reset(Bitmap<Pixel> const& bmp) {
bitmap(bmp);
focal(sqrt(squ(width()) + squ(height())));
center(Vector2D<double>(bmp.width() / 2, bmp.height() / 2));
}
/*!
* @brief 重設bitmap, focal
*
* center代bitmap中心點
*
* @param [in] bmp 新的 \c bitmap
* @param [in] f 新的 \c focal
*/
void reset(Bitmap<Pixel> const& bmp, double f) {
bitmap(bmp);
focal(f);
center(Vector2D<double>(bmp.width() / 2, bmp.height() / 2));
}
/*!
* @brief 重設bitmap, focal, center
*
* @param [in] bmp 新的 \c bitmap
* @param [in] f 新的 \c focal
* @param [in] c 新的中心點作標
*/
void reset(Bitmap<Pixel> const& bmp, double f, Vector2D<double> const& c) {
bitmap(bmp);
focal(f);
center(c);
}
/*!
* @brief 回傳\c bitmap
*/
Bitmap<Pixel> const& bitmap() const {
return self->bmp_;
}
/*!
* @brief 回傳\c bitmap 的參照(非constant)
*/
Bitmap<Pixel>& bitmapGet() {
return self()->bmp_;
}
/*!
* @brief 設定bitmap
*
* @param [in] bmp 新的 bitmap
* @return 新的 \c bitmap
*/
Bitmap<Pixel> const& bitmap(Bitmap<Pixel> const& bmp) {
self()->bmp_.copyFrom(bmp);
return bitmap();
}
/*!
* @brief 回傳focal length
*/
double focal() const {
return self->proj_.focal();
}
/*!
* @brief 設定 focal length
*
* @param [in] f 新的 focal length
* @return 新的 \c focal length
*/
double focal(double f) {
self()->proj_.focal(f);
return focal();
}
/*!
* @brief 回傳相應的 photo projection
*/
PhotoProjection<double> projection() const {
return self->proj_;
}
/*!
* @brief 設定 photo projection
*/
PhotoProjection<double> projection(PhotoProjection<double> const& p) {
if (p.dimension() == 3) {
self()->proj_ = p;
}
return projection();
}
/*!
* @brief 取得照片中心點底片座標
*
* @return 一個二維vector
*/
Vector2D<double> const& center() const {
return self->c_;
}
/*!
* @brief 取得照片中心點底片座標 (non-constant reference)
*
* @return 一個二維vector
*/
Vector2D<double>& centerGet() {
return self()->c_;
}
/*!
* @brief 設定照片中心點底片座標
*
* @param [in] c 新的座標
*
* @return 新的座標
*/
Vector2D<double> const& center(Vector2D<double> const& c) {
self()->c_ = c;
return center();
}
/*!
* @brief 回傳bitmap寬
*/
size_t width() const {
return self->bmp_.width();
}
/*!
* @brief 回傳bitmap高
*/
size_t height() const {
return self->bmp_.height();
}
/*!
* @brief 回傳bitmap的某pixel
*/
Pixel pixel(size_t y, size_t x) const {
return self->bmp_.pixel(y, x);
}
/*!
* @brief 設定某pixel
*/
Pixel pixel(size_t y, size_t x, Pixel const& p) {
self()->bmp_.pixel(y, x, p);
return pixel(y, x);
}
/*!
* @brief 檢查某點是否在底片範圍內
*
* @param [in] yx 底片座標
*
* @return \c true/false
*/
bool inside(Vector2D<double> const& yx) const {
Vector2D<double> c = bitmapCoord(yx);
ssize_t h_max = (ssize_t)height() - 1;
ssize_t w_max = (ssize_t)width () - 1;
return (0 <= c.y() && c.y() <= h_max && 0 <= c.x() && c.x() <= w_max);
}
/*!
* @brief 檢查某點是否在底片範圍內
*
* @param [in] p 大地座標
*
* @return \c true/false
*/
bool inside(Vector3D<double> const& p) const {
if (p.z() > 0) return false;
return inside(Vector2D<double>(self->proj_.transformate(p.matrix())));
}
/*!
* @brief 取得給照片座標中某點的色彩
*
* 用浮點數vector傳入, 所以色彩是經過渲染過的
*
* @param [in] yx 底片座標(原點為center)
*
* @return pixel
*/
Pixel color(Vector2D<double> const& yx) const {
if (!inside(yx)) return Pixel(0);
Vector2D<double> c(bitmapCoord(yx));
int y0 = (int)c.y();
int x0 = (int)c.x();
double h[2] = {1 - (c.y() - y0), c.y() - y0};
double w[2] = {1 - (c.x() - x0), c.x() - x0};
Pixel sum(0);
for (int dy = 0; dy < 2; dy++)
for (int dx = 0; dx < 2; dx++) {
sum = sum + bitmap().pixel(
std::min(y0 + dy, (int)height() - 1),
std::min(x0 + dx, (int)width () - 1)) * (w[dy] * h[dx]);
}
return sum;
}
/*!
* @brief 取得給照片座標中某點的色彩
*
* 這次是輸入大地座標
*
* @param [in] p 大地座標p
* @return pixel
*/
Pixel color(Vector3D<double> const& p) const {
return color(Vector2D<double>(self->proj_.transformate(p.matrix())));
}
/*!
* @brief same as \c .copyFrom(b)
*/
Photo& operator=(Photo const& b) {
return copyFrom(b);
}
/*! @brief 將資料寫入檔案
*
* @note 未完成
*/
bool write(FILE* f, bool bin, unsigned int fg) const {
if (bitmap().write(f, bin, fg) == false) return false;
if (bin) {
double tmp;
if (fwrite(&(tmp = center().x()), sizeof(tmp), 1, f) < 1) return false;
if (fwrite(&(tmp = center().y()), sizeof(tmp), 1, f) < 1) return false;
if (fwrite(&(tmp = focal()), sizeof(tmp), 1, f) < 1) return false;
}
else {
if (fprintf(f, "%f %f\n", center().x(), center().y()) < 2) return false;
if (fprintf(f, "%f\n", focal()) < 1) return false;
}
return true;
}
/*! @brief 將資料讀入
*
* @note 未完成
*/
bool read(FILE* f, bool bin, unsigned int fg) {
if (bitmapGet().read(f, bin, fg) == false) return false;
double tmp[3];
if (bin) {
if (fread(tmp, sizeof(double), 3, f) < 3) return false;
}
else {
if (fscanf(f, "%lf %lf %lf", tmp + 0, tmp + 1, tmp + 2) < 3) return false;
}
centerGet().x(tmp[0]);
centerGet().y(tmp[1]);
focal(tmp[2]);
return true;
}
/*! @brief new一個自己
*
* @return 一個new出來的Photo<Pixel>
*/
ObjBase* create() const {
return new Photo();
}
/*! @brief 複製資料
*
* 輸入型別是 \c ObjBase \c const*
* 這裡假設實體其實是 \c Bitmap.
* 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom
*
* @param [in] b 資料來源
* @return this
*/
ObjBase* copyFrom(ObjBase const* b) {
return &(copyFrom(*(Photo*)b));
}
/*! @brief 回傳class的type
*
* @return \c char \c const\c * 形式的typename
*/
char const* ctype() const{
return typeid(*this).name();
}
/*! @brief 回傳class的type
*
* @return \c std::string 形式的typename
*/
std::string type() const {
return std::string(ctype());
}
};
} // meow
#endif // gra_Photo_H__