diff options
author | cathook <b01902109@csie.ntu.edu.tw> | 2014-06-01 13:56:57 +0800 |
---|---|---|
committer | cathook <b01902109@csie.ntu.edu.tw> | 2014-06-01 13:56:57 +0800 |
commit | d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0 (patch) | |
tree | 16f7920c5079e0aefcf9509d2dbab59c464d42bd /meowpp | |
parent | bd58f63900410ec4764031f2e6de2d75e91434b3 (diff) | |
download | meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar.gz meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar.bz2 meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar.lz meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar.xz meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.tar.zst meow-d5052f1c296dddf51b3e83d59bf3e3c1952cb2d0.zip |
big chnage
Diffstat (limited to 'meowpp')
48 files changed, 10776 insertions, 1382 deletions
diff --git a/meowpp/!readme.asciidoc b/meowpp/!readme.asciidoc new file mode 100644 index 0000000..0d9f0d8 --- /dev/null +++ b/meowpp/!readme.asciidoc @@ -0,0 +1,20 @@ + +meow for *C++ templates* + + +===== Self.h + +包含一個具有 *Copy On Write* 技術的 'class' 而且有實作 *by reference* , +基本上就是改良C\+\+原本的 reference 機制, 原本的 reference 只能在宣告的時候 +指定參照指向的變數, +而這邊則可以動態改變 + + +===== Usage.h + +方便user製作還算精美的 *usage document* 並且利用 `getopt()` 實作讀入參數與分析 + +===== utility.h + +一些不知道要歸類到哪的小functions + diff --git a/meowpp/Self.h b/meowpp/Self.h new file mode 100644 index 0000000..5a62984 --- /dev/null +++ b/meowpp/Self.h @@ -0,0 +1,230 @@ +#ifndef Self_h__ +#define Self_h__ + +#include <cstdlib> + +namespace meow { + +/*! + *@brief 具有copy on write, 且擁有比C++更靈活的reference機制 + * + *使用上就是把所有成員變數包到一個class/structure裡, 送給Self \n + *例如以下 + *@code{.cpp} + * class A { + * private: + * struct Myself { + * int data; + * Myself(){ + * data = 0; + * } + * ~Myself() { + * } + * Myself copyFrom(Myself const& ms) const { + * data = ms.data; + * } + * }; + * Self<Myself> const self; + * public: + * A(): self(true) { // self(true) 表示要建立實體, 即struct Myself + * } + * A(A const& a): self(false) { // for std::swap + * copyFrom(a); + * } + * // A(A const& a); // disable 模糊用法, 與上者二選一 + * A(A const& a, bool reference): self(false) { + * if (reference) { + * referenceFrom(a); + * } else { + * copyFrom(a); + * } + * } + * ~A() { + * } + * void setMemeber(int k) { + * self()->data = k; // self()->?? 可以有write權限 + * } + * int getMemember(int wh) const { + * return self->data; // self->?? 只有const + * } + * A referenceFrom(A const& a) { + * self.referenceFrom(a.self); + * } + * A copyFrom(A const& a) { + * self.copyFrom(a.self); + * } + * A& operator=(A const& b) { // for std::swap + * copyFrom(b); + * } + * A& operator=(A const& b); // 避免諢亂用法 + * }; + * @endcode + * + *@author cathook + * + *@warning \c Self 這個class會把\c operator= 給disable掉, 所以使用它當 + * kernel的class預設的 \c operator= 都會無法使用 + */ +template<class Data> +class Self { +private: + class Body { + private: + struct Kernel { + Data data_; + int counter_; + Kernel() { + counter_ = 1; + } + Kernel(Data const& data) { + counter_ = 1; + data_.copyFrom(data); + } + }; + Kernel *pointer_; + int counter_; + public: + Body() { + counter_ = 1; + pointer_ = new Kernel; + } + Body(Body const& b) { + counter_ = 1; + pointer_ = b.pointer_; + pointer_->counter_++; + } + ~Body() { + pointer_->counter_--; + if (pointer_->counter_ <= 0) { + delete pointer_; + } + } + int attatch() { return ++counter_; } + int detatch() { return --counter_; } + Data const* access() const { return &(pointer_->data_); } + Data * modify() { + if (pointer_->counter_ > 1) { + pointer_->counter_--; + pointer_ = new Kernel(pointer_->data_); + } + return &(pointer_->data_); + } + }; + Body* body_; + + void clear(Body* body) { + if (body != NULL) { + if (body->detatch() <= 0) { + delete body; + } + } + } +public: + /*! + *@brief constructor + * + *@param [in] create_body 是否要new一個實體資料 (如果constructor完, 馬上就要 + * \c copyFrom() , 或是 \c referenceFrom() 的話 + * 不太需要 new一個實體, 否則基本上都要 + */ + Self(bool create_body) { + body_ = (create_body ? new Body() : NULL); + } + + //! @brief 不允許copy constructor + Self(Self const& b); + + //! @brief 解構子 + ~Self() { + clear(body_); + } + + //! @brief 回傳指向 Data const 的指標 + Data const* operator->() const { + return body_->access(); + } + + //! @brief 回傳指向 Data 的指標, 如有需要, 這邊會做資料的duplicate + Data* operator->() { + return body_->modify(); + } + + //! @brief 回傳非const型態的自己 + Self& operator()() const { + return *((Self*)this); + } + + /*! + *@brief 將給定的 \c Self 的資料複製到自己這裡 + * + *@param [in] s 給定的\c Self + *@return 無 + * + *@note 與reference的差別是, copy之後若該給定的 \c Self 有資料修改, + * this 這邊 \b 不會 被改到 + */ + void copyFrom(Self const& s) { + Body* old = body_; + body_ = new Body(*(s.body_)); + clear(old); + } + + /*! + *@brief 將自己reference 到給定的 \c Self + * + *@param [in] s 給定的\c Self + *@return 無 + * + *@note 把reference想像成指標會比較容易思考, 譬如 \c a.referenceFrom(b) + * \c b.referenceFrom(c) 相當於 \b a指向b原本指的記憶體位置, + * \b b指向c原本指的記憶體位置 , 之後更動c時, 只有b會被牽連 + * + */ + void referenceFrom(Self const& s) { + if (body_ != s.body_) { + clear(body_); + body_ = s.body_; + body_->attatch(); + } + } + + /*! + * @brief 比對兩個 \c Self 是否指向同一個reference + * + * @param [in] s 另一個 \c Self + * @return \c true/false 表示是否為同一個reference + */ + bool same(Self const& s) const { + return (body_ == s.body_); + } + + /*! + * @brief 比對兩個 \c Self 的內容是否一樣 + * + * @param [in] s 另一個 \c Self + * @return \c true/false 表示兩個內容是否一樣 + * + * @note 需要用到 Data的equal() + */ + bool equal(Self const& s) const { + if (same(s) || body_->access() == s.body_->access()) return true; + return (body_->access()->equal(*(s.body_->access()))); + } + + /*! + * @brief 以reference作為判斷依據的小於判斷 + * + * @param [in] s 另一個 \c Self + * @return \c true/false 表示自己是否小於另一個 \c Self + */ + bool referenceLess(Self const& s) const { + return (body_ < s.body_); + } + + //! @brief 將 \c operator= 給disable掉 + void operator=(Self const& a); +}; + +} // meow + +#endif // Self_h__ diff --git a/meowpp/Usage.h b/meowpp/Usage.h index 801b11c..4537202 100644 --- a/meowpp/Usage.h +++ b/meowpp/Usage.h @@ -1,160 +1,461 @@ -#ifndef Usage_H__ -#define Usage_H__ +#ifndef MEOW_USAGE_H__ +#define MEOW_USAGE_H__ + +#include "utility.h" #include <cstdlib> #include <string> #include <vector> #include <map> +#include <algorithm> -namespace meow{ - class Usage{ - private: - typedef std::string String; - typedef std::vector<String> Strings; - class Value{ - public: - Value(); - Value(String const& v); - Value(String const& v, String const& d); - String getUsage() const; - String getValue() const; - bool operator==(Value const& b) const; - private: - String value; - String description; - }; - typedef std::vector<Value> Values; - class Option{ - public: - Option(); - Option(String const& des); - Option(String const& des, - String const& typ, - String const& def, - bool must); - bool setValue(String const& str); - String getValue(size_t index) const; - size_t getValuesCount() const; - bool addValueAccept(String const& val, - String const& des); - bool hasSetup() const; - bool hasValue() const; - bool chkSetup() const; - String getUsage(unsigned char opt, bool detail) const; - private: - Strings values; - Values values_accept; - String value_default; - String value_type; - String description; - bool has_value; - bool has_setup; - bool must_setup; - }; - typedef std::map<unsigned char, Option> Options; - typedef Options::const_iterator OptionsIterator; - public: - Usage(); - Usage(String const& _name); - ////////// **# Add other options #** /////////// - bool import(Usage const& usage); - bool update(Usage const& usage); - /////////// **# add a option/value #** ///////// - bool addOption(unsigned char opt, String const& des); - bool addOption(unsigned char opt, String const& des, - String const& val_type, - String const& val_default, - bool must); - bool addOptionValueAccept(unsigned char opt, - String const& val, - String const& des); - ///////// **# access informations #** ////////// - bool hasOptionSetup(unsigned char opt ) const; - size_t getOptionValuesCount(unsigned char opt ) const; - String getOptionValue(unsigned char opt, size_t index) const; - size_t getProcArgsCount() const; - String getProcArg(size_t index) const; - Strings getProcArgs() const; - //////// **# add a usage description #** /////// - void addUsageBegin(String const& des); - void addUsageEnd (String const& des); - ///////////// **# access usages #** //////////// - String getUsage() const; - ////////// **# analysis argc,argv #** ////////// - bool setArguments(int argc, char** argv, String* errmsg); - private: - String name; - Options options; - Strings usage_begin; - Strings usage_end ; - Strings proc_arguments; - }; - //# - //# === meow:: *Usage* (C++ Class) - //# ==== Description - //# `Usage` 是用來分析argc, argv和輸出usage document的class. - //# argc, argv的部份, 有以下規則 - //# - //# * `-c` 其中 `c` 可以代換成正常的一個字元的字符, - //# 這種選像要嘛就是 *有設置* , 不然就是 *沒設置* - //# * `-c <value>` 附加一個value, 這種選項可以是選擇性 ,即要設定與否都可以, - //# 反之則一定要設定. 另外可以給定value的預設值以及哪些value是可接受的 - //# * `<value>` 其他, 一律視為process arguments - //# - //# ==== Methods - //# * `Usage(String const& _name)` + - //# 建構子, 所有說明文字中 *<name>* 都會被代換成 `_name` - //# * `Usage()` + - //# 建構子, `_name` 自動取為 " *nobody* " - //# * `import(Usage const& usage)` + - //# 將另一個usage的設定匯入, 回傳成功與否 *(bool)* - //# * `update(Usage const& usage)` + - //# 將另一個usage分析argc,argv出來的資料拿來用, 回傳成功與否 *(bool)* - //# * `addOption(unsigned char option, String const& description)` + - //# 新增一個不接額外選項的參數, 並附上說明文字, 回傳成功與否 *(bool)* - //# * `addOption(unsigned char option, String const& description, - //# String const& value_type, String const& value_default, bool must)` + - //# 新增一個有額外選項的參數, 並附上說明文字, 額外選項的型別跟預設值. - //# 說明文字中所有的" *<types>* "將會被取代指定的型別, 其中 `must` 代表 - //# " *是否一定要設定此參數* " , 回傳表成功與否 *(bool)* - //# * `addOptionValueAccept(unsigned char option, - //# String const& value, String const& description)` + - //# 針對某個option, 新增一個可接受的額外選項 (如果某個option從頭到尾都 - //# 沒有新增可接受的選項, 則視為不限制), 回傳成功與否 *(bool)* - //# * `hasOptionSetup(unsigned char option)` + - //# 回傳是否有此選項 *(bool)* - //# * `getOptionValuesCount(unsigned char option)` + - //# 回傳此參數被設置了幾次 *(size_t)* , 只對有接額外參數的有效 - //# * `getOptionValue(unsigned char option, size_t index)` + - //# 回傳第`index`個額外選項 *(String)* - //# * `getProcArgsCount()` + - //# 回傳有多少個Process Arguments *(size_t)* - //# * `getProcArg(size_t index)` + - //# 取得第`index`個Process Argument *(String)* - //# * `getProcArgs()` + - //# 回傳一個陣列, 包含所有Process Arguments *(Strings)* - //# * `addUsageBegin(String const& des)` + - //# 新增一段usage document於每個選項逐條說明之前 - //# * `addUsageEnd (String const& des)` + - //# 新增一段usage document於每個選項逐條說明之後 - //# * `String getUsage()` + - //# 回傳usage document *(String)* - //# * `setArguments(int argc, char** argv, String* errmsg)` + - //# 輸入argv, argc, 回傳是否沒有錯誤發生 *(bool)* , 其中如果有錯誤發生, - //# 且 `errmsg != NULL` 則會將錯誤訊息寫入之 - //# - //#[NOTE] - //#================================== - //# * `String` 是 `std::string` . - //# * `Strings` 是 `std::vector< std::string> >`. - //# * 如果沒有寫回傳什麼, 就是回傳 `void` - //#================================== - //# - //#''' - //# +extern "C" { +#include <unistd.h> } -#include "Usage.hpp" +namespace meow { +/*! + * @brief 管理參數設置, 自訂usage document, 分析argc, argv + * + * \b Usage 是用來分析argc, argv和輸出usage document的class. \n + * argc, argv的部份, 有以下規則 + * - \b -c 其中 \a c 可以代換成一個字符, 這種選像可能是 \b 有設置 或 \b 沒設置 + * - \b -c \a value 附加一個 \a value , 這種選項可以是 \b 選擇性 或 + * \b 必要的 , 另外可以給定value的預設值以及哪些value是可接受 + * - \a value 其他, 一律視為 \b process \b arguments + * + * @author cathook + */ +class Usage { +private: + typedef std::string String; + typedef std::vector<String> Strings; + //! 存 (value, description) + class Value { + private: + String value_; + String description_; + public: + Value() { + } + Value(String const& value, String const& description) { + value_ = value; + description_ = stringReplace(description, "<value>", value); + } + String usage() const { + return stringPrintf("%8s%s : %s\n", + " ", value_.c_str(), description_.c_str()); + } + String value() const { + return value_; + } + bool operator==(Value const& b) const { + return (value_ == b.value_); + } + }; + typedef std::vector<Value> Values; + //! 存 option, 其中可能有value可能沒有 + class Option { + private: + Strings values_; + Values values_accept_; + String value_default_; + String value_type_; + String description_; + bool has_value_; + bool has_setup_; + bool must_setup_; + public: + Option() { + } + Option(String const& description) { + has_setup_ = false; + has_value_ = false; + description_ = description; + must_setup_ = false; + } + Option(String const& description, + String const& type, + String const& default_value, + bool must) { + has_setup_ = false; + has_value_ = true; + description_ = description; + value_type_ = type; + value_default_ = default_value; + must_setup_ = must; + } + Strings const& values() const { + return values_; + } + String value(size_t index) const { + if (!has_value_) return ""; + if (!has_setup_ || index >= values_.size()) return value_default_; + return values_[index]; + } + ssize_t valueAdd(String const& value) { + if (!has_value_) { + has_setup_ = true; + return 0; + } + if (values_accept_.size() > 0 && + std::find(values_accept_.begin(), values_accept_.end(), + Value(value, "")) == values_accept_.end()) + return -1; + values_.push_back(value); + has_setup_ = true; + return values_.size() - 1; + } + bool valueAcceptAdd(String const& value, String const& description) { + if (!has_value_) return false; + if (std::find(values_accept_.begin(), values_accept_.end(), + Value(value, "")) == values_accept_.end()){ + values_accept_.push_back(Value(value, description)); + } + return true; + } + bool valueAcceptChk(String const& value){ + if (!has_value_) return false; + if (values_accept_.size() == 0) return true; + return (std::find(values_accept_.begin(), values_accept_.end(), + Value(value, "")) != values_accept_.end()); + } + bool hasSetup() const{ return has_setup_; } + bool hasValue() const{ return has_value_; } + bool chkSetup() const{ return !(must_setup_ && !has_setup_); } + + String usage(unsigned char opt, bool detail) const { + String ret(stringPrintf("-%c ", opt)); + if (!detail) { + if (has_value_) ret += value_type_; + if (!must_setup_) ret = "[" + ret + "]"; + } else { + if (has_value_) { + ret += value_type_ + " "; + String default_string(""); + if (value_default_ != "") + default_string = "defalut='" + value_default_ + "'"; + String optional_string(""); + if (!must_setup_) + optional_string = "optional"; + String tmp; + if (default_string.size() + optional_string.size() > 0) { + if (default_string.size() > 0 && optional_string.size() > 0) { + ret += "(" + optional_string + ", " + default_string + ")"; + } else { + ret += "(" + optional_string + default_string + ")"; + } + } + } + ret += "\n"; + String accept_string; + for (size_t i = 0; i < values_accept_.size(); i++) { + if (i > 0) + accept_string += (i + 1 < values_accept_.size() + ? ", " : " or "); + accept_string += "'" + values_accept_[i].value() + "'"; + } + if (accept_string.size() == 0) accept_string = "... (anything)"; + ret += " " + stringReplace(stringReplace(description_, + "<type>", + value_type_), + "<values>", + accept_string) + "\n"; + for (size_t i = 0; i < values_accept_.size(); i++) { + ret += values_accept_[i].usage(); + } + ret += "\n"; + } + return ret; + } + }; + typedef std::map<unsigned char, Option> Options; + typedef Options::const_iterator OptionsIterator; + String name_; + Options options_; + Strings usage_begin_; + Strings usage_end_; + Strings proc_arguments_; +public: + /*! + * @brief constructor + * + * 所有說明文字中 \a \<name\> 都會被代換成空字串 + */ + Usage() { + } + + /*! + * @brief constructor + * + * 所有說明文字中 \a "<name>" 都會被代換成空字串 \b name + */ + Usage(String const& name) { + name_ = name; + } + + + /*! + * @brief constructor + * + * 將另一個usage原封不動的複製過來 + */ + Usage(Usage const& usage) { + name_ = usage.name_; + options_ = usage.options_; + usage_begin_ = usage.usage_begin_; + usage_end_ = usage.usage_end_; + proc_arguments_ = usage.proc_arguments_; + } + + /*! + * @brief 將另一個usage的設置匯入 + * + * @param [in] usage 另一個usage + * @return \c true/false 表示 \b 是否成功 + */ + bool import(Usage const& usage) { + for (OptionsIterator + it = usage.options_.begin(); it != usage.options_.end(); ++it) { + if (options_.find(it->first) != options_.end()) + return false; + } + for (OptionsIterator + it = usage.options_.begin(); it != usage.options_.end(); ++it) { + options_[it->first] = it->second; + } + for (size_t i = 0; i < usage.usage_begin_.size(); ++i) + usage_begin_.push_back(usage.usage_begin_[i]); + for (size_t i = 0; i < usage.usage_end_.size(); ++i) + usage_end_.push_back(usage.usage_end_[i]); + return true; + } + + /*! + * @brief 將另一個usage的選項設置加進來 + * + * @param [in] usage 另一個usage + * @return \c true/false 表 \b 是否成功 + */ + bool update(Usage const& usage) { + for (OptionsIterator + it = usage.options_.begin(); it != usage.options_.end(); ++it) { + if (options_.find(it->first) == options_.end()) continue; + for(size_t i = 0, I = it->second.values().size(); i < I; i++){ + options_[it->first].valueAdd(it->second.value(i)); + } + } + return true; + } + + /*! + * @brief 新增一個沒有額外選項的選項 + * + * @param [in] opt 指定字符 + * @param [in] des 即description, 用來解釋這個選項的意義用的 + * @return \c true/false 表 \b 是否成功 + */ + bool optionAdd(unsigned char opt, String const& des) { + if (options_.find(opt) != options_.end()) return false; + options_[opt] = Option(des); + return true; + } + + /*! + * @brief 新增一個有額外選項的選項 + * + * @param [in] opt 指定字符 + * @param [in] des 即description, 用來解釋這個選項的意義用的 + * @param [in] val_type 表示額外選項的型態, 寫在USAGE裡面給人看用的 + * @param [in] val_default 預設值, 若為空字串則當作沒有預設值 + * @param [in] must 表示是否一定要設定 + * @return \c true/false 表 \b 是否成功 + */ + bool optionAdd(unsigned char opt, String const& des, + String const& val_type, + String const& val_default, + bool must) { + if (options_.find(opt) != options_.end()) return false; + options_[opt] = Option(des, val_type, val_default, must); + return true; + } + + /*! + * @brief 針對-(opt)新增一個可接受的額外選項 + * + * @param [in] opt 指定字符 + * @param [in] val 額外選項 + * @param [in] des 關於此額外選項的說明 + * @return \c true/false 表 \b 是否成功 + */ + bool optionValueAcceptAdd(unsigned char opt, + String const& val, + String const& des) { + if (options_.find(opt) == options_.end()) return false; + return options_[opt].valueAcceptAdd(val, des); + } + + /*! + * @brief 回傳是否有設定此選項 + * + * @param [in] opt 指定字符 + * @return \c true/false 表 \b 是否有設定此選項 + */ + bool hasOptionSetup(unsigned char opt) const { + return (options_.find(opt) != options_.end() && + options_.find(opt)->second.hasSetup()); + } + + /*! + * @brief 回傳參數 \b -(opt) 被設置幾次 + * + * @param [in] opt 指定字符 + * @return 回傳次數 + */ + size_t optionValuesSize(unsigned char opt) const { + if(options_.find(opt) == options_.end()) return 0; + return options_.find(opt)->second.values().size(); + } + + /*! + * @brief 回傳參數 \b -(opt) 的第 \b index 個額外選項 + * + * @param [in] opt 指定字符 + * @param [in] index 第幾個 + * @return 回傳參數 \b -(opt) 的第 \b index 個額外選項 + */ + String optionValue(unsigned char opt, size_t index) const { + if (options_.find(opt) == options_.end()) { + return String(); + } + return options_.find(opt)->second.value(index); + } + + /*! + * @brief 取得有幾個process arguments + * + * @return 有幾個process arguments + */ + size_t procArgsSize() const { + return proc_arguments_.size(); + } + + /*! + * @brief 取得第i個process argument + * + * @param [in] index 第幾個 + * @return 回傳第 \a index 個 \b process \b argument + */ + String procArg(size_t index) const { + if (index >= proc_arguments_.size()) { + return String(); + } + return proc_arguments_[index]; + } + + /*! + * @brief 取得process arguments array + * + * @return 一個 \c std::vector , 包含所有 \b Process \b arguments + */ + Strings const& procArgs() const{ + return proc_arguments_; + } + + /*! + * @brief 新增一段usage document於每個選項逐條說明之前 + * + * @param [in] des 要新增的usage document + */ + void usageBeginAdd(String const& des) { + usage_begin_.push_back(stringReplace(des, "<name>", name_)); + } + + /*! + * @brief 新增一段usage document於每個選項逐條說明之後 + * + * @param [in] des 要新增的usage document + */ + void usageEndAdd(String const& des) { + usage_end_.push_back(stringReplace(des, "<name>", name_)); + } + + /*! + * @brief 回傳usage string + * + * @return \b usage \b string + */ + String usage() const{ + Usage::String out = stringPrintf("USAGE\n %s", name_.c_str()); + for (OptionsIterator + it = options_.begin(); it != options_.end(); ++it) + out += " " + it->second.usage(it->first, false); + out += "\n\nDESCRIPTION\n"; + for (size_t i = 0; i < usage_begin_.size(); ++i) { + out += " " + usage_begin_[i] + "\n\n"; + } + for (OptionsIterator + it = options_.begin(); it != options_.end(); ++it) { + out += it->second.usage(it->first, true); + } + for (size_t i = 0; i < usage_end_.size(); ++i) { + out += " " + usage_end_[i] + "\n\n"; + } + return out; + } + + /*! + * @brief 給定argc, argv, 將各參數設置 + * @param [in] argc,argv + * @param [out] errmsg 將錯誤訊息寫到這裡 + * (若給定NULL pointer, 則會把錯誤訊息忽略) + * @return \c true/false \b 成功與否 (否的話代表有錯誤的設定值在其中) + */ + bool arguments(int argc, char** argv, String* errmsg){ + opterr = 0; + String s; + OptionsIterator it; + String zzz; + String& err = (errmsg == NULL ? zzz : *errmsg); + for (it = options_.begin(); it != options_.end(); ++it) { + s += (char)(it->first); + if (it->second.hasValue()) s += ":"; + } + bool succ = true; + for (int opt; (opt = getopt(argc, argv, s.c_str())) != -1; ) { + if (options_.find(opt) == options_.end()) { + if(options_.find(optopt) == options_.end()){ + err += stringPrintf("Unknown option '-%c'\n", optopt); + }else{ + err += stringPrintf("No specify argument to '-%c'\n", + optopt); + } + succ = false; + continue; + } + if (options_[opt].valueAdd(optarg == NULL ? "" : optarg) < 0) { + err += stringPrintf("Option argument '%s' to '-%c' is not allowed\n" + , optarg, opt); + succ = false; + continue; + } + } + for (it = options_.begin(); it != options_.end(); it++) { + if (it->second.chkSetup() == false) { + err += stringPrintf("No specify argument to '-%c'\n", + it->first); + succ = false; + continue; + } + } + for (int i = optind; i < argc; i++) { + proc_arguments_.push_back(String(argv[i])); + } + return succ; + } +}; + +} // meow -#endif // Usage_H__ +#endif // MEOW_USAGE_H__ diff --git a/meowpp/colors/!readme.asciidoc b/meowpp/colors/!readme.asciidoc new file mode 100644 index 0000000..28b097a --- /dev/null +++ b/meowpp/colors/!readme.asciidoc @@ -0,0 +1,78 @@ + + +一些 *color space* 以及這些space的 *transformate function* 都放在這資料夾下 + +[NOTE] +目前transformation function的準確率還很低, 有待以後加強 + +===== Color3_Space.h + +`class Color3_Space<T>` *Channel Number = 3* 的 Color Space 的共通 *Base class* + +===== RGB_Space.h + +Channel分別是 + +* Red +* Green +* Blue + +.Classes +* `meow::RGBi_Space` 用 'int' 存資料, 每個channel數值合法範圍是 *0~255* +* `meow::RGBf_Space` 用 'double' 存資料, 每個channel數值合法範圍是 *0.0~1.0* + +.Functions +* `meow::colorTransformation(in, *out)` for +** RGBi_Space <--> RGBf_Space + +===== YUV_Space.h + +Channel分別是 + +* Y 明度 +* U 色度 +* V 濃度 + +.Classes +* `meow::YUVf_Space` 用 'double' 存資料, 每個channel數值合法範圍是 *0~1.0* + +.Functions +* `meow::colorTransformation(in, *out)` for +** YUVf_Space <--> RGBi_Space +** YUVf_Space <--> RGBf_Space + +===== HSL_Space.h + +Channel分別是 + +* H 色調 +* S 飽和度 +* L 亮度 + +.Classes +* `meow::HSLf_Space` 用 'double' 存資料, 每個channel數值合法範圍是 *0~1.0* + +.Functions +* `meow::colorTransformation(in, *out)` for +** HSLf_Space <--> RGBi_Space +** HSLf_Space <--> RGBf_Space +** HSLf_Space <--> YUVf_Space + +===== HSV_Space.h + +Channel分別是 + +* H 色調 +* S 飽和度 +* V 亮度 + +.Classes +* `meow::HSVf_Space` 用 'double' 存資料, 每個channel數值合法範圍是 *0~1.0* + +.Functions +* `meow::colorTransformation(in, *out)` for +** HSVf_Space <--> RGBi_Space +** HSVf_Space <--> RGBf_Space +** HSVf_Space <--> YUVf_Space +** HSVf_Space <--> HSLf_Space + diff --git a/meowpp/colors/Color3_Space.h b/meowpp/colors/Color3_Space.h new file mode 100644 index 0000000..87e215b --- /dev/null +++ b/meowpp/colors/Color3_Space.h @@ -0,0 +1,78 @@ +#ifndef colors_Color3_Space_H__ +#define colors_Color3_Space_H__ + +#include <cstdlib> + +#include "../Self.h" +#include "../geo/Vectors.h" +#include "../math/Matrix.h" +#include "../math/utility.h" + +namespace meow { + +/*! + * @brief 以三個channel所組成的色彩空間 + * + * @author cat_leopard + */ +template<class T> +class Color3_Space { +protected: + Vector3D<T> min_; + Vector3D<T> max_; + Vector3D<T> val_; + Color3_Space(Vector3D<T> const& min_bound, + Vector3D<T> const& max_bound, + Vector3D<T> const& init_value) { + min_ = min_bound; + max_ = max_bound; + val_ = init_value; + } + Color3_Space(Color3_Space const& b) { + min_ = b.min_; + max_ = b.max_; + copyFrom(b); + } +public: + virtual ~Color3_Space() { } + Color3_Space<T>& copyFrom(Color3_Space<T> const& b) { + val_ = b.val_; + return *this; + } + template<class T2> + Color3_Space<T>& copyFrom(Color3_Space<T2> const& b) { + for (size_t i = 0; i < 3; i++) { + val(i, ratioMapping((double)b.min(i), (double)b.max(i), (double)b.rgb(i), + (double)min(i), (double)max(i))); + } + } + Vector3D<T> const& minV() const { return min_; } + Vector3D<T> const& maxV() const { return max_; } + Vector3D<T> const& valV() const { return val_; } + Vector3D<T> const& valV(Vector3D<T> const& vv) { val_ = vv; return val(); } + Vector3D<T> & valVGet() { return val_; } + T const& min(size_t id) const { return minV()(id); } + T const& max(size_t id) const { return maxV()(id); } + T const& val(size_t id) const { return valV()(id); } + T const& val(size_t i, T const& c) { + if (i == 0) val_.x(c); + else if (i == 1) val_.y(c); + else if (i == 2) val_.z(c); + return val(i); + } + T& valGet(size_t id) { + if (id == 0) return valVGet().xGet(); + else if (id == 1) return valVGet().yGet(); + else return valVGet().zGet(); + } + Matrix<T> matrix() const { + Matrix<T> ret(3, 1); + for (size_t i = 0; i < 3; i++) { + ret(i, 0, val(i)); + } + } +}; + +} // meow + +#endif // colors_Color3_Space_H__ diff --git a/meowpp/colors/HSL_Space.h b/meowpp/colors/HSL_Space.h new file mode 100644 index 0000000..f70eab1 --- /dev/null +++ b/meowpp/colors/HSL_Space.h @@ -0,0 +1,188 @@ +#ifndef colors_HSL_Space_H__ +#define colors_HSL_Space_H__ + +#include "Color3_Space.h" +#include "../geo/Vectors.h" + +#include "RGB_Space.h" +#include "YUV_Space.h" +#include "../math/utility.h" + +#include <cstdlib> + +namespace meow { + +/*! + * @brief 以浮點數\b Y(亮度), \b U(色度), \b V(濃度) 三個值所組成的色彩空間 + * + * 其中範圍都介於0.0~1.0之間 + * + * @author cat_leopard + */ +class HSLf_Space: public Color3_Space<double> { +public: + HSLf_Space(): Color3_Space<double>(Vector3D<double>( 0.0, 0.0, 0.0), + Vector3D<double>(PI*2.0, 1.0, 1.0), + Vector3D<double>( 0.0, 0.0, 0.0)) { + } + HSLf_Space(double c): Color3_Space<double>(Vector3D<double>( 0.0, 0.0, 0.0), + Vector3D<double>(PI*2.0, 1.0, 1.0), + Vector3D<double>( c, c, c)) + { } + HSLf_Space(Vector3D<double> const& v): + Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(v)) { + } + HSLf_Space(HSL_Space const& b): Color3_Space<double>(b) { + } + ~HSLf_Space() { + } + double const& hslMin(size_t i) const { return min(i); } + double const& hMin( ) const { return min(0); } + double const& sMin( ) const { return min(1); } + double const& lMin( ) const { return min(2); } + double const& hslMax(size_t i) const { return max(i); } + double const& hMax( ) const { return max(0); } + double const& sMax( ) const { return max(1); } + double const& lMax( ) const { return max(2); } + double const& hsl(size_t i) const { return val(i); } + double const& h( ) const { return hsl(0); } + double const& s( ) const { return hsl(1); } + double const& l( ) const { return hsl(2); } + double const& hsl(size_t i, double c) { return val(i, c); } + double const& h( double c) { return hsl(0, c); } + double const& s( double c) { return hsl(1, c); } + double const& l( double c) { return hsl(2, c); } + double& hslGet(size_t i) { return valGet(i); } + double& hGet( ) { return hslGet(0); } + double& sGet( ) { return hslGet(1); } + double& lGet( ) { return hslGet(2); } + HSLf_Space& operator=(HSLf_Space const& b) { + copyFrom(b); + return *this; + } + HSLf_Space operator+(HSLf_Space const& b) const { + return HSLf_Space(val_ + b.val_); + } + HSLf_Space operator-(HSLf_Space const& b) const { + return HSLf_Space(val_ - b.val_); + } + HSLf_Space operator*(double const& c) const { + return HSLf_Space(val_ * c); + } + HSLf_Space operator/(double const& c) const { + return HSLf_Space(val_ / c); + } + double operator*(HSLf_Space const& b) const { + return val_ * b.val_; + } +}; + +/*! + * @brief \c RGBf_Space to \c HSLf_Space + */ +inline void colorTransformate(RGBf_Space const& rgb, HSLf_Space* hsl) { + double r = normalize(rgb.rMin(), rgb.rMax(), rgb.r()); + double g = normalize(rgb.gMin(), rgb.gMax(), rgb.g()); + double b = normalize(rgb.bMin(), rgb.bMax(), rgb.b()); + double mx = std::max(std::max(r, g), b); + double mn = std::min(std::min(r, g), b); + double h, s, l; + if (mx == mn ) h = 0; + else if(mx == r && g >= b) h = PI/3.0 * (g-b) / (mx-mn); + else if(mx == r && g < b) h = PI/3.0 * (g-b) / (mx-mn) + PI * 2.0; + else if(mx == g ) h = PI/3.0 * (b-r) / (mx-mn) + PI/3.0*2.0; + else h = PI/3.0 * (r-g) / (mx-mn) + PI/3.0*4.0; + l = 0.5 * (mx + mn); + if (l == 0 || mx == mn) s = 0; + else if(l < 0.5 ) s = (mx - mn) / (2.0 * l); + else s = (mx - mn) / (2 - 2.0 * l); + hsl->h(h); + hsl->s(s); + hsl->l(l); +} + +/*! + * @brief \c YUVf_Space to \c HSLf_Space + */ +inline void colorTransformate(YUVf_Space const& yuv, HSLf_Space* hsl) { + RGBf_Space tmp; + colorTransformate(yuv, &tmp); + colorTransformate(tmp, hsl); +} + +/*! + * @brief \c HSLf_Space to \c RGBf_Space + */ +inline void colorTransformate(HSLf_Space const& hsl, RGBf_Space* rgb) { + double h = normalize(hsl.hMin(), hsl.hMax(), hsl.h()); + double s = normalize(hsl.sMin(), hsl.sMax(), hsl.s()); + double l = normalize(hsl.lMin(), hsl.lMax(), hsl.l()); + if(s == 0){ + rgb->r(denormalize(rgb->rMin(), rgb->rMax(), l)); + rgb->g(denormalize(rgb->gMin(), rgb->gMax(), l)); + rgb->b(denormalize(rgb->bMin(), rgb->bMax(), l)); + return ; + } + double q = (l < 0.5 ? (l * (1 + s)) : (l + s - (l * s))); + double p = 2 * l - q; + double t_r = h + 1.0 / 3.0; + double t_g = h; + double t_b = h - 1.0 / 3.0; + if(t_r < 0) t_r = t_r + 1.0; + if(t_r > 1) t_r = t_r - 1.0; + if(t_g < 0) t_g = t_g + 1.0; + if(t_g > 1) t_g = t_g - 1.0; + if(t_b < 0) t_b = t_b + 1.0; + if(t_b > 1) t_b = t_b - 1.0; + double r, g, b; + if (t_r < 1.0 / 6.0) r = p + (q - p) * 6 * t_r; + else if(t_r < 0.5 ) r = q; + else if(t_r < 2.0 / 3.0) r = p + (q - p) * 6 * (2.0 / 3.0 - t_r); + else r = p; + if (t_g < 1.0 / 6.0) g = p + (q - p) * 6 * t_g; + else if(t_g < 0.5 ) g = q; + else if(t_g < 2.0 / 3.0) g = p + (q - p) * 6 * (2.0 / 3.0 - t_g); + else g = p; + if (t_b < 1.0 / 6.0) b = p + (q - p) * 6 * t_b; + else if(t_b < 0.5 ) b = q; + else if(t_b < 2.0 / 3.0) b = p + (q - p) * 6 * (2.0 / 3.0 - t_b); + else b = p; + rgb->r(denormalize(rgb->rMin(), rgb->rMax(), r)); + rgb->g(denormalize(rgb->gMin(), rgb->gMax(), g)); + rgb->b(denormalize(rgb->bMin(), rgb->bMax(), b)); +} + +/*! + * @brief \c HSLf_Space to \c YUVf_Space + */ +inline void colorTransformate(HSLf_Space const& hsl, YUVf_Space* yuv) { + RGBf_Space tmp; + colorTransformate(hsl, &tmp); + colorTransformate(tmp, yuv); +} + +/*! + * @brief \c HSLf_Space to \c RGBi_Space + */ +inline void colorTransformate(HSLf_Space const& hsl, RGBi_Space* rgb) { + RGBf_Space tmp; + colorTransformate(hsl, &tmp); + rgb->copyFrom(tmp); +} + + +/*! + * @brief \c RGBi_Space to \c HSLf_Space + */ +inline void colorTransformate(RGBi_Space const& rgb, HSLf_Space* hsl) { + RGBf_Space tmp; + tmp.copyFrom(rgb); + colorTransformate(rgb, hsl); +} + +} // meow + + +#endif // colors_HSL_Space_H__ diff --git a/meowpp/colors/HSV_Space.h b/meowpp/colors/HSV_Space.h new file mode 100644 index 0000000..96ba33c --- /dev/null +++ b/meowpp/colors/HSV_Space.h @@ -0,0 +1,188 @@ +#ifndef colors_HSV_Space_H__ +#define colors_HSV_Space_H__ + +#include "Color3_Space.h" +#include "../geo/Vectors.h" + +#include "RGB_Space.h" +#include "YUV_Space.h" +#include "HSL_Space.h" +#include "../math/utility.h" + +#include <cstdlib> + +namespace meow { + +/*! + * @brief 以浮點數\b Y(亮度), \b U(色度), \b V(濃度) 三個值所組成的色彩空間 + * + * 其中範圍都介於0.0~1.0之間 + * + * @author cat_leopard + */ +class HSVf_Space: public Color3_Space<double> { +public: + HSVf_Space(): Color3_Space<double>(Vector3D<double>( 0.0, 0.0, 0.0), + Vector3D<double>(PI*2.0, 1.0, 1.0), + Vector3D<double>( 0.0, 0.0, 0.0)) { + } + HSVf_Space(double c): Color3_Space<double>(Vector3D<double>( 0.0, 0.0, 0.0), + Vector3D<double>(PI*2.0, 1.0, 1.0), + Vector3D<double>( c, c, c)) + { } + HSVf_Space(Vector3D<double> const& v): + Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(v)) { + } + HSVf_Space(HSV_Space const& b): Color3_Space<double>(b) { + } + ~HSVf_Space() { + } + double const& hsvMin(size_t i) const { return min(i); } + double const& hMin( ) const { return min(0); } + double const& sMin( ) const { return min(1); } + double const& vMin( ) const { return min(2); } + double const& hsvMax(size_t i) const { return max(i); } + double const& hMax( ) const { return max(0); } + double const& sMax( ) const { return max(1); } + double const& vMax( ) const { return max(2); } + double const& hsv(size_t i) const { return val(i); } + double const& h( ) const { return hsv(0); } + double const& s( ) const { return hsv(1); } + double const& v( ) const { return hsv(2); } + double const& hsv(size_t i, double c) { return val(i, c); } + double const& h( double c) { return hsv(0, c); } + double const& s( double c) { return hsv(1, c); } + double const& v( double c) { return hsv(2, c); } + double& hsvGet(size_t i) { return valGet(i); } + double& hGet( ) { return hsvGet(0); } + double& sGet( ) { return hsvGet(1); } + double& vGet( ) { return hsvGet(2); } + HSVf_Space& operator=(HSVf_Space const& b) { + copyFrom(b); + return *this; + } + HSVf_Space operator+(HSVf_Space const& b) const { + return HSVf_Space(val_ + b.val_); + } + HSVf_Space operator-(HSVf_Space const& b) const { + return HSVf_Space(val_ - b.val_); + } + HSVf_Space operator*(double const& c) const { + return HSVf_Space(val_ * c); + } + HSVf_Space operator/(double const& c) const { + return HSVf_Space(val_ / c); + } + double operator*(HSVf_Space const& b) const { + return val_ * b.val_; + } +}; + +/*! + * @brief \c RGBf_Space to \c HSVf_Space + */ +inline void colorTransformate(RGBf_Space const& rgb, HSVf_Space* hsv) { + double r = normalize(rgb.rMin(), rgb.rMax(), rgb.r()); + double g = normalize(rgb.gMin(), rgb.gMax(), rgb.g()); + double b = normalize(rgb.bMin(), rgb.bMax(), rgb.b()); + double mx = std::max(std::max(r, g), b); + double mn = std::min(std::min(r, g), b); + double h, s, v; + if (mx == mn ) h = 0; + else if(mx == r && g >= b) h = PI/3.0 * (g-b) / (mx-mn); + else if(mx == r && g < b) h = PI/3.0 * (g-b) / (mx-mn) + PI * 2.0; + else if(mx == g ) h = PI/3.0 * (b-r) / (mx-mn) + PI/3.0*2.0; + else h = PI/3.0 * (r-g) / (mx-mn) + PI/3.0*4.0; + if(mx == 0) s = 0; + else s = 1 - mn / mx; + v = mx; + hsv->h(h); + hsv->s(s); + hsv->v(v); +} + +/*! + * @brief \c YUVf_Space to \c HSVf_Space + */ +inline void colorTransformate(YUVf_Space const& yuv, HSVf_Space* hsv) { + RGBf_Space tmp; + colorTransformate( yuv, &tmp); + colorTransformate(*tmp, hsv); +} + +/*! + * @brief \c HSLf_Space to \c HSVf_Space + */ +inline void colorTransformate(HSLf_Space const& hsl, HSVf_Space* hsv) { + RGBf_Space tmp; + colorTransformate( hsl, &tmp); + colorTransformate(*tmp, hsv); +} + +/*! + * @brief \c HSVf_Space to \c RGBf_Space + */ +inline void colorTransformate(HSVf_Space const& hsv, RGBf_Space* rgb) { + double h = normalize(hsv.hMin(), hsv.hMax(), hsv.h()) * 360; + double s = normalize(hsv.sMin(), hsv.sMax(), hsv.s()); + double v = normalize(hsv.vMin(), hsv.vMax(), hsv.v()); + int hi = (int)h / 60 % 6; + double f = h / 60.0 - hi; + double p = v * (1 - s); + double q = v * (1 - f * s); + double t = v * (1 - (1 - f) * s); + double r, g, b; + if (hi == 0){ r = v; g = t; b = p; } + else if(hi == 1){ r = q; g = v; b = p; } + else if(hi == 2){ r = p; g = v; b = t; } + else if(hi == 3){ r = p; g = q; b = v; } + else if(hi == 4){ r = t; g = p; b = v; } + else { r = v; g = p; b = q; } + rgb->r(denormalize(rgb->rMin(), rgb->rMax(), r)); + rgb->g(denormalize(rgb->gMin(), rgb->gMax(), g)); + rgb->b(denormalize(rgb->bMin(), rgb->bMax(), b)); +} + +/*! + * @brief \c HSVf_Space to \c YUVf_Space + */ +inline void colorTransformate(HSVf_Space const& hsv, YUVf_Space* yuv) { + RGBf_Space tmp; + colorTransformate( hsv, &tmp); + colorTransformate(*tmp, yuv); +} + +/*! + * @brief \c HSVf_Space to \c HSLf_Space + */ +inline void colorTransformate(HSVf_Space const& hsv, HSLf_Space* hsl) { + RGBf_Space tmp; + colorTransformate( hsv, &tmp); + colorTransformate(*tmp, hsl); +} + +/*! + * @brief \c HSVf_Space to \c RGBi_Space + */ +inline void colorTransformate(HSVf_Space const& hsv, RGBi_Space* rgb) { + RGBf_Space tmp; + colorTransformate(hsv, &tmp); + rgb->copyFrom(tmp); +} + + +/*! + * @brief \c RGBi_Space to \c HSVf_Space + */ +inline void colorTransformate(RGBi_Space const& rgb, HSVf_Space* hsv) { + RGBf_Space tmp; + tmp.copyFrom(rgb); + colorTransformate(rgb, hsv); +} + +} // meow + + +#endif // colors_HSV_Space_H__ diff --git a/meowpp/colors/RGB_Space.h b/meowpp/colors/RGB_Space.h new file mode 100644 index 0000000..ea04598 --- /dev/null +++ b/meowpp/colors/RGB_Space.h @@ -0,0 +1,168 @@ +#ifndef colors_RGB_Space_H__ +#define colors_RGB_Space_H__ + +#include "Color3_Space.h" +#include "../geo/Vectors.h" +#include "../math/utility.h" + +#include <cstdlib> + +namespace meow { + +/*! + * @brief 以整數 \b Red, \b Green, \b Blue 三個值所組成的色彩空間 + * + * 其中範圍都介於0~255之間 + * + * @author cat_leopard + */ +class RGBi_Space: public Color3_Space<int> { +public: + RGBi_Space(): Color3_Space<int>(Vector3D<int>( 0, 0, 0), + Vector3D<int>(255, 255, 255), + Vector3D<int>( 0, 0, 0)) { + } + RGBi_Space(int c): Color3_Space<int>(Vector3D<int>( 0, 0, 0), + Vector3D<int>(255, 255, 255), + Vector3D<int>( c, c, c)) { + } + RGBi_Space(Vector3D<int> const& v): + Color3_Space<int>(Vector3D<int>( 0, 0, 0), + Vector3D<int>(255, 255, 255), + Vector3D<int>(v)) { + } + RGBi_Space(RGBi_Space const& b): Color3_Space<int>(b) { + } + ~RGBi_Space() { + } + int const& rgbMin(size_t i) const { return min(i); } + int const& rMin( ) const { return min(0); } + int const& gMin( ) const { return min(1); } + int const& bMin( ) const { return min(2); } + int const& rgbMax(size_t i) const { return max(i); } + int const& rMax( ) const { return max(0); } + int const& gMax( ) const { return max(1); } + int const& bMax( ) const { return max(2); } + int const& rgb(size_t i) const { return val(i); } + int const& r( ) const { return rgb(0); } + int const& g( ) const { return rgb(1); } + int const& b( ) const { return rgb(2); } + int const& rgb(size_t i, int c) { return val(i, c); } + int const& r( int c) { return rgb(0, c); } + int const& g( int c) { return rgb(1, c); } + int const& b( int c) { return rgb(2, c); } + int& rgbGet(size_t i) { return valGet(i); } + int& rGet( ) { return rgbGet(0); } + int& gGet( ) { return rgbGet(1); } + int& bGet( ) { return rgbGet(2); } + RGBi_Space& operator=(RGBi_Space const& b) { + copyFrom(b); + return *this; + } + RGBi_Space operator+(RGBi_Space const& b) const { + return RGBi_Space(val_ + b.val_); + } + RGBi_Space operator-(RGBi_Space const& b) const { + return RGBi_Space(val_ - b.val_); + } + RGBi_Space operator*(int c) const { + return RGBi_Space(val_ * c); + } + RGBi_Space operator/(int c) const { + return RGBi_Space(val_ / c); + } + int operator*(RGBi_Space const& b) const { + return val_ * b.val_; + } +}; + +/*! + * @brief 以浮點數\b Red, \b Green, \b Blue 三個值所組成的色彩空間 + * + * 其中範圍都介於0.0~1.0之間 + * + * @author cat_leopard + */ +class RGBf_Space: public Color3_Space<double> { +public: + RGBf_Space(): Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(0.0, 0.0, 0.0)) { + } + RGBf_Space(double c): Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>( c, c, c)) { + } + RGBf_Space(Vector3D<double> const& v): + Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(v)) { + } + RGBf_Space(RGBf_Space const& b): Color3_Space<double>(b) { + } + ~RGBf_Space() { + } + double const& rgbMin(size_t i) const { return min(i); } + double const& rMin( ) const { return min(0); } + double const& gMin( ) const { return min(1); } + double const& bMin( ) const { return min(2); } + double const& rgbMax(size_t i) const { return max(i); } + double const& rMax( ) const { return max(0); } + double const& gMax( ) const { return max(1); } + double const& bMax( ) const { return max(2); } + double const& rgb(size_t i) const { return val(i); } + double const& r( ) const { return rgb(0); } + double const& g( ) const { return rgb(1); } + double const& b( ) const { return rgb(2); } + double const& rgb(size_t i, double c) { return val(i, c); } + double const& r( double c) { return rgb(0, c); } + double const& g( double c) { return rgb(1, c); } + double const& b( double c) { return rgb(2, c); } + double& rgbGet(size_t i) { return valGet(i); } + double& rGet( ) { return rgbGet(0); } + double& gGet( ) { return rgbGet(1); } + double& bGet( ) { return rgbGet(2); } + RGBf_Space& operator=(RGBf_Space const& b) { + copyFrom(b); + return *this; + } + RGBf_Space operator+(RGBf_Space const& b) const { + return RGBf_Space(val_ + b.val_); + } + RGBf_Space operator-(RGBf_Space const& b) const { + return RGBf_Space(val_ - b.val_); + } + RGBf_Space operator*(double const& c) const { + return RGBf_Space(val_ * c); + } + RGBf_Space operator/(double const& c) const { + return RGBf_Space(val_ / c); + } + double operator*(RGBf_Space const& b) const { + return val_ * b.val_; + } +}; + +/*! + * @brief \c RGBi_Space to \c RGBf_Space + */ +inline void colorTransformate(RGBi_Space const& a, RGBf_Space* b) { + for (size_t i = 0; i < 3; ++i) { + b->rgb(i, ratioMapping<double>(a.rgbMin(i), a.rgbMax(i), a.rgb(i), + b->rgbMin(i), b->rgbMax(i))); + } +} + +/*! + * @brief \c RGBf_Space to \c RGBi_Space + */ +inline void colorTransformate(RGBf_Space const& a, RGBi_Space* b) { + for (size_t i = 0; i < 3; ++i) { + b->rgb(i, ratioMapping<double>(a.rgbMin(i), a.rgbMax(i), a.rgb(i), + b->rgbMin(i), b->rgbMax(i))); + } +} + +} // meow + +#endif // colors_RGB_Space_H__ diff --git a/meowpp/colors/YUV_Space.h b/meowpp/colors/YUV_Space.h new file mode 100644 index 0000000..651caaf --- /dev/null +++ b/meowpp/colors/YUV_Space.h @@ -0,0 +1,131 @@ +#ifndef colors_YUV_Space_H__ +#define colors_YUV_Space_H__ + +#include "Color3_Space.h" +#include "../geo/Vectors.h" + +#include "RGB_Space.h" +#include "../math/utility.h" + +#include <cstdlib> + +namespace meow { + +/*! + * @brief 以浮點數\b Y(亮度), \b U(色度), \b V(濃度) 三個值所組成的色彩空間 + * + * 其中範圍都介於0.0~1.0之間 + * + * @author cat_leopard + */ +class YUVf_Space: public Color3_Space<double> { +public: + YUVf_Space(): Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(0.0, 0.0, 0.0)) { + } + YUVf_Space(double c): Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>( c, c, c)) { + } + YUVf_Space(Vector3D<double> const& v): + Color3_Space<double>(Vector3D<double>(0.0, 0.0, 0.0), + Vector3D<double>(1.0, 1.0, 1.0), + Vector3D<double>(v)) { + } + YUVf_Space(YUV_Space const& b): Color3_Space<double>(b) { + } + ~YUVf_Space() { + } + double const& yuvMin(size_t i) const { return min(i); } + double const& yMin( ) const { return min(0); } + double const& uMin( ) const { return min(1); } + double const& vMin( ) const { return min(2); } + double const& yuvMax(size_t i) const { return max(i); } + double const& yMax( ) const { return max(0); } + double const& uMax( ) const { return max(1); } + double const& vMax( ) const { return max(2); } + double const& yuv(size_t i) const { return val(i); } + double const& y( ) const { return yuv(0); } + double const& u( ) const { return yuv(1); } + double const& v( ) const { return yuv(2); } + double const& yuv(size_t i, double c) { return val(i, c); } + double const& y( double c) { return yuv(0, c); } + double const& u( double c) { return yuv(1, c); } + double const& v( double c) { return yuv(2, c); } + double& yuvGet(size_t i) { return valGet(i); } + double& yGet( ) { return yuvGet(0); } + double& uGet( ) { return yuvGet(1); } + double& vGet( ) { return yuvGet(2); } + YUVf_Space& operator=(YUVf_Space const& b) { + copyFrom(b); + return *this; + } + YUVf_Space operator+(YUVf_Space const& b) const { + return YUVf_Space(val_ + b.val_); + } + YUVf_Space operator-(YUVf_Space const& b) const { + return YUVf_Space(val_ - b.val_); + } + YUVf_Space operator*(double const& c) const { + return YUVf_Space(val_ * c); + } + YUVf_Space operator/(double const& c) const { + return YUVf_Space(val_ / c); + } + double operator*(YUVf_Space const& b) const { + return val_ * b.val_; + } +}; + +/*! + * @brief \c RGBf_Space to \c YUVf_Space + */ +inline void colorTransformate(RGBf_Space const& rgb, YUVf_Space* yuv) { + double r = normalize(rgb.rMin(), rgb.rMax(), rgb.r()); + double g = normalize(rgb.gMin(), rgb.gMax(), rgb.g()); + double b = normalize(rgb.bMin(), rgb.bMax(), rgb.b()); + double y = 0.299 * r + 0.587 * g + 0.114 * b; + double u = -0.169 * r - 0.331 * g + 0.500 * b + 0.5; + double v = 0.500 * r - 0.419 * g - 0.081 * b + 0.5; + yuv->y(denormalize(yuv->yMin(), yuv->yMax(), y)); + yuv->u(denormalize(yuv->uMin(), yuv->uMax(), u)); + yuv->v(denormalize(yuv->vMin(), yuv->vMax(), v)); +} + +/*! + * @brief \c YUVf_Space to \c RGBf_Space + */ +inline void colorTransformate(YUVf_Space const& yuv, RGBf_Space* rgb) { + double y = normalize(yuv.yMin(),yuv.yMax(),yuv.y()); + double u = normalize(yuv.uMin(),yuv.uMax(),yuv.u()); + double v = normalize(yuv.vMin(),yuv.vMax(),yuv.v()); + double r = y - 0.00093 * (u - 0.5) + 1.401687 * (v - 0.5); + double g = y - 0.34370 * (u - 0.5) - 0.714170 * (v - 0.5); + double b = y + 1.77216 * (u - 0.5) - 0.000990 * (v - 0.5); + rgb->r(denormalize(rgb->rMin(), rgb->rMax(), r)); + rgb->g(denormalize(rgb->gMin(), rgb->gMax(), g)); + rgb->b(denormalize(rgb->bMin(), rgb->bMax(), b)); +} + +/*! + * @brief \c RGBi_Space to \c YUVf_Space + */ +inline void colorTransformate(RGBi_Space const& rgb, YUVf_Space* yuv) { + RGBf_Space tmp; + tmp.copyFrom(rgb); + colorTransformate(tmp, yuv); +} + +/*! + * @brief \c YUVf_Space to \c RGBi_Space + */ +inline void colorTransformate(YUVf_Space const& yuv, RGBi_Space* rgb) { + RGBf_Space tmp; + colorTransformate(yuv, &tmp); + rgb->copyFrom(tmp); +} + +} // meow + +#endif // colors_YUV_H__ diff --git a/meowpp/dsa/!readme.asciidoc b/meowpp/dsa/!readme.asciidoc new file mode 100644 index 0000000..d6eb3d7 --- /dev/null +++ b/meowpp/dsa/!readme.asciidoc @@ -0,0 +1,57 @@ + + +包含一些資料結構 + +===== BinaryIndexTree.h + +極度簡化的 *SegmentTree* 已無區間更新的操作. + +.Classes +* `meow::BinaryIndexTree<Value>` + +===== DisjointSet.h + +用來維護一堆互斥集的資訊. + +.Classes +* `meow::DisjointSet` + +===== HashTable.h + +就是傳說中的HashTable + +.Classes +* `meow::HashTableList<Data, HashFunc>` + +===== KD_Tree.h + +查詢第k近鄰居用的 + +.Classes +* `meow::KD_Tree<Vector>` + +===== MergeableHeap.h + +可合併Heap + +.Classes +* `meow::MergeableHeap<Element>` + +===== SegmentTree.h + +線段樹 +.Classes +* `meow::SegmentTree<Value>` + +===== SplayTree.h + +伸展樹, 比一般平衡樹稍強的東東 +* `meow::SplayTree<Key, Value>` +* `meow::SplayTree_Range<Key, Value>` + +===== VP_Tree.h + +查詢第k近鄰居用的 + +.Classes +* `meow::VP_Tree<Vector>` diff --git a/meowpp/dsa/BinaryIndexTree.h b/meowpp/dsa/BinaryIndexTree.h index 36f24d7..5525c62 100644 --- a/meowpp/dsa/BinaryIndexTree.h +++ b/meowpp/dsa/BinaryIndexTree.h @@ -4,70 +4,99 @@ #include <cstdlib> #include <vector> +#include <algorithm> -namespace meow{ - //# - //#=== meow:: *BinaryIndexTree<Value>* (C++ class) - //#==== Description - //# 極度簡化版的 `SegmentTree` 已無法區間操作, 區間詢問的區間開頭也一定要 - //# 在 `index=0` 的地方 - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator | Parameters | Return_Type| Description - //#|const | Value | operator+ |(Value `n`) | Value | 合併用(類似 - //# `SegmentTree` 的 - //# operator{b} ) - //#|===================================================================== - //# - template<class Value> - class BinaryIndexTree{ - private: - std::vector<Value> _array; - public: - BinaryIndexTree(); - BinaryIndexTree(size_t __size, Value const& __value); - BinaryIndexTree(BinaryIndexTree const& __tree2); - - //#==== Support Methods - //# - //# * N <- `this` 中擁有的資料數 - //# - //#[options="header",width="100%",cols="1>m,3>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||reset|(size_t `size`, Value const& `value`)|void|O(`size`) - //#|將資料長度刷成 `N = size` 且每一個元素都是 `value` - void reset(size_t __size, Value const& __value); - - - //#||update|(size_t `index`, Value const& `value`)|void|O(logN) - //#|將第 `index` (從零開始算) 多加上 `value` - void update( size_t __index, Value const& __value); - - - //#|const|query|(size_t `index`)|void|O(logN) - //#|詢問 `0~index` 的區間值 - Value query (ssize_t __index) const; - - - //#|===================================================================== - }; - //# - //#[NOTE] - //#======================================== - //# * 一般來說只能用在維護區間總和, 維護區間最大值只有在特殊情況才可以, 即 - //# '針對每個元素, 每次 update() 的值一定會大於等於原本的值' - //# * 若要用區間最大值 , 則 `Value` 的 `operator+` 要寫成 `std::max(...)` - //#======================================== - //# - //# ''' -} +namespace meow { -#include "BinaryIndexTree.hpp" +template<class Value> +/*! + * @brief 極度簡化的 \c SegmentTree 已無區間更新的操作 + * + * 一般來說只能用在維護區間總和, 維護區間最大值只有在特殊情況才可以, 即 + * \b 針對每個元素, \b 每次update() \b 的值一定會大於等於原本的值 . + * 若要用區間最大值 , 則 \a Value 的 \c operator+ 要寫成 \c std::max(...) + * + * @author cat_leopard + */ +class BinaryIndexTree { +private: + std::vector<Value> array_; +public: + /*! + * @brief constructor + */ + BinaryIndexTree() { + } -#endif // dsa_BinaryIndexTree_H__ + /*! + * @brief constructor + * + * @param [in] size 要維護的區間大小 \b [0,size) + * @param [in] value 預設值 + */ + BinaryIndexTree(size_t size, Value const& value): + array_(size, value) { + } + + /*! + * @brief constructor + * + * 將另一個 \c BinaryIndexTree 原封不動的複製過來 + * @param [in] tree2 另外一個 \c BinaryIndexTree + */ + BinaryIndexTree(BinaryIndexTree const& tree2): + array_(tree2.array_) { + } + + /*! + * @brief 將資料洗掉, 重設 + * + * 時間複雜度\b O(N) + * + * @param [in] size 要維護的區間大小 \b [0,size) + * @param [in] init 預設值 + * @return 無 + */ + void reset(size_t size, Value const& init) { + array_.clear(); + array_.resize(size, init); + } + + /*! + * @brief 將array中第 \a index (從零算起)個element多加上指定的值 + * + * 時間複雜度\b O(logN) + * + * @param [in] index 指定的index + * @param [in] value 指定的值 + * @return 無 + */ + void update(size_t index, Value const& value) { + index++; + for ( ; index <= array_.size(); index += (index & -index)) { + array_[index - 1] = array_[index - 1] + value; + } + } + + /*! + * @brief 詢問 \a 0~index 的區間值 + * + * 時間複雜度\b O(logN) + * + * @param [in] index 指定的index + * @return 區間值 + */ + Value query(ssize_t index) const { + index = std::min(index + 1, (ssize_t)array_.size()); + Value ret(0); + for ( ; 0 < index; index -= (index & -index)) { + ret = ret + array_[index - 1]; + } + return ret; + } +}; + +} + +#endif // dsa_BinaryIndexTree_H__ diff --git a/meowpp/dsa/DisjointSet.h b/meowpp/dsa/DisjointSet.h index bb777e1..4575835 100644 --- a/meowpp/dsa/DisjointSet.h +++ b/meowpp/dsa/DisjointSet.h @@ -1,73 +1,135 @@ #ifndef dsa_DisjointSet_H__ #define dsa_DisjointSet_H__ -#include <cstdlib> - #include <vector> +#include <cstdlib> +#include <cstdio> + +namespace meow { +/*! + * @brief 用來維護一堆互斥集的資訊 + * + * DisjointSet 是個 \b 輕量級Data \b Dtructure, 用來維護一堆互斥集的資訊. \n + * 相關資料可參考 + * <a href="http://www.csie.ntnu.edu.tw/~u91029/DisjointSets.html"> + * 演算法筆記 + * </a> + * + * @note + * - 時間複雜度 \b 非常快 表示它真的算的超級快, 可以視為常數時間 + * - 預設值所有 \a number 所在的集合的編號就是 \a number 本身, + * 即沒有任兩個數在同一個set裡面 + * + * @author cat_leopard + */ +class DisjointSet { +private: + size_t n_; + std::vector<size_t> father_; + std::vector<size_t> depth_; + // + size_t root_(size_t now) { + if (father_[now] == now) return now; + return (father_[now] = root_(father_[now])); + } + + size_t merge_(size_t a, size_t b) { + a = root_(a); + b = root_(b); + if (a == b) return a; + if (depth_[a] > depth_[b]) { + father_[b] = a; + return a; + } + else { + father_[a] = b; + if (depth_[a] == depth_[b]) depth_[b]++; + return b; + } + } +public: + /*! + *@brief constructor + */ + DisjointSet(): n_(0) { + } + + /*! + *@brief constructor + * + *@param [in] n elements數 + */ + DisjointSet(size_t n) { + reset(n); + } + + /*! + *@brief constructor + * + *將另一個 \c DisjointSet 原封不動的複製過來 + * + *@param [in] dsj 另一個 \c DisjointSet + */ + DisjointSet(DisjointSet const& dsj): + n_(dsj.n_), father_(dsj.father_), depth_(dsj.depth_) { + } + + /*! + *@brief 回傳指定的number所在的 \b 集合的編號 + * + *時間複雜度 \b 超級快 + * + *@param [in] a 指定的number + *@return 集合的編號 + */ + size_t root(size_t a) const { + return ((DisjointSet*)this)->root_(a); + } + + + /*! + *@brief 回傳總element數 + * + *@return 總element數 + */ + size_t size() const { + return n_; + } + + /*! + *@brief 重設 + * + *清空, 並且設定總集合大小為 \a n + * + *@param [in] n 重新設定的集合大小 \a n + *@return 無 + */ + void reset(size_t n) { + n_ = n; + father_.resize(n); + depth_ .resize(n); + for (size_t i = 0; i < n; i++) { + father_[i] = i; + depth_ [i] = 1; + } + } + + /*! + * @brief 合併 + * + * 將 \a number1 所在的集合 跟 \b number2 所在的集合 \b 合併, + * 並回傳合併後新的集合的編號. \n + * 時間複雜度\b 非常快 + * + * @param [in] a 即上述\a number1 + * @param [in] b 即上述\a number2 + * @return 新的編號 + */ + size_t merge(size_t a, size_t b) { + return merge_(a, b); + } +}; -namespace meow{ - //# - //#=== meow:: *DisjointSet* (C++ class) - //#==== Description - //# `DisjointSet` 是個*輕量級Data Dtructure*, 用來維護一堆互斥集的資訊. - //# 相關資料可參考 - //# link:http://www.csie.ntnu.edu.tw/~u91029/DisjointSets.html[演算法筆記] - //# - class DisjointSet{ - private: - size_t n; - std::vector<size_t> father; - std::vector<size_t> depth; - // - size_t _root(size_t now); - size_t _merge(size_t a, size_t b); - public: - DisjointSet(); - DisjointSet(size_t _n); - DisjointSet(DisjointSet const& dsj); - - //#==== Support Methods - //# - //#[options="header",width="100%",cols="1>m,3>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#|const |root |(size_t `number`) | size_t | very fast - //#|回傳 `number` 所在的 *集合的編號* (0~N-1) - size_t root (size_t a ) const; - - - //#|const |size |() | size_t | very fast - //#|回傳 *總集合大小* - size_t size ( ) const; - - - //#| |reset|(size_t `N`) | void | O(N) - //#| 清空, 並且設定總集合大小為 `N` - void reset(size_t _n ); - - - //#| |merge|(size_t `number1`,\size_t `number2`)| size_t | very fast - //#| 將 `number1` 所在的集合 跟 `number2` 所在的集合 *合併*, - //# 並回傳合併後新的集合的編號 - size_t merge(size_t a, size_t b); - - - //#|===================================================================== - }; - //# - //#[NOTE] - //#======================================== - //# * *very fast* 表示它算的真的超級快, 可以視為常數時間. - //# * 預設值所有 `number` 所在的集合的編號就是 `number` 本身, - //# 即沒有任兩個數在同一個set裡面 - //#======================================== - //# - //# ''' } -#include "DisjointSet.hpp" - - #endif // dsa_DisjointSet_H__ diff --git a/meowpp/dsa/HashTable.h b/meowpp/dsa/HashTable.h new file mode 100644 index 0000000..9171c72 --- /dev/null +++ b/meowpp/dsa/HashTable.h @@ -0,0 +1,217 @@ +#ifndef dsa_HashTable_H__ +#define dsa_HashTable_H__ + +#include <vector> +#include <list> + +namespace meow { + +/*! + * @brief 一個當key相撞時會用list解決的hash_table + * + * @author cat_leopard + */ +template<class Data, class HashFunc> +class HashTableList { +private: + std::vector<std::list<Data> > table_; + HashFunc func_; +public: + /*! + * @brief constructor + */ + HashTableList() { + } + + /*! + * @brief constructor + * + * 設定table size, hash function + */ + HashTableList(size_t size, HashFunc const& func): table_(size), func_(func) { + } + + /*! + * @brief destructor + */ + ~HashTableList() { + } + + /*! + * @brief copy + */ + HashTableList& copyFrom(HashTableList const& b) { + table_ = b.table_; + func_ = b.func_; + return *this; + } + + /*! + * @brief 清除資料 + */ + void clear() { + for (size_t i = 0, I = table_.size(); i < I; i++) { + table_[i].clear(); + } + } + + /*! + * @brief 清除資料, 指定新的size與hash function + */ + void reset(size_t size, HashFunc const& func) { + table_.clear(); + table_.resize(std::max(size, 1u)); + func_ = func; + } + + /*! + * @brief 回傳table size + */ + size_t tableSize() const { + return table_.size(); + } + + /*! + * @brief 回傳目前有多少element在其中 + */ + size_t size() const { + size_t ret = 0; + for (size_t i = 0, I = table_.size(); i < I; i++) { + ret += table_[i].size(); + } + return ret; + } + + /*! + * @brief 回傳hash function + */ + HashFunc const& func() const { + return func_; + } + + /*! + * @brief 加入新的element + */ + bool add(Data const& e) { + size_t index = func_(e) % size(); + table_[index].push_back(e); + return true; + } + + /*! + * @brief 把給定的HashTableList中所有的element全加進來 + */ + bool add(HashTableList const& h) { + for (size_t i = 0, I = h.table_.size(); i < I; i++) { + for (std::list<Data>::const_iterator + it = h.table_[index].begin(); it != h.table_[index].end(); ++it) { + insert(*it); + } + } + return true; + } + + /*! + * @brief 刪除element + */ + bool del(Data const& e) { + size_t index = func_(e) % size(); + for (std::list<Data>::const_iterator + it = table_[index].begin(); it != table_[index].end(); ++it) { + if ((*it) == e) { + table_[index].erase(i); + return true; + } + } + return false; + } + + /*! + * @brief 刪除有出現在給定的的HashTableList中的element + */ + bool del(HashTableList const& h) { + if (size() > h.size()) { + for (size_t i = 0, I = h.table_.size(); i < I; i++) { + for (std::list<Data>::const_iterator + it = h.table_[index].begin(); it != h.table_[index].end(); ++it) { + erase(*it); + } + } + } + else { + for (size_t i = 0, I = table_.size(); i < I; i++) { + for (std::list<Data>::const_iterator + it = table_[index].begin(); it != table_[index].end(); ) { + if (h.exist(*it)) { + table_[index].erase(it); + } + else { + ++it; + } + } + } + } + return true; + } + + /*! + * @brief 查看某element是否已經擁有 + */ + bool exist(Data const& e) const { + size_t index = func_(e) % size(); + for (std::list<Data>::const_iterator + it = table_[index].begin(); it != table_[index].end(); ++it) { + if ((*it) == e) + return true; + } + return false; + } + + /*! + * @brief 回傳所有存下來的資料 + */ + std::vector<Data> all() const { + std::vector<Data> ret; + for (size_t i = 0, I = table_.size(); i < I; i++) { + for (std::list<Data>::const_iterator + it = table_[i].begin(); it != table_[i].end(); ++it) { + ret.push_back(*it); + } + } + return ret; + } + + /*! + * @brief 回傳所有存下來且key為index的資料 + */ + std::vector<Data> all(size_t index) const { + index %= table_.size(); + std::vector<Data> ret; + for (std::list<Data>::const_iterator + it = table_[index].begin(); it != table_[index].end(); ++it) { + ret.push_back(*it); + } + return ret; + } + + //! @brief same as \c copyFrom(h) + HashTableList& operator=(HashTableList const& h) { + return copyFrom(h); + } + + //! @brief same as \c add(h) + HashTableList& operator+=(HashTableList const& h) { + add(h); + return *this; + } + + //! @brief same as \c del(h) + HashTableList& operator-=(HashTableList const& h) { + del(h); + return *this; + } +}; + +} + +#endif // dsa_HashTable_H__ diff --git a/meowpp/dsa/KD_Tree.h b/meowpp/dsa/KD_Tree.h index dcb60a7..05f9b1b 100644 --- a/meowpp/dsa/KD_Tree.h +++ b/meowpp/dsa/KD_Tree.h @@ -1,162 +1,303 @@ #ifndef dsa_KD_Tree_H__ #define dsa_KD_Tree_H__ +#include "../utility.h" +#include "../math/utility.h" + #include <cstdlib> #include <vector> +#include <algorithm> #include <queue> -namespace meow{ - //# - //#=== meow:: *KD_Tree<Vector, Scalar>* (C++ class) - //#==== Description - //# `KD_Tree` 全名k-dimension tree, 用來維護由 *N個K維度向量所成的集合*, - //# 並可於該set中查找 *前i個離給定向量最接近的向量* - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator | Parameters | Return_Type| Description - //#|const | Vector|operator[] |(size_t `n`) | Scalar | 取得第 `n` 維度量 - //#|const | Vector|operator< |(Vector `v`) | bool | 權重比較 - //#|const | Scalar|operator* |(Scalar `s`) | Scalar | 相乘 - //#|const | Scalar|operator+ |(Scalar `s`) | Scalar | 相加 - //#|const | Scalar|operator- |(Scalar `s`) | Scalar | 相差 - //#|const | Scalar|operator< |(Scalar `s`) | bool | 大小比較 - //#|===================================================================== - //# - template<class Vector, class Scalar> - class KD_Tree{ - private: - struct Node{ - Vector _vector; - ssize_t _lChild; - ssize_t _rChild; - // - Node(Vector __vector, ssize_t __lChild, ssize_t __rChild); - }; - typedef std::vector<Node> Nodes; - // - class Sorter{ - private: - Nodes const* _nodes; - size_t _cmp; - public: - Sorter(Nodes const* __nodes, size_t __cmp); - bool operator()(size_t const& __a, size_t const& __b) const; - }; - struct Answer{ - ssize_t _index; - Scalar _dist2; - // - Answer(ssize_t __index, Scalar __dist2); - Answer(Answer const& __answer2); - }; - class AnswerCompare{ - private: - Nodes const* _nodes; - bool _cmpValue; - public: - AnswerCompare(Nodes const* __nodes, bool __cmpValue); - bool operator()(Answer const& __a, Answer const& __b) const; - }; - typedef std::vector<Answer> AnswerV; - typedef std::priority_queue<Answer, AnswerV, AnswerCompare> Answers; - // - const ssize_t _NIL; - // - Nodes _nodes; - size_t _root; - bool _needRebuild; - size_t _dimension; - // - Scalar distance2(Vector const& __v1, Vector const& __v2) const; - // - void query(Vector const& __vector, - size_t __nearestNumber, - AnswerCompare const& __answerCompare, - ssize_t __index, - int __depth, - std::vector<Scalar>& __dist2Vector, - Scalar __dist2Minimum, - Answers *__out) const; - ssize_t build(ssize_t __beg, - ssize_t __end, - std::vector<size_t>* __orders, - int __depth); - public: - //#==== Custom Type Definitions - //# * `Vectors` <- `std::vector<Vector>` - //# - typedef typename std::vector<Vector> Vectors; - // - KD_Tree(); - KD_Tree(size_t __dimension); - ~KD_Tree(); - - //#==== Support Methods - //# - //# * N <- `this` 中擁有的資料數 - //# * K <- `this` 資料維度 - //# - //#[options="header",width="100%",cols="1>m,3>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||insert|(Vector const& `v`)|void| O(1) - //#|將向量 `v` 加到set中 - void insert(Vector const& __vector); - - - //#||erase|(Vector const& `v`)|bool| O(N) - //#|將向量 `v` 從set中移除, '~TODO:可以再優化~' - bool erase (Vector const& __vector); - - - //#||build|()|void|O(KN logN) or O(1) - //#|檢查距上一次 `build()` 至今是否有 `insert/erase` 被呼叫, - //# 若有, 重新建樹, 否則不做事 - void build(); +namespace meow { +/*! + * @brief \c k-dimension tree + * + * 全名k-dimension tree, 用來維護由\b N個K維度向量所成的集合, + * 並可於該set中查找 \b 前i個離給定向量最接近的向量 + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const |Vector |operator[] |(size_t \c n) |Scalar | 取得第 `n` 維度量 | + * |const |Vector |operator< |(Vector \c v) |bool | 權重比較 | + * |const |Scalar |operator* |(Scalar \c s) |Scalar | 相乘 | + * |const |Scalar |operator+ |(Scalar \c s) |Scalar | 相加 | + * |const |Scalar |operator- |(Scalar \c s) |Scalar | 相差 | + * |const |Scalar |operator< |(Scalar \c s) |bool | 大小比較 | + * + * @note: + * 此資料結構只有在 N >> 2 <sup>K</sup> 時才比較有優勢, + * 當 K 逐漸變大時, 所花時間會跟暴搜沒兩樣 + * + * @author cat_leopard + */ +template<class Vector, class Scalar> +class KD_Tree { +private: + struct Node { + Vector vector_; + ssize_t lChild_; + ssize_t rChild_; + + Node(Vector v, ssize_t l, ssize_t r): vector_(v), lChild_(l), rChild_(r){ + } + }; + typedef std::vector<Node> Nodes; + + class Sorter { + private: + Nodes const* nodes_; + size_t cmp_; + public: + Sorter(Nodes const* nodes, size_t cmp): + nodes_(nodes), cmp_(cmp){ + } + bool operator()(size_t const& a, size_t const& b) const{ + if ((*nodes_)[a].vector_[cmp_] != (*nodes_)[b].vector_[cmp_]) { + return ((*nodes_)[a].vector_[cmp_] < (*nodes_)[b].vector_[cmp_]); + } + return ((*nodes_)[a].vector_ < (*nodes_)[b].vector_); + } + }; + struct Answer { + ssize_t index_; + Scalar dist2_; + // + Answer(ssize_t index, Scalar dist2): + index_(index), dist2_(dist2) { + } + Answer(Answer const& answer2): + index_(answer2.index_), dist2_(answer2.dist2_) { + } + }; + class AnswerCompare { + private: + Nodes const* nodes_; + bool cmpValue_; + public: + AnswerCompare(Nodes const* nodes, bool cmpValue): + nodes_(nodes), cmpValue_(cmpValue) { + } + bool operator()(Answer const& a, Answer const& b) const { + if (cmpValue_ == true && a.dist2_ == b.dist2_) { + return ((*nodes_)[a.index_].vector_ < (*nodes_)[b.index_].vector_); + } + return (a.dist2_ < b.dist2_); + } + }; + typedef std::vector<Answer> AnswerV; + typedef std::priority_queue<Answer, AnswerV, AnswerCompare> Answers; + // + const ssize_t kNIL_; + // + Nodes nodes_; + size_t root_; + bool needRebuild_; + size_t dimension_; + // + Scalar distance2(Vector const& v1, Vector const& v2) const { + Scalar ret(0); + for(size_t i = 0; i < dimension_; i++){ + ret += squ(v1[i] - v2[i]); + } + return ret; + } + // + void query(Vector const& v, + size_t nearestNumber, + AnswerCompare const& answerCompare, + ssize_t index, + int depth, + std::vector<Scalar>& dist2Vector, + Scalar dist2Minimum, + Answers *out) const { + if (index == kNIL_) return ; + size_t cmp = depth % dimension_; + ssize_t this_side, that_side; + if (!(nodes_[index].vector_[cmp] < v[cmp])) { + this_side = nodes_[index].lChild_; + that_side = nodes_[index].rChild_; + }else{ + this_side = nodes_[index].rChild_; + that_side = nodes_[index].lChild_; + } + query(v, nearestNumber, answerCompare, + this_side, depth + 1, + dist2Vector, dist2Minimum, + out); + Answer my_ans(index, distance2(nodes_[index].vector_, v)); + if (out->size() < nearestNumber || answerCompare(my_ans, out->top())) { + out->push(my_ans); + if (out->size() > nearestNumber) out->pop(); + } + Scalar dist2_old(dist2Vector[cmp]); + dist2Vector[cmp] = squ(nodes_[index].vector_[cmp] - v[cmp]); + Scalar dist2Minimum2(dist2Minimum + dist2Vector[cmp] - dist2_old); + if (out->size() < nearestNumber || !(out->top().dist2_ < dist2Minimum)) { + query(v, nearestNumber, answerCompare, + that_side, depth + 1, + dist2Vector, dist2Minimum2, + out); + } + dist2Vector[cmp] = dist2_old; + } + ssize_t build(ssize_t beg, + ssize_t end, + std::vector<size_t>* orders, + int depth) { + if (beg > end) return kNIL_; + size_t tmp_order = dimension_; + size_t which_side = dimension_ + 1; + ssize_t mid = (beg + end) / 2; + size_t cmp = depth % dimension_; + for (ssize_t i = beg; i <= mid; i++) { + orders[which_side][orders[cmp][i]] = 0; + } + for (ssize_t i = mid + 1; i <= end; i++) { + orders[which_side][orders[cmp][i]] = 1; + } + for (size_t i = 0; i < dimension_; i++) { + if (i == cmp) continue; + size_t left = beg, right = mid + 1; + for (int j = beg; j <= end; j++) { + size_t ask = orders[i][j]; + if(ask == orders[cmp][mid]) { + orders[tmp_order][mid] = ask; + } + else if(orders[which_side][ask] == 1) { + orders[tmp_order][right++] = ask; + } + else { + orders[tmp_order][left++] = ask; + } + } + for (int j = beg; j <= end; j++) { + orders[i][j] = orders[tmp_order][j]; + } + } + nodes_[orders[cmp][mid]].lChild_ = build(beg, mid - 1, orders, depth + 1); + nodes_[orders[cmp][mid]].rChild_ = build(mid + 1, end, orders, depth + 1); + return orders[cmp][mid]; + } +public: + //! Custom Type: Vectors is \c std::vector<Vector> + typedef typename std::vector<Vector> Vectors; + + //! @brief constructor, with dimension = 1 + KD_Tree(): kNIL_(-1), root_(kNIL_), needRebuild_(false), dimension_(1) { + } + + //! @brief constructor, given dimension + KD_Tree(size_t dimension): + kNIL_(-1), root_(kNIL_), needRebuild_(false), dimension_(dimension) { + } + + //! @brief destructor + ~KD_Tree() { + } - //#||forceBuild|()|void|O(KN logN) - //#|重新建樹 - void forceBuild(); - + /*! + * @brief 將給定的Vector加到set中 + */ + void insert(Vector const& v) { + nodes_.push_back(Node(v, kNIL_, kNIL_)); + needRebuild_ = true; + } - //#|const|query|(Vector const& `v`,\size_t `i`,\bool `cmp`)|Vectors - //#|O(KN ^1-1/K^ ) - //#|於set中找尋距離 `v` 前 `i` 近的向量, 並依照由近而遠的順序排序. - //# 如果有兩個向量 `v1`,`v2` 距離一樣, 且 `cmp` 為 `true` , 則直接依照 - //# `v1 < v2` 來決定誰在前面. 最後回傳一陣列包含所有解. - Vectors query(Vector const& __vector, - size_t __nearestNumber, - bool __compareWholeVector) const; + /*! + * @brief 將給定的Vector從set移除 + */ + bool erase(Vector const& v) { + for (size_t i = 0, I = nodes_.size(); i < I; i++) { + if (nodes_[i] == v) { + if (i != I - 1) { + std::swap(nodes_[i], nodes_[I - 1]); + } + needRebuild_ = true; + return true; + } + } + return false; + } + /*! + * @brief 檢查至今是否有 insert/erase 被呼叫來決定是否 \c rebuild() + */ + void build(){ + if (needRebuild_) { + forceBuild(); + } + } - //#||clear|()|void|O(1) - //#|清空所有資料 - void clear(); + /*! + * @brief 重新建樹 + */ + void forceBuild() { + std::vector<size_t> *orders = new std::vector<size_t>[dimension_ + 2]; + for (size_t j = 0; j < dimension_ + 2; j++) { + orders[j].resize(nodes_.size()); + } + for (size_t j = 0; j < dimension_; j++) { + for (size_t i = 0, I = nodes_.size(); i < I; i++) { + orders[j][i] = i; + } + std::sort(orders[j].begin(), orders[j].end(), Sorter(&nodes_, j)); + } + root_ = build(0, (ssize_t)nodes_.size() - 1, orders, 0); + delete [] orders; + needRebuild_ = false; + } + /*! + * @brief 查找 + * + * 於set中找尋距離指定向量前 \c i 近的向量, 並依照由近而遠的順序排序. + * 如果有兩個向量\c v1,v2 距離一樣, 且 \c cmp 為\c true , 則直接依照 + * \c v1<v2 來決定誰在前面. 最後回傳一陣列包含所有解. + */ + Vectors query(Vector const& v, + size_t nearestNumber, + bool compareWholeVector) const { + ((KD_Tree*)this)->build(); + AnswerCompare answer_compare(&nodes_, compareWholeVector); + Answers answer_set(answer_compare); + std::vector<Scalar> tmp(dimension_, 0); + query(v, nearestNumber, + answer_compare, + root_, 0, + tmp, Scalar(0), + &answer_set); + Vectors ret(answer_set.size()); + for (int i = (ssize_t)answer_set.size() - 1; i >= 0; i--) { + ret[i] = nodes_[answer_set.top().index_].vector_; + answer_set.pop(); + } + return ret; + } - //#||reset|(size_t `dimension`)|void|O(1) - //#|清空所有資料並且指定維度為 `dimension` - void reset(size_t __dimension); + /*! + * @brief 清空所有資料 + */ + void clear() { + root_ = kNIL_; + nodes_.clear(); + needRebuild_ = false; + } + /*! + * @brief 清空所有資料並重新給定維度 + */ + void reset(size_t dimension) { + clear(); + dimension_ = dimension; + } +}; - //#|===================================================================== - }; - //# - //#[NOTE] - //#======================================== - //# * 此資料結構只有在 N >> 2 ^K^ 時才比較有優勢, - //# 當 K 逐漸變大時, 所花時間會跟暴搜沒兩樣 - //#======================================== - //# - //# ''' } -#include "KD_Tree.hpp" - #endif // dsa_KD_Tree_H__ diff --git a/meowpp/dsa/MergeableHeap.h b/meowpp/dsa/MergeableHeap.h index 0ddb100..af7ad75 100644 --- a/meowpp/dsa/MergeableHeap.h +++ b/meowpp/dsa/MergeableHeap.h @@ -2,107 +2,167 @@ #define dsa_MergeableHeap_H__ #include <cstdlib> - -namespace meow{ - //# - //#=== meow:: *MergeableHeap<Element>* (C++ class) - //#==== Description - //# 一個用 *左偏樹* 實作的 *Maximum-Heap* , 除了原本heap有的功能外, - //# 還支援 `merge`, `split` 等功能 - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator | Parameters | Return_Type| Description - //#|const |Element |operator< |(Element `v`)| bool | 大小比較 - //#|===================================================================== - //# - template<class Element> - class MergeableHeap{ // maximum-heap - private: - struct Node{ - Element value; - Node* l_child; - Node* r_child; - size_t weight; - Node(Element const& _value); - }; - // - Node* root; - // - void clear(Node* _node ); - Node* dup (Node* _node2 ); - Node* merge(Node* _left, Node* _right); - public: - MergeableHeap(); - MergeableHeap(MergeableHeap const& _heap2); - MergeableHeap& operator=(MergeableHeap const& _heap2); - ~MergeableHeap(); - - //#==== Support Methods - //# - //# * N <- `this` 中擁有的資料數 - //# - //#[options="header",width="100%",cols="1>m,2>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||moveTo|(MergeableHeap* `h`)|void|O(M) - //#|將 `this` 的資料複寫到 `h` 上面, 同時清空自己, - //# 時間複雜度中的M是 `h` 所擁有的資料數 - void moveTo(MergeableHeap* _heap2); - - - //#|const|top|()|Element const&|O(1) - //#|回傳最大的那個 `Element` - Element const& top() const; - - - //#|const|size|()|size_t|O(1) - //#|回傳 `this` 中擁有的資料數 - size_t size() const; - - - //#|const|empty|()|bool|O(1) - //#|回傳 `this` 是否為空 - bool empty() const; - - - //#||push|(Element const& `e`)|void|O(logN) - //#|將 `e` 加入 - void push(Element const& _value); - - - //#||pop|()|void|O(logN) - //#|將最大的 `Element` 移除 - void pop(); - - - //#||clear|()|void|O(N) - //#|將資料清空 - void clear(); - - - //#||merge|(MergeableHeap* `heap2`)|void|O(logN+logM) - //#|將 `heap2` 的資料統統加到 `this` 中, 並且清空 `heap2` - //# 時間複雜度中的M是 `heap2` 的資料數 - void merge(MergeableHeap* _heap2); - - //#|===================================================================== +#include <algorithm> + +namespace meow { + +/*! + * @brief + * + * 一個用 \b 左偏樹 實作的 \c Maximum-Heap , 除了原本heap有的功能外, + * 還支援 \c merge 功能 + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const |Element |operator< |(Element \c b)|bool | 大小比較 | + * + * @note: + * 假設現在有兩個MergeableHeap \c A 和 \c B, 則: + * - 執行 \c A.merge(&B) 後 \c B 會變成空的 + * - 執行 \c B.moveTo(&A) 後 \c B 會變成空的, \c A 原本擁有的資料也會覆蓋掉 + * + * @author cat_leopard + */ +template<class Element> +class MergeableHeap { // maximum-heap +private: + struct Node { + Element value_; + Node* lChild_; + Node* rChild_; + size_t weight_; + Node(Element const& value): + value_(value), lChild_(NULL), rChild_(NULL), weight_(1){ + } }; - //# - //# [WARNING] - //#============================================== - //# * 假設現在有兩個MergeableHeap `A` 和 `B`, 則: - //# ** 執行 `A.merge(&B)` 後 `B` 會變成空的 - //# ** 執行 `B.moveTo(&A)` 後 `B` 會變成空的, `A` 原本擁有的資料也會覆蓋掉 - //#============================================== - //# - //# ''' - //# -} -#include "MergeableHeap.hpp" + Node* root_; + + void clear(Node* node) { + if (node != NULL) { + clear(node->lChild_); + clear(node->rChild_); + delete node; + } + } + Node* dup(Node* node) { + if (node == NULL) return NULL; + Node* ret = new Node(node->value_); + ret->lChild_ = dup(node->lChild_); + ret->rChild_ = dup(node->rChild_); + ret->weight_ = 1; + ret->weight_ += (ret->lChild_ == NULL ? 0 : ret->lChild_->weight_); + ret->weight_ += (ret->rChild_ == NULL ? 0 : ret->rChild_->weight_); + return ret; + } + Node* merge(Node* left, Node* right) { + if (left == NULL) return right; + if (right == NULL) return left; + if (left->value_ < right->value_) { + std::swap(left, right); + } + left->rChild_ = merge(left->rChild_, right); + size_t lw = (left->lChild_ == NULL ? 0 : left->lChild_->weight_); + size_t rw = (left->rChild_ == NULL ? 0 : left->rChild_->weight_); + if (lw < rw) { + std::swap(left->lChild_, left->rChild_); + } + left->weight_ = 1 + lw + rw; + return left; + } +public: + //! @brief constructor + MergeableHeap(): root_(NULL){ + } + + //! @brief constructor, 並且複製資料 + MergeableHeap(MergeableHeap const& heap2): root_(dup(heap2.root_)) { + } + + //! @brief destructor + ~MergeableHeap(){ + clear(root_); + } + + //! @brief 複製資料 + MergeableHeap& copyFrom(MergeableHeap const& heap2) { + delete root_; + root_ = dup(heap2.root_); + return *this; + } + + /*! + * @brief 將自己的資料丟給指定的heap, 從此自己一身空 + */ + void moveTo(MergeableHeap* heap2){ + heap2->clear(); + heap2->root_ = root_; + root_ = NULL; + } + + /*! + * @brief 回傳最大的那個 Element + */ + Element const& top() const { + return root_->value_; + } + + /*! + * @brief 回傳資料個數 + */ + size_t size() const { + return (root_ == NULL ? 0 : root_->weight_); + } + + /*! + * @brief 回傳是否為空 + */ + bool empty() const { + return (size() == 0); + } + + /*! + * @brief 加入element + */ + void push(Element const& value) { + root_ = merge(root_, new Node(value)); + } + + /*! + * @brief 將最大的element移除 + */ + void pop() { + Node* l = root_->lChild_; + Node* r = root_->rChild_; + delete root_; + root_ = merge(l, r); + } + + /*! + * 將資料清空 + */ + void clear() { + clear(root_); + root_ = NULL; + } + + /*! + * 將給定的MergeableHeap的資料統統加到自己身上並且清空該heap + */ + void merge(MergeableHeap* heap2) { + root_ = merge(root_, heap2->root_); + heap2->root_ = NULL; + } + + //! @brief same as \c copyFrom(heap2) + MergeableHeap& operator=(MergeableHeap const& heap2) { + return copyFrom(heap2); + } +}; + +} #endif // dsa_MergeableHeap_H__ diff --git a/meowpp/dsa/SegmentTree.h b/meowpp/dsa/SegmentTree.h index 52d5a6f..b2fa749 100644 --- a/meowpp/dsa/SegmentTree.h +++ b/meowpp/dsa/SegmentTree.h @@ -1,100 +1,194 @@ #ifndef dsa_SegmentTree_H__ #define dsa_SegmentTree_H__ -#include <cstdlib> +#include "../math/utility.h" #include <vector> +#include <algorithm> + +#include <cstdlib> -namespace meow{ - //# - //#=== meow:: *SegmentTree<Value>* (C++ class) - //#==== Description - //# 維護一個陣列, 並且讓user可以有區間查詢, 區間修改的小東東 - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator | Parameters |Return_Type| Description - //#|const |Value |operator+ |(Value `v`)|Value | 相加(位移) - //#|const |Value |operator* |(size_t `n`)|Value | 每個Value都一樣, - //# 長為 `n` 的區間的值 - //#|const |Value |operator{b}|(Value `v`)|Value | 區間合併後的值 - //#|===================================================================== - //# - //# * 若要維護區間最小值, 即每次都是詢問範圍 `[a, b]` 的最小值, 則可以定義 - //# ** `operator+` 為 '回傳相加值' - //# ** `operator*` 為 '回傳*this' - //# ** `operator|` 為 '回傳std::min(*this, v)' - //# * 若要維護區間最總和, 即每次都是詢問範圍 `[a, b]` 的總和, 則可以定義 - //# ** `operator+` 為 '回傳相加值' - //# ** `operator*` 為 '回傳(*this) * n' - //# ** `operator|` 為 '回傳相加值' - //# - template<class Value> - class SegmentTree{ - private: - struct Node{ - Value _value; - Value _offset; - bool _sameFlag; - }; - // - size_t _size; - std::vector<Node> _nodes; - // - void update(size_t __index, size_t __size, Value const& __value, - bool __override); - void update(size_t __l, size_t __r, size_t __L, size_t __R, - size_t __index, Value const& __value, - bool __override); - Value query(size_t __l, size_t __r, size_t __L, size_t __R, - size_t __index); - // - bool rangeCorrect(ssize_t* __first, ssize_t* __last) const; - public: - SegmentTree(); - SegmentTree(size_t __size); - SegmentTree(SegmentTree const& __tree2); - // - //#==== Support Methods - //# - //# * N <- `this` 所維護的陣列長度 - //# - //#[options="header",width="100%",cols="1>m,2>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||reset|(size_t `size`)|void|O(1) - //#|將資料清空且設定維護範圍是 `0~size - 1` 其中時間複雜度確切多少未知 - //# 要看 `std::vector::resize()` 的效率 - void reset(size_t __size); - - - //#|const|query|(ssize_t `first`,\ssize_t `last`)|Value|O(logN) - //#|回傳區間 `[first,last]` (邊界都含) 的區間值 - Value query (ssize_t __first, ssize_t __last) const; - - - //#||override|(ssize_t `first`,\ssize_t `last`,\Value const& `value`) - //#|void|O(logN) - //#|將區間 `[first,last]` 全部都設定成 `value` - void override(ssize_t __first, ssize_t __last, Value const& __value); - - - //#||offset|(ssize_t `first`,\ssize_t `last`,\Value const& `delta`) - //#|void|O(logN) - //#|將區間 `[first,last]` 全部都加上 `delta` - void offset (ssize_t __first, ssize_t __last, Value const& __delta); - - - //#|===================================================================== +namespace meow { +/*! + * @brief 中文名 \c 線段樹 + * + * 維護一個陣列, 並且讓user可以有區間查詢, 區間修改的小東東 + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const |Vector |operator[] |(size_t \c n) |Scalar | 取得第 `n` 維度量 | + * |const |Vector |operator< |(Vector \c v) |bool | 權重比較 | + * |const |Scalar |operator* |(Scalar \c s) |Scalar | 相乘 | + * |const |Scalar |operator+ |(Scalar \c s) |Scalar | 相加 | + * |const |Scalar |operator- |(Scalar \c s) |Scalar | 相差 | + * |const |Scalar |operator< |(Scalar \c s) |bool | 大小比較 | + * |const |Value |operator+ |(Value \c v) |Value | 相加(位移) | + * |const |Value |operator* |(size_t \c n) |Value | 每個Value都一樣, + * 長為 `n` 的區間的值| + * |const |Value |operator{b}|(Value \c v) |Value | 區間合併後的值 | + * + * - 若要維護區間最小值, 即每次都是詢問範圍 `[a, b]` 的最小值, 則可以定義 + * - \c operator+ 為 '回傳相加值' + * - \c operator* 為 '回傳*this' + * - \c operator| 為 '回傳std::min(*this, v)' + * - 若要維護區間最總和, 即每次都是詢問範圍 `[a, b]` 的總和, 則可以定義 + * - \c operator+ 為 '回傳相加值' + * - \c operator* 為 '回傳(*this) * n' + * - \c operator| 為 '回傳相加值' + * + * @author cat_leopard + */ +template<class Value> +class SegmentTree { +private: + struct Node { + Value value_; + Value offset_; + bool sameFlage_; }; - //# - //# ''' - //# -} + // + size_t size_; + std::vector<Node> nodes_; + // + void update(size_t index, size_t size, Value const& value, bool override) { + if (override) { + nodes_[index].value_ = value * size; + nodes_[index].offset_ = value; + nodes_[index].sameFlage_ = true; + } + else { + nodes_[index].value_ = nodes_[index].value_ + value * size; + nodes_[index].offset_ = nodes_[index].offset_ + value; + } + } + void update(size_t l, size_t r, size_t L, size_t R, + size_t index, Value const& value, + bool override) { + if (l == L && r == R) { + update(index, R - L + 1, value, override); + return ; + } + size_t mid = (L + R) / 2; + if (L < R) { + update(index * 2 + 1, mid - L + 1, + nodes_[index].offset_, nodes_[index].sameFlage_); + update(index * 2 + 2, R - mid, + nodes_[index].offset_, nodes_[index].sameFlage_); + nodes_[index].offset_ = Value(0); + nodes_[index].sameFlage_ = false; + } + if (r <= mid) { + update(l, r, L ,mid, index * 2 + 1, value, override); + } + else if (mid + 1 <= l) { + update(l, r, mid + 1,R, index*2 + 2, value, override); + } + else { + update(l, mid , L, mid , index * 2 + 1, value, override); + update( mid + 1, r, mid + 1, R, index * 2 + 2, value, override); + } + nodes_[index].value_ = ( + (nodes_[index * 2 + 1].value_ | nodes_[index * 2 + 2].value_) + + nodes_[index].offset_ + ); + } + Value query(size_t l, size_t r, size_t L, size_t R, size_t index) { + if (l == L && r == R) return nodes_[index].value_; + Value off = nodes_[index].offset_ * (r - l + 1); + if (nodes_[index].sameFlage_) return off; + size_t mid = (L + R) / 2; + if (r <= mid) return query(l, r, L , mid, index * 2 + 1) + off; + else if(mid + 1 <= l) return query(l, r, mid + 1, R, index * 2 + 2) + off; + else{ + return ( query(l, mid , L, mid , index * 2 + 1) + | query( mid + 1, r, mid + 1, R, index * 2 + 2) + ) + off; + } + } + // + bool rangeCorrect(ssize_t* first, ssize_t* last) const { + if (*last < *first || *last < 0 || (ssize_t)size_ - 1 < *first) + return false; + *first = inRange((ssize_t)0, (ssize_t)size_ - 1, *first); + *last = inRange((ssize_t)0, (ssize_t)size_ - 1, *last ); + return true; + } +public: + //! @brief constructor + SegmentTree() { + reset(1); + } + + //! @brief constructor, with \c size gived + SegmentTree(size_t size) { + reset(size); + } + + //! @brief constructor, 並且複製資料 + SegmentTree(SegmentTree const& tree2): + size_(tree2.size_), nodes_(tree2.nodes_) { + } -#include "SegmentTree.hpp" + /*! + * @brief 複製 + */ + SegmentTree copyFrom(SegmentTree const& b) { + size_ = b.size_; + nodes_ = b.nodes_; + return *this; + } + + /*! + * @brief 回傳size + */ + size_t size() const { + return size_; + } + + /*! + * @brief 將資料清空且設定維護範圍是 \c 0~size-1 + */ + void reset(size_t size){ + size_ = std::max(size, (size_t)1); + nodes_.resize(size * 4); + nodes_[0].sameFlage_ = true; + nodes_[0].value_ = Value(0); + nodes_[0].offset_ = Value(0); + } + + /*! + * @brief 回傳區間 \c [first,last] (邊界都含) 的區間值 + */ + Value query(ssize_t first, ssize_t last) const { + if (rangeCorrect(&first, &last) == false) return Value(); + return ((SegmentTree*)this)->query(first, last, 0, size_ - 1, 0); + } + + /*! + * @brief 將區間 \c [first,last] 全部都設定成 \c value + */ + void override(ssize_t first, ssize_t last, Value const& value) { + if (rangeCorrect(&first, &last) == false) return ; + update(first, last, 0, size_ - 1, 0, value, true); + } + + /*! + * @brief 將區間 \c [first,last] 全部都加上 \c delta + */ + void offset(ssize_t first, ssize_t last, Value const& delta) { + if (rangeCorrect(&first, &last) == false) return ; + update(first, last, 0, size_ - 1, 0, delta, false); + } + + //! @brief same as copyFrom(b) + SegmentTree& operator=(SegmentTree const& b) { + return copyFrom(b); + } +}; + +} #endif // dsa_SegmentTree_H__ diff --git a/meowpp/dsa/SplayTree.h b/meowpp/dsa/SplayTree.h index da451b0..5e9fb3c 100644 --- a/meowpp/dsa/SplayTree.h +++ b/meowpp/dsa/SplayTree.h @@ -2,234 +2,1150 @@ #define dsa_SplayTree_h__ #include <cstdlib> - #include <utility> -namespace meow{ - //# - //#=== meow:: *SplayTree<Key, Value>* (C++ class) - //#==== Description - //# `SplayTree` 是一種神乎其技的資料結構, 維護一堆 Key->Value . 並且支援 - //# 一些 `std::map` 難以快速實踐的操作, 如 `split` , `merge` , `keyOffset` - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator| Parameters | Return_Type| Description - //#|const |Key |operator+|(Key `k`) | Key | 相加 - //#|const |Key |operator<|(Key `k`) | bool | 大小比較 - //#| |Key |'Key' |(int `n`) | | 建構子, `n` 永遠是0 - //#| |Value | 'Value' |( ) | | 建構子 - //#|===================================================================== - //# - template<class Key, class Value> - class SplayTree{ - private: - struct Node{ - Key _key; - Key _keyOffset; - Value _value; - size_t _size; - Node* _parent; - Node* _child[2]; - // - Node(Key const& __key, Value const& __value); - // - void keyOffset(Key const& __delta); - void syncDown() const; - void syncUp () const; - }; - // - Node* _root; - // - void connect(Node const* __parent, size_t __left_right, - Node const* __child) const; - Node const* splay (Node const* __node) const; - // - void clear(Node* __node); - Node* dup(Node* __node); - // - Node const* findKey (Node const* __node, Key const& __key ) const; - Node const* findMinMax(Node const* __node, bool __minimum) const; - Node const* findOrder (Node const* __node, size_t __order ) const; - // - void split(Node* __root, Node** __left, Node** __right); - Node* merge( Node* __left, Node* __right); - public: - //#==== Custom Type Definitions - //# - //# * `Element` -> 用來當作回傳資料的媒介 - //# ** 重定義 `operator->()` 到 `std::pair<Key const&, Value&>*` - //# ** 重定義 `operator*()` 到 `std::pair<Key const&, Value&>&` - //# ** 有 `operator==` , `operator!=`, `operator=` 可用 - //# ** 可以直接用 `(*e).second = some_value` 來改變SplayTree中的資料 - //# - class Element{ - private: - typedef std::pair<Key const&, Value&> Entry; - Entry* _entry; - Node * _node; - // - void reset(Node* __node); - public: - Element(); - Element(Node* __node); - Element(Element const& __iterator2); - ~Element(); - // - Element& operator=(Element const& __e2); - // - Entry* operator->(); - Entry& operator *(); - // - bool operator==(Element const& __e2) const; - bool operator!=(Element const& __e2) const; - }; - // - SplayTree(); - SplayTree(SplayTree const& __tree2); - ~SplayTree(); - SplayTree& operator=(SplayTree const& __tree2); - // - //#==== Support Methods - //# - //# * N <- `this` 中擁有的資料數 - //# * M <- `tree2` 中擁有的資料數 - //# - //#[options="header",width="100%",cols="1>m,3>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||moveTo|(SplayTree* `tree2`)|void|O(M) - //#|將 `this` 的資料複寫到 `tree2` 上面, 同時清空自己, - //# 時間複雜度中的M是 `tree2` 所擁有的資料數 - void moveTo(SplayTree* __tree2); - - - //#|const|lowerBound|(Key const& `k`)|Element|O(logN) - //#|找出第一個(最小的) Element且 `k` <= 它的 Key, 並且回傳之. - //# 找不到的話回傳 `this->end()` - Element lowerBound(Key const& __key) const; - - - //#|const|lowerBound|(Key const& `k`)|Element|O(logN) - //#|找出第一個(最小的) Element且 `k` < 它的 Key, 並且回傳之. - //# 找不到的話回傳 `this->end()` - Element upperBound(Key const& __key) const; - - - //#|const|lowerBound|(Key const& `k`)|Element|O(logN) - //#|找出第一個(最小的) Element且 `k` >= 它的 Key, 並且回傳之. - //# 找不到的話回傳 `this->end()` - Element rLowerBound(Key const& __key) const; - - - //#|const|lowerBound|(Key const& `k`)|Element|O(logN) - //#|找出第一個(最小的) Element且 `k` > 它的 Key, 並且回傳之. - //# 找不到的話回傳 `this->end()` - Element rUpperBound(Key const& __key) const; - - - //#|const|find|(Key const& `k`)|Element|O(logN) - //#|找出 Key= `k` 的Elemenet 並回傳. 找不到的話回傳 `this->end()` - Element find (Key const& __key) const; - - - //#|const|order|(size_t `ord`)|Element|O(logN) - //#|將Elements依照Key由小到大排序, 回傳第 `ord` 個Element (由0算起). - //# 其中如果 `ord` > N - 1, 則會回傳 `this->last()` - Element order(size_t __order) const; - - - //#|const|first|(size_t `ord`)|Element|O(logN) - //#|回傳Key最小的Element, 如果SplayTree為空, 則回傳 `this->end()` - Element first() const; - - - //#|const|last|(size_t `ord`)|Element|O(logN) - //#|回傳Key最大的Element, 如果SplayTree為空, 則回傳 `this->end()` - Element last() const; - - - //#|const|end|()|Element|O(1) - //#|回傳一個指向NULL的Element, 以供 `find` , `order` , `first` - //# , `last` 等判斷是否有找到相對應的Element - Element end() const; - - - //#|const|size|()|size_t|O(1)| 回傳資料數 - size_t size() const; - - - //#|const|size|()|bool|O(1)| 回傳是否為空 - bool empty() const; - - - //#||clear|()|void|O(N)| 清空資料 - void clear(); - - - //#||insert|(Key const& `key`,\Value const& `value`)|bool|O(logN) - //#|檢查是否已有Element的Key 為 `key`, 若有則回傳 `false` , 否則將 - //# 一個 (Key -> Value) = (`key` -> `value`)的Element加入, 並回傳 `true` - //# 將所有Element的Key同加上 `delta` - bool insert(Key const& __key, Value const& __value); - - - //#||erase|(Key const& `key`)|bool|O(logN) - //#|檢查是否已有Element的Key 為 `key`, 若有則刪除之, 並回傳 `true`, - //# 否則則回傳 `false` - bool erase(Key const& __key); - - - //#||keyOffset|(Key const& `delta`)|void|O(1) - //#|將所有Element的Key同加上 `delta` - void keyOffset(Key const& __delta); - - - //#||operator[]|(Key const& `key`)|Value&|O(logN) - //#|檢查是否已有Element的Key 為 `key`, 若有則回傳相對應的Value的Reference - //# 否則先執行 `insert(key, Value())` 再回傳相對應的Reference - Value& operator[](Key const& __key); - - - //#||splitOut|(Key const& `upper_bound`,\SplayTree* `tree2`)|void - //#|O(logN) + O(M) - //#|將 `tree2` 清空, 再將所有Key > `upper_bound` 的Element都丟到 `tree2` - void splitOut(Key const& __upper_bound, SplayTree* __right); - - - //#||mergeAfter|(SplayTree* `tree2`)|void|O(logN) + O(logM) - //#|檢查是否 `this` 中的 Key 都小於 `tree2` 中的Key, 是的話把 `tree2` - //# 中的 Element 都搬到 `this` , 同時清空 `tree2` , 回傳 `true`. 否則 - //# 回傳 `false` - bool mergeAfter(SplayTree* __tree2); - - - //#||merge|(SplayTree* `tree2`)|void|O(logN) + O(logM) - //#|檢查是否 `this` 中的 Key 都小於 `tree2` 中的Key 或者 - //# 是否 `this` 中的 Key 都大於 `tree2` 中的Key, 是的話把 `tree2` - //# 中的 Element 都搬到 `this` , 同時清空 `tree2` , 回傳 `true`. 否則 - //# 回傳 `false` - bool merge(SplayTree* __tree2); - - - //#|===================================================================== +#include "../math/utility.h" + +namespace meow { + +/*! + * @brief + * + * 是一種神乎其技的資料結構, 維護一堆 Key->Value . 並且支援 + * 一些 \c std::map 難以快速實踐的操作, 如 \c split , \c merge , \c keyOffset + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const |Key |operator+ |(Key \c k) | Key |相加 | + * |const |Key |operator< |(Key \c k) | bool |大小比較 | + * | |Key |operator= |(Key \c k) | Key |copy oper | + * | |Key |Key |(int \c n) | |構子,\c n 永遠是0 | + * | |Value | Value |( ) | |建構子 | + * + * @note: + * -假設現在有兩個SplayTree `A` 和 `B`, 則: + * -執行 `B.moveTo(&A)` 後 `B` 會變成空的, `A` 原本擁有的資料也會覆蓋掉 + * -行 `A.merge(&B)` 或 `A.mergeAfter(&B)` 後 + * 如果檢查發現確實可以merge, 則之後 `B` 會變成空的 + * + * @author cat_leopard + */ +template<class Key, class Value> +class SplayTree { +private: + struct Node { + Key key_; + Key keyOffset_; + Value value_; + size_t size_; + Node* parent_; + Node* child_[2]; + + Node(Key const& key, Value const& value): + key_(key), keyOffset_(0), value_(value) { + size_ = 1; + parent_ = NULL; + child_[0] = NULL; + child_[1] = NULL; + } + // + void keyOffset(Key const& delta) { + key_ = key_ + delta; + keyOffset_ = keyOffset_ + delta; + } + void syncDown() const { + for (size_t i = 0; i < 2; i++) { + if (child_[i] == NULL) continue; + child_[i]->keyOffset(keyOffset_); + } + ((Node*)this)->keyOffset_ = Key(0); + } + void syncUp() const { + ((Node*)this)->size_ = 1; + for (size_t i = 0; i < 2; i++) { + if (child_[i] == NULL) continue; + ((Node*)this)->size_ += child_[i]->size_; + } + } }; - //# - //#[NOTE] - //#======================================== - //# * 假設現在有兩個SplayTree `A` 和 `B`, 則: - //# ** 執行 `B.moveTo(&A)` 後 `B` 會變成空的, `A` 原本擁有的資料也會覆蓋掉 - //# ** 執行 `A.merge(&B)` 或 `A.mergeAfter(&B)` 後 - //# 如果檢查發現確實可以merge, 則之後 `B` 會變成空的 - //#======================================== - //# - //# ''' - //# -} -#include "SplayTree.hpp" + Node* root_; + + //! @brief 指定左子or右子, 連接parent<--->child + void connect(Node const* parent, size_t left_right, Node const* child) const { + Node* p = (Node*)parent; + Node* c = (Node*)child; + if (p != NULL) p->child_[left_right] = c; + if (c != NULL) c->parent_ = p; + } + + //! @brief 一路往上轉 + Node const* splay(Node const* node) const { + if (node != NULL && node->parent_ != NULL) { + for (const Node *g_grand, *grand, *parent, *child = node; ; ) { + g_grand = (grand = parent = child->parent_)->parent_; + size_t pc = (parent->child_[0] == child ? 0 : 1); + connect(parent, pc, child->child_[!pc]); + connect(child , !pc, parent); + if (g_grand != NULL) { + g_grand = (grand = g_grand)->parent_; + size_t gp = (grand->child_[0] == parent ? 0 : 1); + Node const* who = (pc == gp ? parent : child); + connect(grand, gp, who->child_[!gp]); + connect(who , !gp, grand); + grand->syncUp(); + } + parent->syncUp(); + child ->syncUp(); + if (g_grand == NULL) { + connect(NULL, 0, child); + break; + } + connect(g_grand, (g_grand->child_[0] == grand ? 0 : 1), child); + } + } + return (((SplayTree*)this)->root_ = (Node*)node); + } + + void clear(Node* node) { + if (node == NULL) return ; + clear(node->child_[0]); + clear(node->child_[1]); + delete node; + } + + Node* dup(Node* node2) { + if (node2 == NULL) return NULL; + node2->syncDown(); + Node* node = new Node(node2->key_, node2->value_); + connect(node, 0, dup(node2->child_[0])); + connect(node, 1, dup(node2->child_[1])); + node->syncUp(); + return node; + } + + Node const* findKey(Node const* node, Key const& key) const { + Node const* ret = node; + while (node != NULL) { + node->syncDown(); + ret = node; + if (!(key < node->key_)) { + if (!(node->key_< key)) break; + node = node->child_[1]; + } + else { + node = node->child_[0]; + } + } + return ret; + } + Node const* findMinMax(Node const* node, bool minimum) const { + Node const* ret = node; + for (int i = minimum ? 0 : 1; node != NULL; node = node->child_[i]) { + node->syncDown(); + ret = node; + } + return ret; + } + Node const* findOrder(Node const* node, size_t order) const { + Node const* ret = node; + while (node != NULL) { + node->syncDown(); + ret = node; + size_t ord = 1 + (node->child_[0] == NULL ? 0 : node->child_[0]->size_); + if (ord == order) return ret; + else if(ord < order){ node = node->child_[1]; order -= ord; } + else { node = node->child_[0]; } + } + return ret; + } + + void split(Node* root, Node** left, Node** right) { + if (root == NULL) { *left = NULL; *right = NULL; return ; } + root->syncDown(); + *left = root; + *right = root->child_[1]; + if (*right != NULL) { + (*left )->child_[1] = NULL; + (*right)->parent_ = NULL; + (*left )->syncUp(); + } + } + Node* merge(Node* left, Node* right) { + if (left == NULL) return right; + if (right == NULL) return left ; + left->syncDown(); + connect(left, 1, right); + left->syncUp(); + return left; + } +public: + /*! + * @brief 類似 \c stl 的 \c iterator ,不過這邊叫做\c Element + * + * 用來當作回傳資料的媒介 + */ + class Element{ + private: + typedef std::pair<Key const&, Value&> Entry; + Entry* entry_; + Node * node_; + // + void reset(Node* node) { + node_ = node; + delete entry_; + entry_ = (node == NULL ? NULL : new Entry(node->key_, node->value_)); + } + public: + Element(): entry_(NULL), node_(NULL) { + } + Element(Node* node): entry_(NULL), node_(NULL) { + reset(node); + } + Element(Element const& element2): entry_(NULL), node_(NULL) { + reset(element2.node_); + } + ~Element(){ + delete entry_; + } + + //! @brief 複製資料 + Element& copyFrom(Element const& e) { + reset(e.node_); + return *this; + } + + //! @brief 比對兩者是否為指向同一個Entry + bool same(Element const& e2) const { + return (node_ == e2.node_); + } + + //! @brief same as copyFrom + Element& operator=(Element const& e2) { + return copyFrom(e2); + } + + //! @brief 重導至\c std::pair<Key \c const&,\c Value&>* + Entry* operator->() { + return entry_; + } + + //! @brief 重導至\c std::pair<Key \c const&,\c Value&>& + Entry& operator*() { + return *entry_; + } + + //! @brief same as \c same(e2) + bool operator==(Element const& e2) const{ + return same(e2); + } + + //! @brief same as \c !same(e2) + bool operator!=(Element const& e2) const{ + return !same(e2); + } + }; + + //! @brief constructor + SplayTree(): root_(NULL) { + } + + //! @brief constructor, 複製資料 + SplayTree(SplayTree const& tree2): + root_(dup((Node*)(tree2.root_))) { + } + + //! @brief destructor + ~SplayTree(){ + clear(root_); + } + + /*! + * @brief 複製資料 + */ + SplayTree& copyFrom(SplayTree const& tree2) { + clear(root_); + root_ = dup((Node*)(tree2.root_)); + return *this; + } + + /*! + * @brief 將資料都丟到 \c tree2 身上, 並且清空自己 + */ + void moveTo(SplayTree* tree2) { + tree2->clear(); + tree2->root_ = root_; + root_ = NULL; + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k <= 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element lowerBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || !(root_->key_ < key)) return Element(root_); + if (root_->child_[1] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[1], true)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k < 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element upperBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || key < root_->key_) return Element(root_); + if (root_->child_[1] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[1], true)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k >= 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element rLowerBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || !(key < root_->key_)) return Element(root_); + if (root_->child_[0] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[0], false)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k > 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element rUpperBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || root_->key_ < key) return Element(root_); + if (root_->child_[0] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[0], false)); + return Element(root_); + } + + /*! + * @brief 找出 Key= \c k 的Elemenet 並回傳. 找不到的話回傳 \c this->end() + */ + Element find(Key const& key) const { + splay(findKey(root_, key)); + if (root_ != NULL && !(key < root_->key_) && !(root_->key_ < key)) { + return Element(root_); + } + return Element(NULL); + } + + /*! + * @brief 將Elements依照Key由小到大排序, 回傳第 \c ord 個Element (由0算起). + * + * 其中如果 \c ord>N-1, 則會回傳 \c this->last() + */ + Element order(size_t order) const { + if (root_ == NULL || order >= root_->size_) return Element(NULL); + splay(findOrder(root_, order + 1)); + return Element(root_); + } + + /*! + * @brief 回傳Key最小的Element, 如果SplayTree為空, 則回傳 \c this->end() + */ + Element first() const { + splay(findMinMax(root_, true)); + return Element(root_); + } + + /*! + * @brief 回傳Key最大的Element, 如果SplayTree為空, 則回傳 \c this->end() + */ + Element last() const { + splay(findMinMax(root_, false)); + return Element(root_); + } + + /*! + * @brief 回傳一個指向NULL的Element, + * + * 以供 \c find ,\c order ,\c first ,\c last 等判斷是否有找到相對應的Element + */ + Element end() const { + return Element(NULL); + } + + /*! + * @brief 回傳資料個數 + */ + size_t size() const { + return (root_ == NULL ? 0 : root_->size_); + } + + /*! + * @brief 回傳是否為空 + */ + bool empty() const{ + return (size() == 0); + } + + /*! + * @brief 清空 + */ + void clear() { + clear(root_); + root_ = NULL; + } + + /*! + * @brief 插入一組\c (Key ---> \c Value) + * + * 檢查是否已有Element的Key 為 \c key, 若有則回傳 \c false , 否則將 + * 一個 (Key -> Value) = (\c key -> \c value)的Element加入, 並回傳 \c true + */ + bool insert(Key const& key, Value const& value) { + if (root_ == NULL) { + root_ = new Node(key, value); + } + else { + Node* parent = (Node*)findKey(root_, key); + if (!(parent->key_ < key) && !(key < parent->key_)) { + splay(parent); + return false; + } + Node* new_node = new Node(key, value); + connect(parent, (parent->key_ < key ? 1 : 0), new_node); + parent->syncUp(); + splay(new_node); + } + return true; + } + + /*! + * @brief 刪除一組資料 + * + * 檢查是否已有Element的Key 為 \c key, 若有則刪除之, 並回傳 \c true, + * 否則則回傳 \c false + */ + bool erase(Key const& key) { + if (root_ == NULL) return false; + Node* body = (Node*)findKey(root_, key); + if (body->key_ < key || key < body->key_) { + splay(body); + return false; + } + Node* ghost; + if (body->child_[1] == NULL) { + ghost = body->child_[0]; + if (ghost != NULL) ghost->syncDown(); + } + else { + ghost = (Node*)findMinMax(body->child_[1], true); + connect(ghost, 0, body->child_[0]); + if (ghost != body->child_[1]) { + connect(ghost->parent_, 0, ghost->child_[1]); + connect(ghost, 1, body->child_[1]); + for (Node* a = ghost->parent_; a != ghost; a = a->parent_) + a->syncUp(); + } + ghost->syncUp(); + } + Node* parent = body->parent_; + connect(parent, parent != NULL && parent->child_[0] == body ? 0 : 1, ghost); + delete body; + splay(ghost != NULL ? ghost : parent); + return true; + } + + /*! + * @brief 將所有Element的Key同加上 \c delta + */ + void keyOffset(Key const& delta) { + if (root_ != NULL) { + root_->keyOffset(delta); + } + } + + /*! + * @brief 將\c tree2 清空, 再將所有Key > \c upper_bound 的Element都丟過去 + */ + void splitOut(Key const& upper_bound, SplayTree* right) { + right->clear(); + if (rLowerBound(upper_bound) != end()) { + split(root_, &root_, &(right->root_)); + } + else { + right->root_ = root_; + root_ = NULL; + } + } + + /*! + * @brief 合併 + * + * 檢查是否自己中的 Key 都小於 \c tree2 中的Key, 是的話把 \c tree2` + * 中的 Element 都搬到自己這, 同時清空 \c tree2 , 否則回傳 \c false + */ + bool mergeAfter(SplayTree* tree2) { + if (root_ == NULL || tree2->root_ == NULL || + last()->first < tree2->first()->first) { + root_ = merge(root_, tree2->root_); + tree2->root_ = NULL; + return true; + } + return false; + } + + /*! + * @brief 合併 + * + * 檢查是否自己中的 Key 都小於 \c tree2 中的Key, 或是完全相反, + * 是的話把 \c tree2`中的 Element 都搬到自己這, + * 同時清空 \c tree2 , 否則回傳 \c false + */ + bool merge(SplayTree* tree2) { + if (root_ == NULL || tree2->root_ == NULL || + last()->first < tree2->first()->first) { + root_ = merge(root_, tree2->root_); + } + else if(tree2->last()->first < first()->first) { + root_ = merge(tree2->root_, root_); + } + else { + return false; + } + tree2->root_ = NULL; + return true; + } + + /*! + * @brief 就像\c stl::map::operator[] + * + * 會先檢查是否已有Element的Key 為 \c key, 若有則回傳相對應的Value的Reference + * 否則先執行 \c insert(key,Value()) 再回傳相對應的Reference + */ + Value& operator[](Key const& key) { + if (find(key) == end()) insert(key, Value()); + return root_->value_; + } + + //! @brief same as \c copyFrom(tree2) + SplayTree& operator=(SplayTree const& tree2) { + return copyFrom(tree2); + } +}; + +/*! + * @brief + * + * 基本上跟SplayTree一樣, 不過這邊結合線段樹, 多了區間操作 + * (線段樹相關operator定義請見 \c SegmentTree ) + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const |Key |operator+ |(Key \c k) | Key |相加 | + * |const |Key |operator< |(Key \c k) | bool |大小比較 | + * | |Key |operator= |(Key \c k) | Key |copy oper | + * | |Key |Key |(int \c n) | |構子,\c n 永遠是0 | + * | |Value | Value |( ) | |建構子 | + * + * @note: + * -假設現在有兩個SplayTree `A` 和 `B`, 則: + * -執行 `B.moveTo(&A)` 後 `B` 會變成空的, `A` 原本擁有的資料也會覆蓋掉 + * -行 `A.merge(&B)` 或 `A.mergeAfter(&B)` 後 + * 如果檢查發現確實可以merge, 則之後 `B` 會變成空的 + * + * @author cat_leopard + */ +template<class Key, class Value> +class SplayTree_Range { +private: + struct Node { + Value valueOffset_; + Value range_; + Key key_; + Key keyOffset_; + Value value_; + bool same_; + size_t size_; + Node* parent_; + Node* child_[2]; + + Node(Key const& key, Value const& value): + valueOffset_(0), range_(value), + key_(key), keyOffset_(0), value_(value) { + same_ = false; + size_ = 1; + parent_ = NULL; + child_[0] = NULL; + child_[1] = NULL; + } + // + void keyOffset(Key const& delta) { + key_ = key_ + delta; + keyOffset_ = keyOffset_ + delta; + } + void valueUpdate(Value const& delta, bool over) { + if(over) { + value_ = delta * size_; + valueOffset_ = delta; + range_ = delta * size_; + same_ = true; + } + else { + value_ = value_ + delta * size_; + valueOffset_ = valueOffset_ + delta; + range_ = range_ + delta * size_; + } + } + void syncDown() const { + for (size_t i = 0; i < 2; i++) { + if (child_[i] == NULL) continue; + child_[i]->keyOffset(keyOffset_); + child_[i]->valueUpdate(valueOffset_, same_); + } + ((Node*)this)->keyOffset_ = Key(0); + ((Node*)this)->valueOffset_ = Value(0); + ((Node*)this)->same_ = false; + } + void syncUp() const { + ((Node*)this)->size_ = 1; + Value* v[3] = {&(((Node*)this)->value_), NULL, NULL}; + size_t vct = 1; + for (size_t i = 0; i < 2; i++) { + if (child_[i] == NULL) continue; + ((Node*)this)->size_ += child_[i]->size_; + v[vct++] = &(child_[i]->range_); + } + if (vct == 1) ((Node*)this)->range_ = (*v[0]); + else if(vct == 2) ((Node*)this)->range_ = (*v[0]) | (*v[1]); + else ((Node*)this)->range_ = (*v[0]) | (*v[1]) | (*v[2]); + } + }; + + Node* root_; + + //! @brief 指定左子or右子, 連接parent<--->child + void connect(Node const* parent, size_t left_right, Node const* child) const { + Node* p = (Node*)parent; + Node* c = (Node*)child; + if (p != NULL) p->child_[left_right] = c; + if (c != NULL) c->parent_ = p; + } + + //! @brief 一路往上轉 + Node const* splay(Node const* node) const { + if (node != NULL && node->parent_ != NULL) { + for (const Node *g_grand, *grand, *parent, *child = node; ; ) { + g_grand = (grand = parent = child->parent_)->parent_; + size_t pc = (parent->child_[0] == child ? 0 : 1); + connect(parent, pc, child->child_[!pc]); + connect(child , !pc, parent); + if (g_grand != NULL) { + g_grand = (grand = g_grand)->parent_; + size_t gp = (grand->child_[0] == parent ? 0 : 1); + Node const* who = (pc == gp ? parent : child); + connect(grand, gp, who->child_[!gp]); + connect(who , !gp, grand); + grand->syncUp(); + } + parent->syncUp(); + child ->syncUp(); + if (g_grand == NULL) { + connect(NULL, 0, child); + break; + } + connect(g_grand, (g_grand->child_[0] == grand ? 0 : 1), child); + } + } + return (((SplayTree_Range*)this)->root_ = (Node*)node); + } + + void clear(Node* node) { + if (node == NULL) return ; + clear(node->child_[0]); + clear(node->child_[1]); + delete node; + } + + Node* dup(Node* node2) { + if (node2 == NULL) return NULL; + node2->syncDown(); + Node* node = new Node(node2->key_, node2->value_); + connect(node, 0, dup(node2->child_[0])); + connect(node, 1, dup(node2->child_[1])); + node->syncUp(); + return node; + } + + Node const* findKey(Node const* node, Key const& key) const { + Node const* ret = node; + while (node != NULL) { + node->syncDown(); + ret = node; + if (!(key < node->key_)) { + if (!(node->key_< key)) break; + node = node->child_[1]; + } + else { + node = node->child_[0]; + } + } + return ret; + } + Node const* findMinMax(Node const* node, bool minimum) const { + Node const* ret = node; + for (int i = minimum ? 0 : 1; node != NULL; node = node->child_[i]) { + node->syncDown(); + ret = node; + } + return ret; + } + Node const* findOrder(Node const* node, size_t order) const { + Node const* ret = node; + while (node != NULL) { + node->syncDown(); + ret = node; + size_t ord = 1 + (node->child_[0] == NULL ? 0 : node->child_[0]->size_); + if (ord == order) return ret; + else if(ord < order){ node = node->child_[1]; order -= ord; } + else { node = node->child_[0]; } + } + return ret; + } + + void split(Node* root, Node** left, Node** right) { + if (root == NULL) { *left = NULL; *right = NULL; return ; } + root->syncDown(); + *left = root; + *right = root->child_[1]; + if (*right != NULL) { + (*left )->child_[1] = NULL; + (*right)->parent_ = NULL; + (*left )->syncUp(); + } + } + Node* merge(Node* left, Node* right) { + if (left == NULL) return right; + if (right == NULL) return left ; + left->syncDown(); + connect(left, 1, right); + left->syncUp(); + return left; + } +public: + /*! + * @brief 類似 \c stl 的 \c iterator ,不過這邊叫做\c Element + * + * 用來當作回傳資料的媒介 + */ + class Element{ + private: + typedef std::pair<Key const&, Value&> Entry; + Entry* entry_; + Node * node_; + // + void reset(Node* node) { + node_ = node; + delete entry_; + entry_ = (node == NULL ? NULL : new Entry(node->key_, node->value_)); + } + public: + Element(): entry_(NULL), node_(NULL) { + } + Element(Node* node): entry_(NULL), node_(NULL) { + reset(node); + } + Element(Element const& element2): entry_(NULL), node_(NULL) { + reset(element2.node_); + } + ~Element(){ + delete entry_; + } + + //! @brief 複製資料 + Element& copyFrom(Element const& e) { + reset(e.node_); + return *this; + } + + //! @brief 比對兩者是否為指向同一個Entry + bool same(Element const& e2) const { + return (node_ == e2.node_); + } + + //! @brief same as copyFrom + Element& operator=(Element const& e2) { + return copyFrom(e2); + } + + //! @brief 重導至\c std::pair<Key \c const&,\c Value&>* + Entry* operator->() { + return entry_; + } + + //! @brief 重導至\c std::pair<Key \c const&,\c Value&>& + Entry& operator*() { + return *entry_; + } + + //! @brief same as \c same(e2) + bool operator==(Element const& e2) const{ + return same(e2); + } + + //! @brief same as \c !same(e2) + bool operator!=(Element const& e2) const{ + return !same(e2); + } + }; + + //! @brief constructor + SplayTree_Range(): root_(NULL) { + } + + //! @brief constructor, 複製資料 + SplayTree_Range(SplayTree_Range const& tree2): + root_(dup((Node*)(tree2.root_))) { + } + + //! @brief destructor + ~SplayTree_Range() { + clear(root_); + } + + /*! + * @brief 複製資料 + */ + SplayTree_Range& copyFrom(SplayTree_Range const& tree2) { + clear(root_); + root_ = dup((Node*)(tree2.root_)); + return *this; + } + + /*! + * @brief 將資料都丟到 \c tree2 身上, 並且清空自己 + */ + void moveTo(SplayTree_Range* tree2) { + tree2->clear(); + tree2->root_ = root_; + root_ = NULL; + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k <= 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element lowerBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || !(root_->key_ < key)) return Element(root_); + if (root_->child_[1] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[1], true)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k < 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element upperBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || key < root_->key_) return Element(root_); + if (root_->child_[1] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[1], true)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k >= 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element rLowerBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || !(key < root_->key_)) return Element(root_); + if (root_->child_[0] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[0], false)); + return Element(root_); + } + + /*! + * @brief 找出第一個(最小的) Element且 \c k > 它的 Key, 並且回傳之. + * + * 找不到的話回傳 \c this->end() + */ + Element rUpperBound(Key const& key) const { + splay(findKey(root_, key)); + if (root_ == NULL || root_->key_ < key) return Element(root_); + if (root_->child_[0] == NULL) return Element(NULL); + splay(findMinMax(root_->child_[0], false)); + return Element(root_); + } + + /*! + * @brief 找出 Key= \c k 的Elemenet 並回傳. 找不到的話回傳 \c this->end() + */ + Element find(Key const& key) const { + splay(findKey(root_, key)); + if (root_ != NULL && !(key < root_->key_) && !(root_->key_ < key)) { + return Element(root_); + } + return Element(NULL); + } + + /*! + * @brief 將Elements依照Key由小到大排序, 回傳第 \c ord 個Element (由0算起). + * + * 其中如果 \c ord>N-1, 則會回傳 \c this->last() + */ + Element order(size_t order) const { + if (root_ == NULL || order >= root_->size_) return Element(NULL); + splay(findOrder(root_, order + 1)); + return Element(root_); + } + + /*! + * @brief 回傳Key最小的Element, 如果SplayTree為空, 則回傳 \c this->end() + */ + Element first() const { + splay(findMinMax(root_, true)); + return Element(root_); + } + + /*! + * @brief 回傳Key最大的Element, 如果SplayTree為空, 則回傳 \c this->end() + */ + Element last() const { + splay(findMinMax(root_, false)); + return Element(root_); + } + + /*! + * @brief 回傳一個指向NULL的Element, + * + * 以供 \c find ,\c order ,\c first ,\c last 等判斷是否有找到相對應的Element + */ + Element end() const { + return Element(NULL); + } + + /*! + * @brief 回傳資料個數 + */ + size_t size() const { + return (root_ == NULL ? 0 : root_->size_); + } + + /*! + * @brief 回傳是否為空 + */ + bool empty() const{ + return (size() == 0); + } + + /*! + * @brief 查找 + * + * 詢問目前整個range的值 + */ + Value query() const { + if (root_ == NULL) return Value(0); + return root_->range_; + } + + /*! + * @brief 查找 + * + * 詢問給定range的值 + */ + Value query(Key const& first, Key const& last) const { + SplayTree_Range* self = (SplayTree_Range*)this; + Node* tmp; + rUpperBound(first); + self->split(self->root_, &tmp, &(self->root_)); + upperBound(last); + Value ret(0); + if (root_ != NULL && root_->child_[0] != NULL) { + ret = root_->child_[0]->range_; + } + self->root_ = self->merge(tmp, self->root_); + return ret; + } + + /*! + * @brief 清空 + */ + void clear() { + clear(root_); + root_ = NULL; + } + + /*! + * @brief 插入一組\c (Key ---> \c Value) + * + * 檢查是否已有Element的Key 為 \c key, 若有則回傳 \c false , 否則將 + * 一個 (Key -> Value) = (\c key -> \c value)的Element加入, 並回傳 \c true + */ + bool insert(Key const& key, Value const& value) { + if (root_ == NULL) { + root_ = new Node(key, value); + } + else { + Node* parent = (Node*)findKey(root_, key); + if (!(parent->key_ < key) && !(key < parent->key_)) { + splay(parent); + return false; + } + Node* new_node = new Node(key, value); + connect(parent, (parent->key_ < key ? 1 : 0), new_node); + parent->syncUp(); + splay(new_node); + } + return true; + } + + /*! + * @brief 刪除一組資料 + * + * 檢查是否已有Element的Key 為 \c key, 若有則刪除之, 並回傳 \c true, + * 否則則回傳 \c false + */ + bool erase(Key const& key) { + if (root_ == NULL) return false; + Node* body = (Node*)findKey(root_, key); + if (body->key_ < key || key < body->key_) { + splay(body); + return false; + } + Node* ghost; + if (body->child_[1] == NULL) { + ghost = body->child_[0]; + if (ghost != NULL) ghost->syncDown(); + } + else { + ghost = (Node*)findMinMax(body->child_[1], true); + connect(ghost, 0, body->child_[0]); + if (ghost != body->child_[1]) { + connect(ghost->parent_, 0, ghost->child_[1]); + connect(ghost, 1, body->child_[1]); + for (Node* a = ghost->parent_; a != ghost; a = a->parent_) + a->syncUp(); + } + ghost->syncUp(); + } + Node* parent = body->parent_; + connect(parent, parent != NULL && parent->child_[0] == body ? 0 : 1, ghost); + delete body; + splay(ghost != NULL ? ghost : parent); + return true; + } + + /*! + * @brief 將所有Element的Key同加上 \c delta + */ + void keyOffset(Key const& delta) { + if (root_ != NULL) { + root_->keyOffset(delta); + } + } + + /*! + * @brief 將所有Element的Value同加上 \c delta + */ + void valueOffset(Value const& delta){ + if (root_ != NULL) { + root_->valueUpdate(delta, false); + } + } + + /*! + * @brief 將所有Element的Value全部設定成\c value + */ + void valueOverride(Value const& value){ + if(root_ != NULL){ + root_->valueUpdate(value, true); + } + } + + /*! + * @brief 將\c tree2 清空, 再將所有Key > \c upper_bound 的Element都丟過去 + */ + void splitOut(Key const& upper_bound, SplayTree_Range* right) { + right->clear(); + if (rLowerBound(upper_bound) != end()) { + split(root_, &root_, &(right->root_)); + } + else { + right->root_ = root_; + root_ = NULL; + } + } + + /*! + * @brief 合併 + * + * 檢查是否自己中的 Key 都小於 \c tree2 中的Key, 是的話把 \c tree2` + * 中的 Element 都搬到自己這, 同時清空 \c tree2 , 否則回傳 \c false + */ + bool mergeAfter(SplayTree_Range* tree2) { + if (root_ == NULL || tree2->root_ == NULL || + last()->first < tree2->first()->first) { + root_ = merge(root_, tree2->root_); + tree2->root_ = NULL; + return true; + } + return false; + } + + /*! + * @brief 合併 + * + * 檢查是否自己中的 Key 都小於 \c tree2 中的Key, 或是完全相反, + * 是的話把 \c tree2`中的 Element 都搬到自己這, + * 同時清空 \c tree2 , 否則回傳 \c false + */ + bool merge(SplayTree_Range* tree2) { + if (root_ == NULL || tree2->root_ == NULL || + last()->first < tree2->first()->first) { + root_ = merge(root_, tree2->root_); + } + else if(tree2->last()->first < first()->first) { + root_ = merge(tree2->root_, root_); + } + else { + return false; + } + tree2->root_ = NULL; + return true; + } + + /*! + * @brief 就像\c stl::map::operator[] + * + * 會先檢查是否已有Element的Key 為 \c key, 若有則回傳相對應的Value的Reference + * 否則先執行 \c insert(key,Value()) 再回傳相對應的Reference + */ + Value& operator[](Key const& key) { + if (find(key) == end()) insert(key, Value()); + return root_->value_; + } + + //! @brief same as \c copyFrom(tree2) + SplayTree_Range& operator=(SplayTree_Range const& tree2){ + return copyFrom(tree2); + } +}; + +} #endif // dsa_SplayTree_h__ diff --git a/meowpp/dsa/VP_Tree.h b/meowpp/dsa/VP_Tree.h index e2daa28..75186e6 100644 --- a/meowpp/dsa/VP_Tree.h +++ b/meowpp/dsa/VP_Tree.h @@ -7,164 +7,331 @@ #include <list> #include <vector> +#include <stack> #include <queue> -namespace meow{ - //# - //#=== meow:: *VP_Tree<Vector, Scalar>* (C++ class) - //#==== Description - //# `VP_Tree` 用來維護由 *N個K維度向量所成的集合*, - //# 並可於該set中查找 *前i個離給定向量最接近的向量*. + - //# 不像 `KD_Tree` 二分樹每次都選擇一個維度去分, 分成小的跟大的, - //# `VP_Tree` 每次選一個點, 將資料分成 離這個點近的, 跟離這個點遠的. - //# 至於怎麼選呢...., 嘛還沒研究, 先random - //# - //# .參考資料連結: - //# * http://stevehanov.ca/blog/index.php?id=130[link] - //# * http://pnylab.com/pny/papers/vptree/vptree[link] - //# - //#==== Template Class Operators Request - //#[options="header",width="70%",cols="1>m,1<,3<s,5<,3<,15<",grid="rows"] - //#|===================================================================== - //#|Const?|Typename| Operator | Parameters | Return_Type| Description - //#|const | Vector|operator[] |(size_t `n`) | Scalar | 取得第 `n` 維度量 - //#|const | Vector|operator= |(Vector `v`) | Vector& | copy operator - //#|const | Vector|operator< |(Vector `v`) | bool | 權重比較 - //#|const | Scalar| 'Scalar' |(int `n`) | Scalar | 建構子, - //# 其中一定`n=0 or 4` - //#|const | Scalar|operator* |(Scalar `s`) | Scalar | 相乘 - //#|const | Scalar|operator+ |(Scalar `s`) | Scalar | 相加 - //#|const | Scalar|operator- |(Scalar `s`) | Scalar | 相差 - //#|const | Scalar|operator- |( ) | Scalar | 取負號 - //#|const | Scalar|operator< |(Scalar `s`) | bool | 大小比較 - //#|===================================================================== - //# - template<class Vector, class Scalar> - class VP_Tree{ - public: - //#==== Custom Type Definitions - //# * `Vectors` <- `std::vector<Vector>` - //# - typedef typename std::vector<Vector> Vectors; - private: - // - struct Node{ - size_t _index; - Scalar _threshold; - Node* _nearChild; - Node* _farChild; - Node(size_t __index); - }; - struct Answer{ - size_t _index; - Scalar _dist2; - // - Answer(size_t __index, Scalar const& __dist2); - Answer(Answer const& __answer2); - }; - class AnswerCompare{ - private: - Vectors const* _vectors; - bool _cmpValue; - public: - AnswerCompare(Vectors const* __vectors, bool __cmpValue); - bool operator()(Answer const& __a, Answer const& __b) const; - }; - typedef std::vector<Answer> AnswerV; - typedef std::priority_queue<Answer, AnswerV, AnswerCompare> Answers; - // - Vectors _vectors; - Node* _root; - size_t _dimension; - bool _needRebuild; - // - Scalar distance2(Vector const& __v1, Vector const& __v2) const; - int distanceCompare(Scalar const& __a2, Scalar const& __b2, - Scalar const& __c2) const; - Scalar split(ssize_t __first, ssize_t __last, size_t __order, - Vector const& __center); - // - Node* build(ssize_t __first, ssize_t __last); - void query(Vector const& __vector, - size_t __k, - AnswerCompare const& __cmp, - Node const* __node, - Answers* __out) const; - void clear(Node* __root); - Node* dup(Node* __root); - public: - VP_Tree(); - VP_Tree(VP_Tree const& __tree2); - VP_Tree(size_t __dimension); - ~VP_Tree(); - VP_Tree& operator=(VP_Tree const& __tree2); +namespace meow { - //#==== Support Methods - //# - //# * N <- `this` 中擁有的資料數 - //# * D <- `this` 資料維度 - //# - //#[options="header",width="100%",cols="1>m,3>s,7<,3<,3^,20<",grid="rows"] - //#|===================================================================== - //#|Const?|Name | Parameters | Return_Type| Time_Complexity| Description - - - //#||insert|(Vector const& `v`)|void| O(1) - //#|將向量 `v` 加到set中 - void insert(Vector const& __vector); - - - //#||erase|(Vector const& `v`)|bool| O(N) - //#|將向量 `v` 從set中移除, '~TODO:可以再優化~' - bool erase (Vector const& __vector); - - - //#||build|()|void|O(KN logN) or O(1) - //#|檢查距上一次 `build()` 至今是否有 `insert/erase` 被呼叫, - //# 若有, 重新建樹, 否則不做事 - void build(); - - - //#||forceBuild|()|void|O(KN logN) - //#|重新建樹 - void forceBuild(); +/*! + * @brief 跟KD_Tree很像歐 + * + * \c VP_Tree 用來維護由 \b N個K維度向量所成的集合 , + * 並可於該set中查找 \b 前i個離給定向量最接近的向量* . + * 不像 \c KD_Tree 二分樹每次都選擇一個維度去分, 分成小的跟大的, + * \c VP_Tree 每次選一個點, 將資料分成 離這個點近的, 跟離這個點遠的. + * 至於怎麼選呢...., 嘛還沒研究, 先random + * + * 參考資料連結: + * - http://stevehanov.ca/blog/index.php?id=130 + * - http://pnylab.com/pny/papers/vptree/vptree + * + * Template Class Operators Request + * -------------------------------- + * + * |const?|Typename|Operator | Parameters |Return Type | Description | + * |-----:|:------:|----------:|:-------------|:----------:|:------------------| + * |const | Vector|operator[] |(size_t \c n) | Scalar | 取得第\c n 維度量 | + * |const | Vector|operator= |(Vector \c v) | Vector& | copy operator | + * |const | Vector|operator< |(Vector \c v) | bool | 權重比較 | + * |const | Scalar| 'Scalar' |(int \c n) | Scalar | 建構子, + * 其中一定\c n=0or4 | + * |const | Scalar|operator* |(Scalar \c s) | Scalar | 相乘 | + * |const | Scalar|operator+ |(Scalar \c s) | Scalar | 相加 | + * |const | Scalar|operator- |(Scalar \c s) | Scalar | 相差 | + * |const | Scalar|operator- |( ) | Scalar | 取負號 | + * |const | Scalar|operator< |(Scalar \c s) | bool | 大小比較 | + * + * @note: + * -實測結果發覺, 維度小的時候, 比起中規中矩的 \c KD_Tree, \c VP_Tree 有 + * \b random 於其中, 因此時間複雜度只是期望值 \c O(logN) 但是測資大到 + * 一定程度, \c KD_Tree 效率會一整個大幅掉下, 但 \c VP_Tree 幾乎不受影響 + * -TODO \c insert(), \c erase() 算是未完成功能 + */ +template<class Vector, class Scalar> +class VP_Tree { +public: + typedef std::vector<Vector> Vectors; +private: + struct Node { + size_t index_; + Scalar threshold_; + Node* nearChild_; + Node* farChild_; + // + Node(size_t index): index_(index), nearChild_(NULL), farChild_(NULL){ + } + }; + struct Answer { + size_t index_; + Scalar dist2_; + // + Answer(size_t index, Scalar const& dist2): index_(index), dist2_(dist2){ + } + Answer(Answer const& answer2): + index_(answer2.index_), dist2_(answer2.dist2_){ + } + }; + class AnswerCompare { + private: + Vectors const* vectors_; + bool cmpValue_; + public: + AnswerCompare(Vectors const* vectors, bool cmpValue): + vectors_(vectors), cmpValue_(cmpValue){ + } + bool operator()(Answer const& a, Answer const& b) const { + if (a.dist2_ < b.dist2_) return true; + if (b.dist2_ < a.dist2_) return false; + return (cmpValue_ && ((*vectors_)[a.index_] < (*vectors_)[b.index_])); + } + }; + typedef std::vector<Answer> AnswerV; + typedef std::priority_queue<Answer, AnswerV, AnswerCompare> Answers; + + Vectors vectors_; + Node* root_; + size_t dimension_; + bool needRebuild_; + + Scalar distance2(Vector const& v1, Vector const& v2) const { + Scalar ret(0); + for (size_t i = 0; i < dimension_; i++) ret += squ(v1[i] - v2[i]); + return ret; + } + int distanceCompare(Scalar const& a2, Scalar const& b2, + Scalar const& c2) const { + if (b2 < 0) { + return -distanceCompare(c2, -b2, a2); + } + Scalar cab(c2 - a2 - b2); + if (cab < Scalar(0)) return 1; + Scalar ab2(Scalar(4) * a2 * b2), cab2(squ(cab)); + if ( ab2 < cab2) return -1; + else if (cab2 < ab2) return 1; + else return 0; + } + Scalar split(ssize_t first, ssize_t last, size_t order, + Vector const& center) { + ssize_t first0 = first; + std::vector<Scalar> dist2(last - first + 1); + for (ssize_t i = first; i <= last; i++) { + dist2[i - first0] = distance2(vectors_[i], center); + } + while (first < last) { + size_t thresholdindex_ = first + rand() % (last - first + 1); + Scalar threshold(dist2[thresholdindex_ - first0]); + size_t large_first = last + 1; + for( ssize_t i=first; first<=(ssize_t)large_first-1; large_first--) { + if (threshold < dist2[large_first - 1 - first0]) continue; + while (i < (ssize_t)large_first-1&&!(threshold < dist2[i-first0])) i++; + if (i < (ssize_t)large_first - 1){ + std::swap(dist2 [large_first - 1 - first0], dist2 [i - first0]); + std::swap(vectors_[large_first - 1 ], vectors_[i ]); + i++; + } + else { + break; + } + } + if (large_first == (size_t)last + 1) { + std::swap(dist2 [thresholdindex_-first0], dist2 [last-first0]); + std::swap(vectors_[thresholdindex_ ], vectors_[last ]); + if ((ssize_t)order == last - first) { + first = last; + break; + } + last--; + } + else { + if (order < large_first - first) { + last = large_first - 1; + } + else { + order -= large_first - first; + first = large_first; + } + } + } + return dist2[first - first0]; + } + // + Node* build(ssize_t first, ssize_t last) { + if (first > last) return NULL; + Node* ret = new Node(first); + if (first < last) { + std::swap(vectors_[first], + vectors_[first + rand() % (last - first + 1)]); + ssize_t mid = (first + 1 + last + 1) / 2; + ret->threshold_ = split(first + 1, last, mid - (first + 1), + vectors_[first]); + ret->nearChild_ = build(first + 1, mid - 1 ); + ret->farChild_ = build( mid , last); + } + return ret; + } + void query(Vector const& vector, + size_t k, + AnswerCompare const& cmp, + Node const* node, + Answers* out) const { + if (node == NULL) return ; + Scalar dist2 = distance2(vector, vectors_[node->index_]); + Answer my_ans(node->index_, dist2); + if (out->size() < k || cmp(my_ans, out->top())) { + out->push(my_ans); + if (out->size() > k) { + out->pop(); + } + } + if (node->nearChild_ == NULL && node->farChild_ == NULL) return ; + if (out->size() < k || distanceCompare(dist2, -out->top().dist2_, + node->threshold_) <= 0) { + query(vector, k, cmp, node->nearChild_, out); + } + if (out->size() < k || distanceCompare(dist2, out->top().dist2_, + node->threshold_) >= 0) { + query(vector, k, cmp, node->farChild_, out); + } + } + void clear(Node* root) { + if(root == NULL) return ; + clear(root->nearChild_); + clear(root->farChild_); + delete root; + } + Node* dup(Node* root) { + if(root == NULL) return ; + Node* ret = new Node(root->index_); + ret->threshold_ = root->threshold_; + ret->nearChild_ = dup(root->nearChild_); + ret->farChild_ = dup(root->farChild_ ); + return ret; + } +public: + //! @brief constructor, with dimension = 1 + VP_Tree(): root_(NULL), vectors_(0), dimension_(1), needRebuild_(false){ + reset(0); + } + + //! @brief constructor, 複製資料 + VP_Tree(VP_Tree const& tree2): + vectors_(tree2.vectors_), + root_(dup(tree2.root_)), + dimension_(tree2.dimension_), + needRebuild_(tree2.needRebuild_) { + } + + //! @brief constructor, 給定dimension + VP_Tree(size_t dimension): + vectors_(0), + root_(NULL), + dimension_(0), + needRebuild_(false) { + reset(dimension); + } + + //! @brief destructor + ~VP_Tree() { + clear(root_); + } + + /*! + * @brief 複製資料 + */ + VP_Tree& copyFrom(VP_Tree const& tree2) { + reset(tree2.dimension_); + vectors_ = tree2.vectors_; + root_ = dup(tree2.root_); + needRebuild_ = tree2.needRebuild_; + return *this; + } + /*! + * @brief 將給定的Vector加到set中 + */ + void insert(Vector const& vector) { + vectors_.push_back(vector); + needRebuild_ = true; + } - //#|const|query|(Vector const& `v`,\size_t `i`,\bool `cmp`)|Vectors - //#|O(logN) ~Expected~ - //#|於set中找尋距離 `v` 前 `i` 近的向量, 並依照由近而遠的順序排序. - //# 如果有兩個向量 `v1`,`v2` 距離一樣, 且 `cmp` 為 `true` , 則直接依照 - //# `v1 < v2` 來決定誰在前面. 最後回傳一陣列包含所有解. - Vectors query(Vector const& __vector, - size_t __nearestNumber, - bool __compareWholeVector) const; + /*! + * @brief 將給定的Vector從set移除 + */ + bool erase (Vector const& vector) { + for (ssize_t i = 0, I = vectors_.size(); i < I; i++) { + if (vectors_[i] == vector) { + if (i != I - 1) std::swap(vectors_[i], vectors_[I - 1]); + needRebuild_ = true; + vectors_.pop_back(); + return true; + } + } + return false; + } + /*! + * @brief 檢查至今是否有 insert/erase 被呼叫來決定是否 \c rebuild() + */ + void build() { + if (needRebuild_) { + forceBuild(); + } + } - //#||clear|()|void|O(1) - //#|清空所有資料 - void clear(); + /*! + * @brief 重新建樹 + */ + void forceBuild() { + root_ = build(0, (size_t)vectors_.size() - 1); + needRebuild_ = false; + } + /*! + * @brief 查找 + * + * 於set中找尋距離指定向量前 \c i 近的向量, 並依照由近而遠的順序排序. + * 如果有兩個向量\c v1,v2 距離一樣, 且 \c cmp 為\c true , 則直接依照 + * \c v1<v2 來決定誰在前面. 最後回傳一陣列包含所有解. + */ + Vectors query(Vector const& vector, + size_t nearestNumber, + bool compareWholeVector) const { + ((VP_Tree*)this)->build(); + AnswerCompare cmp(&vectors_, compareWholeVector); + Answers answers(cmp); + query(vector, nearestNumber, cmp, root_, &answers); + std::stack<Answer> rev; + for ( ; !answers.empty(); answers.pop()) rev.push(answers.top()); + Vectors ret; + for ( ; !rev.empty(); rev.pop()) ret.push_back(vectors_[rev.top().index_]); + return ret; + } - //#||reset|(size_t `dimension`)|size_t|O(1) - //#|清空所有資料並且指定維度為 `max(1, dimension)` 並且回傳指定後的維度 - size_t reset(size_t __dimension); + /*! + * @brief 清空所有資料 + */ + void clear() { + clear(root_); + vectors_.clear(); + root_ = NULL; + needRebuild_ = false; + } + /*! + * @brief 清空所有資料並重新給定維度 + */ + size_t reset(size_t dimension) { + clear(); + dimension_ = std::max((size_t)1, dimension); + return dimension_; + } + + //! @brief same as \c copyFrom(tree2) + VP_Tree& operator=(VP_Tree const& tree2) { + return copyFrom(tree2); + } +}; - //#|===================================================================== - }; - //# - //#[NOTE] - //#======================================== - //# * 實測結果發覺, 維度小的時候, 比起中規中矩的 `KD_Tree`, `VP_Tree` 有 - //# 'randomp' 於其中, 因此時間複雜度只是期望值 'O(logN)' 但是測資大到 - //# 一定程度, `KD_Tree` 效率會一整個大幅掉下, 但 `VP_Tree` 幾乎不受影響 - //# * 'TODO' `insert()`, `erase()` 算是未完成功能 - //# - //#======================================== - //# - //# ''' } -#include "VP_Tree.hpp" - #endif // dsa_VP_Tree_H__ diff --git a/meowpp/geo/!readme.asciidoc b/meowpp/geo/!readme.asciidoc new file mode 100644 index 0000000..7308afc --- /dev/null +++ b/meowpp/geo/!readme.asciidoc @@ -0,0 +1,12 @@ + +計算幾何相關, 算是從math中特化出來的 + +===== Vectors.h + +實作上不是用陣列, 是直接宣告2到3個變數分別存x, y (,z) + +.Classes +* `meow::Vector2D<Scalar>` +* `meow::Vector3D<Scalar>` + + diff --git a/meowpp/geo/Vectors.h b/meowpp/geo/Vectors.h new file mode 100644 index 0000000..8cdafc9 --- /dev/null +++ b/meowpp/geo/Vectors.h @@ -0,0 +1,510 @@ +#ifndef geo_Vectors_H__ +#define geo_Vectors_H__ + +#include "../math/utility.h" +#include "../math/Vector.h" +#include "../math/Matrix.h" + +#include <cmath> + +namespace meow{ + +/*! + * @brief 2D's vector + * + * @author cat_leopard + */ +template<class Scalar> +class Vector2D { +private: + Scalar x_, y_; +public: + //! @brief consturctor (0, 0) + Vector2D(): x_(0), y_(0) { + } + + //! @brief consturctor (from another Vector2D) + Vector2D(Vector2D const& v): x_(v.x_), y_(v.y_) { + } + + //! @brief constructor (s, s) + Vector2D(Scalar const& s): x_(s), y_(s) { + } + + //! @brief constructor (sx, sy) + Vector2D(Scalar const& sx, Scalar const& sy): x_(sx), y_(sy) { + } + + //! @brief constructor (from another Vector) + Vector2D(Vector<Scalar> const& v): x_(v(0)), y_(v(1)) { + } + + //! @brief constructor (from another Vector, i-th) + Vector2D(Vector<Scalar> const& v, size_t i): x_(v(i)), y_(v(i + 1)) { + } + + //! @brief destructor + ~Vector2D() { + } + + //! @brief copy + Vector2D& copyFrom(Vector2D const& v) { + return xy(v.x(), v.y()); + } + + //! @brief access x + Scalar const& x() const { + return x_; + } + + //! @brief access x with non constant reference + Scalar& xGet() { + return x_; + } + + //! @brief access y with non constant reference + Scalar& yGet() { + return y_; + } + + //! @brief access y + Scalar const& y() const { + return y_; + } + + //! @brief modify x + Scalar const& x(Scalar const& s) { + x_ = s; + return x(); + } + + //! @brief modify y + Scalar const& y(Scalar const& s) { + y_ = s; + return y(); + } + + //! @brief modify x and y + Vector2D& xy(Scalar const& sx, Scalar const& sy){ + x(sx); + y(sy); + return *this; + } + + //! @brief return \a +(*this) + Vector2D positive() const { + return *this; + } + + //! @brief return \a -(*this) + Vector2D negative() const { + return Vector2D(-x(), -y()); + } + + //! @brief return \a count-clockwise \a rotate \a 90 \a degree of itself + Vector2D right()const{ + return Vector2D(-y(), x()); + } + + //! @brief return \a (*this)+v + Vector2D add(Vector2D const& v) const { + return Vector2D(x() + v.x(), y() + v.y()); + } + + //! @brief Let itself add v + Vector2D& added(Vector2D const& v) { + return xy(x() + v.x(), y() + v.y()); + } + + //! @brief return \a (*this)-v + Vector2D sub(Vector2D const& v) const { + return Vector2D(x() - v.x(), y() - v.y()); + } + + //! @brief Let itself substract v + Vector2D& subed(Vector2D const& v) { + return xy(x() - v.x(), y() - v.y()); + } + + //! @brief return \a (*this)*s , where s is a scalar + Vector2D mul(Scalar const& s) const { + return Vector2D(x() * s, y() * s); + } + + //! @brief Let itself mulitple s + Vector2D& muled(Scalar const& s) { + return xy(x() * s, y() * s); + } + + //! @brief return \a (*this)/s , where s is a scalar + Vector2D div(Scalar const& s) const { + return Vector2D(x() / s, y() / s); + } + + //! @brief Let itself divide s + Vector2D& dived(Scalar const& s) { + return xy(x() / s, y() / s); + } + + //! @brief same as dot(v) + Scalar mul(Vector2D const& v) const { + return dot(v); + } + + //! @brief dot + Scalar dot(Vector2D const& v) const { + return x() * v.x() + y() * v.y(); + } + + //! @brief cross + Scalar cross(Vector2D const& v) const { + return x() * v.y() - y() * v.x(); + } + + //! @brief sqrt of length2 + Scalar length() const { + return Scalar(sqrt(double(length2()))); + } + + //! @brief same as \a dot(*this) + Scalar length2() const { + return dot(*this); + } + + //! @brief return normalize form of itself + Vector2D normalize() const { + return div(length()); + } + + //! @brief normalize itself + Vector2D& normalized() { + return dived(length()); + } + + //! @brief return rotate \a theta degree of itself + Vector2D rotate(Scalar const& theta) const { + Scalar cs(cos(-double(theta))); + Scalar sn(sin(-double(theta))); + Vector2D<Scalar> new_x(cs, sn); + return Vector2D(new_x.dot(*this), new_x.cross(*this)); + } + + //! @brief Let itself rotate \a theta degree + Vector2D& rotated(Scalar const& theta) { + return copyFrom(rotate(theta)); + } + + //! @brief return reflect from given vector \a v + Vector2D reflect(Vector2D const& v) const { + return v.mul(v.dot(*this) * 2 / v.length2()).sub(*this); + } + + //! @brief reflect itself given vector \a v + Vector2D& reflected(Vector2D const& v) { + return copyFrom(reflecte(v)); + } + + //! @brief return a 2x1 matrix form of itself + Matrix<Scalar> matrix() const { + static Matrix<Scalar> ret(2, 1, Scalar(0)); + ret(0, 0, x()); + ret(1, 0, y()); + return ret; + } + + //! @brief return a 3x1 matrix form of itself + Matrix<Scalar> matrix(Scalar const& homo) const { + static Matrix<Scalar> ret(3, 1, Scalar(0)); + ret(0, 0, x()); + ret(1, 0, y()); + ret(2, 0, homo); + return ret; + } + + Scalar const& operator()(size_t n) const { + return (n == 0 ? x() : y()); + } + + Vector2D& operator()(Scalar const& sx, Scalar const& sy) { + return xy(sx, sy); + } + + Vector2D operator+() const { return positive(); } + Vector2D operator-() const { return negative(); } + Vector2D operator~() const { return right (); } + + Vector2D operator+(Vector2D const& v) const { return add(v); } + Vector2D operator-(Vector2D const& v) const { return sub(v); } + Vector2D operator*(Scalar const& s) const { return mul(s); } + Vector2D operator/(Scalar const& s) const { return div(s); } + Scalar operator*(Vector2D const& v) const { return mul(v); } + + Vector2D& operator=(Vector2D const& v) { return copyFrom(v); } + Vector2D& operator+=(Vector2D const& v) { return added(v); } + Vector2D& operator-=(Vector2D const& v) { return subed(v); } + Vector2D& operator*=(Scalar const& s) { return muled(s); } + Vector2D& operator/=(Scalar const& s) { return dived(s); } +}; + +/*! + * @brief 3D's vector + * + * @author cat_leopard + */ +template<class Scalar> +class Vector3D{ +private: + Scalar x_, y_, z_; +public: + //! @brief consturctor (0, 0) + Vector3D(): x_(0), y_(0), z_(0) { + } + + //! @brief consturctor (from another Vector3D) + Vector3D(Vector3D const& v): x_(v.x_), y_(v.y_), z_(v.z_) { + } + + //! @brief constructor (s, s) + Vector3D(Scalar const& s): x_(s), y_(s), z_(s) { + } + + //! @brief constructor (sx, sy) + Vector3D(Scalar const& sx, + Scalar const& sy, + Scalar const& sz): x_(sx), y_(sy), z_(sz) { + } + + //! @brief constructor (from another Vector) + Vector3D(Vector<Scalar> const& v): x_(v(0)), y_(v(1)), z_(v(2)) { + } + + //! @brief constructor (from another Vector, i-th) + Vector3D(Vector<Scalar> const& v, size_t i): x_(v(i)), y_(v(i+1)), z_(v(i+2)){ + } + + //! @brief destructor + ~Vector3D(){ + } + + //! @brief copy + Vector3D& copyFrom(Vector3D const& v){ + return xyz(v.x(), v.y(), v.z()); + } + + //! @brief access x + Scalar const& x() const{ + return x_; + } + + //! @brief access y + Scalar const& y() const{ + return y_; + } + + //! @brief access z + Scalar const& z() const{ + return z_; + } + + //! @brief access x with non constant reference + Scalar& xGet() { + return x_; + } + + //! @brief access y with non constant reference + Scalar& yGet() { + return y_; + } + + //! @brief access z with non constant reference + Scalar& zGet() { + return z_; + } + + //! @brief modify x + Scalar const& x(Scalar const& s) { + x_ = s; + return x(); + } + + //! @brief modify y + Scalar const& y(Scalar const& s) { + y_ = s; + return y(); + } + + //! @brief modify z + Scalar const& z(Scalar const& s) { + z_ = s; + return z(); + } + + //! @brief modify x and y + Vector3D& xyz(Scalar const& sx, Scalar const& sy, Scalar const& sz) { + x(sx); + y(sy); + z(sz); + return *this; + } + + //! @brief return \a +(*this) + Vector3D positive() const { + return *this; + } + + //! @brief return \a -(*this) + Vector3D negative() const { + return Vector3D(-x(), -y(), -z()); + } + + //! @brief return \a (*this)+v + Vector3D add(Vector3D const& v) const { + return Vector3D(x() + v.x(), y() + v.y(), z() + v.z()); + } + + //! @brief Let itself add v + Vector3D& added(Vector3D const& v) { + return xyz(x() + v.x(), y() + v.y(), z() + v.z()); + } + + //! @brief return \a (*this)-v + Vector3D sub(Vector3D const& v) const { + return Vector3D(x() - v.x(), y() - v.y(), z() - v.z()); + } + + //! @brief Let itself substract v + Vector3D& subed(Vector3D const& v) { + return xyz(x() - v.x(), y() - v.y(), z() - v.z()); + } + + //! @brief return \a (*this)*s , where s is a scalar + Vector3D mul(Scalar const& s) const { + return Vector3D(x() * s, y() * s, z() * s); + } + + //! @brief Let itself mulitple s + Vector3D& muled(Scalar const& s) { + return xyz(x() * s, y() * s, z() * s); + } + + //! @brief return \a (*this)/s , where s is a scalar + Vector3D div(Scalar const& s) const { + return Vector3D(x() / s, y() / s, z() / s); + } + + //! @brief Let itself divide s + Vector3D& dived(Scalar const& s) { + return xyz(x() / s, y() / s, z() / s); + } + + //! @brief same as dot(v) + Scalar mul(Vector3D const& v) const { + return dot(v); + } + + //! @brief dot + Scalar dot(Vector3D const& v) const { + return x() * v.x() + y() * v.y() + z() * v.z(); + } + + //! @brief cross + Vector3D cross(Vector3D const& v) const { + return Vector3D(y() * v.z() - z() * v.y(), + z() * v.x() - x() * v.z(), + x() * v.y() - y() * v.x()); + } + + //! @brief crossed + Vector3D& crossed(Vector3D const& v) { + return copyFrom(cross(v)); + } + + //! @brief sqrt of length2 + Scalar length() const { + return Scalar(sqrt(double(length2()))); + } + + //! @brief same as \a dot(*this) + Scalar length2() const { + return dot(*this); + } + + //! @brief return normalize form of itself + Vector3D normalize() const { + return div(length()); + } + + //! @brief normalize itself + Vector3D& normalized() { + return dived(length()); + } + + //! @brief return rotate \a theta degree by \a axis of itself + Vector3D rotate(Vector3D const& axis, double theta) const { + Vector3D a(axis.normalize()); + Vector3D xx(sub(a) .mul(cos(theta))); + Vector3D yy(a.cross(*this).mul(sin(theta))); + return a.mul(a.dot(*this)).add(xx).add(yy); + } + + //! @brief Let itself rotate \a theta degree + Vector3D& rotated(Vector3D const& axis, double theta) { + return copyFrom(rotate(axis, theta)); + } + + //! @brief return reflect from given vector \a v + Vector3D reflect(Vector3D const& v) const { + return v.mul(v.dot(*this) * 2 / v.length2()).sub(*this); + } + + //! @brief reflect itself given vector \a v + Vector3D& reflected(Vector3D const& v) { + return copyFrom(reflecte(v)); + } + + //! @brief return a 3x1 matrix form of itself + Matrix<Scalar> matrix() const { + static Matrix<Scalar> ret(3, 1, Scalar(0)); + ret(0, 0, x()); + ret(1, 0, y()); + ret(2, 0, z()); + return ret; + } + + //! @brief return a 3x1 matrix form of itself + Matrix<Scalar> matrix(Scalar const& homo) const { + static Matrix<Scalar> ret(4, 1, Scalar(0)); + ret(0, 0, x()); + ret(1, 0, y()); + ret(2, 0, z()); + ret(3, 0, homo); + return ret; + } + + Scalar const& operator()(size_t n) const { + return (n == 0 ? x() : (n == 1 ? y() : z())); + } + + Vector3D& operator()(Scalar const& sx, Scalar const& sy, Scalar const& sz) { + return xyz(sx, sy, sz); + } + + Vector3D operator+() const { return positive(); } + Vector3D operator-() const { return negative(); } + + Vector3D operator+(Vector3D const& v) const { return add(v); } + Vector3D operator-(Vector3D const& v) const { return sub(v); } + Vector3D operator*(Scalar const& s) const { return mul(s); } + Vector3D operator/(Scalar const& s) const { return div(s); } + Scalar operator*(Vector3D const& v) const { return mul(v); } + + Vector3D& operator=(Vector3D const& v) { return copyFrom(v); } + Vector3D& operator+=(Vector3D const& v) { return added(v); } + Vector3D& operator-=(Vector3D const& v) { return subed(v); } + Vector3D& operator*=(Scalar const& s) { return muled(s); } + Vector3D& operator/=(Scalar const& s) { return dived(s); } +}; + +} + +#endif // geo_Vectors_H__ diff --git a/meowpp/gra/Bitmap.h b/meowpp/gra/Bitmap.h new file mode 100644 index 0000000..c4ed4e0 --- /dev/null +++ b/meowpp/gra/Bitmap.h @@ -0,0 +1,411 @@ +#ifndef gra_Bitmap_H__ +#define gra_Bitmap_H__ + + +#include "../Self.h" + +#include "../math/utility.h" +#include "../math/Matrix.h" + +#include "../oo/ObjBase.h" + +#include <vector> +#include <cmath> +#include <string> +#include <typeinfo> +#include <cstdlib> + +namespace meow{ + +/*! + * @brief 二維點陣資料 + * + * @author cat_leopard + */ +template<class Pixel> +class Bitmap: public ObjBase { +private: + Matrix<Pixel> matrix_; + // + static std::vector<double> gaussianFactor1(double sigma) { + double sigma2 = squ(sigma); + size_t width = std::max(ceil((double)(sigma * 2)), 0.0); + std::vector<double> factor(width + 1 + width); + for (size_t x = 0; x < width; x++) { + factor[width - x - 1] = exp(-(squ((double)x)) / (2.0 * sigma2)); + factor[width + x + 1] = exp(-(squ((double)x)) / (2.0 * sigma2)); + } + factor[width] = 1.0; + return factor; + } + static std::vector<double> gradianceFactor1(double sigma) { + double sigma2 = squ(sigma), ss = sigma * 2; + size_t width = std::max(ceil(ss), 1.0); + std::vector<double> factor(width + 1 + width); + for (size_t x = 0; x < width; x++) { + factor[width - x - 1] = (double)x * exp(-(squ((double)x))/(2.0*sigma2)); + factor[width + x + 1] = -(double)x * exp(-(squ((double)x))/(2.0*sigma2)); + } + factor[width] = 0.0; + return factor; + } + Bitmap xyBlur(std::vector<double> const& factor, + ssize_t dx, ssize_t dy) const { + Bitmap ret(*this); + if (factor.size() > 0) { + ssize_t w = factor.size() / 2; + for (size_t y = 0, Y = height(); y < Y; y++) { + for (size_t x = 0, X = width(); x < X; x++) { + Pixel sum(0); + double fsum(0); + for (ssize_t i = -w; i <= w; i++) { + ssize_t x2 = (ssize_t)x + dx * i; + ssize_t y2 = (ssize_t)y + dy * i; + if (0 <= x2 && x2 < (ssize_t)X && 0 <= y2 && y2 < (ssize_t)Y) { + sum = sum + pixel(y2, x2) * factor[i + w]; + fsum = fsum + fabs(factor[i + w]); + } + } + ret.pixel(y, x, sum / fsum); + } + } + } + return ret; + } +public: + /*! + * @brief constructor, 產生一個空的Bitmap + */ + Bitmap() { + } + + /*! + * @brief constructor, 複製一個bitmap + */ + Bitmap(Bitmap const& b): matrix_(b.matrix_) { + } + + /*! + * @brief constructor, 指定寬高, 預設\c Pixel + * + * @param [in] h 高 + * @param [in] w 寬 + * @param [in] p 預設pixel + */ + Bitmap(size_t h, size_t w, Pixel const& p): matrix_(h, w, p) { + } + + /*! + * @brief destructor + */ + ~Bitmap(){ + } + + /*! + * @brief 複製 + */ + Bitmap& copyFrom(Bitmap const& b) { + matrix_.copyFrom(b.matrix_); + return *this; + } + + /*! + * @brief reference + */ + Bitmap& referenceFrom(Bitmap const& b) { + matrix_.referenceFrom(b.matrix_); + return *this; + } + + /*! + * @brief 全部重設 + * + * @param [in] h 高 + * @param [in] w 寬 + * @param [in] p 預設pixel + * @return 無 + */ + void reset(size_t h, size_t w, Pixel const& p) { + matrix_.reset(h, w, p); + } + + /*! + * @brief 清除資料, 寬高階規零 + */ + void clear() { + matrix_.size(0, 0, Pixel(0)); + } + + /*! + * @brief 回傳高度 + */ + size_t height() const { + return matrix_.rows(); + } + + /*! + * @brief 回傳寬度 + */ + size_t width() const { + return matrix_.cols(); + } + + /*! + * @brief 回傳高度乘以寬度 + */ + size_t size() const { + return matrix_.size(); + } + + /*! + * @brief 修改高度 + * + * @param [in] h2 新的高 + * @param [in] p 如果高有變大, 則新長出來的地方用此 pixel填補 + * @return 新的高 + */ + size_t height(size_t h2, Pixel const& p) { + return matrix_.rows(h2, p); + } + + /*! + * @brief 修改寬度 + * + * @param [in] w2 新的寬 + * @param [in] p 如果寬有變大, 則新長出來的地方用此 pixel填補 + * @return 新的寬 + */ + size_t width(size_t w2, Pixel const& p) { + return matrix_.cols(w2, p); + } + + /*! + * @brief 修改寬高 + * + * @param [in] h2 新的高 + * @param [in] w2 新的寬 + * @param [in] p 如果寬or高有變大, 則新長出來的地方用此 pixel填補 + * @return 新的size + */ + size_t size(size_t h2, size_t w2, Pixel const& p) { + return matrix_.size(h2, w2, p); + } + + /*! + * @brief 取得 (y, x) 的pixel + * + * @param [in] y + * @param [in] x + * @return 該pixel 的 constant reference + */ + Pixel pixel(size_t y, size_t x) const { + return matrix_.entry(y, x); + } + + /*! + * @brief 修改 (y, x) 的pixel + * + * @param [in] y + * @param [in] x + * @param [in] p 指定的顏色 + * @return 該pixel 的 constant reference + */ + Pixel pixel(size_t y, size_t x, Pixel const& p) { + return matrix_.entry(y, x, p); + } + + /*! + * @brief 區塊修改 + * + * 一口氣將一個矩形區塊修改成指定的顏色 + * + * @param [in] yFirst y軸方向最小值(含) + * @param [in] yLast y軸方向最大值(含) + * @param [in] xFirst x軸方向最小值(含) + * @param [in] xLast x軸方向最大值(含) + * @param [in] p 指定的顏色 + * @return 無 + */ + void pixels(ssize_t yFirst, ssize_t yLast, + ssize_t xFirst, ssize_t xLast, + Pixel const& p) { + return matrix_.entries(yFirst, yLast, xFirst, xLast, p); + } + + /*! + * @brief 回傳高斯模糊 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return 一個\c Bitmap , 是自己被高斯模糊後的結果 + */ + Bitmap gaussian(double radiusY, double radiusX) const { + return (xyBlur(gaussianFactor1(radiusY), 1, 0). + xyBlur(gaussianFactor1(radiusX), 0, 1)); + } + + /*! + * @brief 把自己高斯模糊 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return *this + */ + Bitmap<Pixel>& gaussianed(double radiusY, double radiusX) { + copyFrom(gaussian(radiusY, radiusX)); + return *this; + } + + /*! + * @brief 回傳對x偏微分 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return 一個\c Bitmap , 是自己被偏微分後的結果 + */ + Bitmap<Pixel> gradianceX(double radiusY, double radiusX) const { + return (xyBlur(gaussianFactor1(radiusY), 1, 0). + xyBlur(gradianceFactor1(radiusX), 0, 1)); + } + + /*! + * @brief 把自己對x偏微分 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return *this + */ + Bitmap<Pixel>& gradiancedX(double radiusY, double radiusX) { + return copyFrom(gradianceX(radiusY, radiusX)); + } + + /*! + * @brief 回傳對y偏微分 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return 一個\c Bitmap , 是自己被偏微分後的結果 + */ + Bitmap<Pixel> gradianceY (double radiusY, double radiusX) const { + return (xyBlur(gaussianFactor1(radiusX), 0, 1). + xyBlur(gradianceFactor1(radiusY), 1, 0)); + } + + /*! + * @brief 把自己對y偏微分 + * + * @param [in] radiusY 高斯模糊的Y軸方向的sigma + * @param [in] radiusX 高斯模糊的X軸方向的sigma + * @return *this + */ + Bitmap<Pixel>& gradiancedY(double radiusY, double radiusX) { + return copyFrom(gradianceY(radiusY, radiusX)); + } + + /*! + * @brief same as \c copyFrom(b) + */ + Bitmap& operator=(Bitmap const& b) { + return copyFrom(b); + } + + /*! + * @brief same as \c pixel(y, x) + */ + Pixel operator()(size_t y, size_t x) const { + return pixel(y, x); + } + + /*! + * @brief same as \c pixel(y, x, p) + */ + Pixel const& operator()(size_t y, size_t x, Pixel const& p) const { + return pixel(y, x, p); + } + + /*! @brief 將資料寫入檔案 + * + * @note 未完成, 輸入參數 fg 無用 + */ + bool write(FILE* f, bool bin, unsigned int fg) const { + size_t w = width(), h = height(); + if (bin) { + if (fwrite(&h, sizeof(size_t), 1, f) < 1) return false; + if (fwrite(&w, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fprintf(f, "%lu %lu\n", h, w) < 2) return false; + } + if (fg) { + // TODO + return false; + } + return true; + //return propertyWrite(__f, __bin, __fg); + } + + /*! @brief 將資料讀入 + * + * @note 未完成, 輸入參數 fg 無用 + */ + bool read(FILE* f, bool bin, unsigned int fg) { + size_t w, h; + if (bin) { + if (fread(&h, sizeof(size_t), 1, f) < 1) return false; + if (fread(&w, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fscanf(f, "%lu %lu\n", &h, &w) < 2) return false; + } + if (fg) { + // TODO + return false; + } + else { + reset(h, w, Pixel(0)); + } + return true; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的Bitmap<Pixel> + */ + ObjBase* create() const { + return new Bitmap(); + } + + /*! @brief 複製資料 + * + * 輸入型別是 \c ObjBase \c const* + * 這裡假設實體其實是 \c Bitmap. + * 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom + * + * @param [in] b 資料來源 + * @return this + */ + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(Bitmap*)b)); + } + + /*! @brief 回傳class的type + * + * @return \c char \c const\c * 形式的typename + */ + char const* ctype() const{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + + /*! @brief 回傳class的type + * + * @return \c std::string 形式的typename + */ + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // gra_Bitmap_H__ diff --git a/meowpp/gra/Camera.h b/meowpp/gra/Camera.h new file mode 100644 index 0000000..45321de --- /dev/null +++ b/meowpp/gra/Camera.h @@ -0,0 +1,544 @@ +#ifndef gra_Camera_H__ +#define gra_Camera_H__ + +#include "Photo.h" +#include "IdentityPoints.h" +#include "../Self.h" +#include "../math/utility.h" +#include "../math/LinearTransformations.h" +#include "../math/methods.h" +#include "../oo/ObjBase.h" + +namespace meow { + +/*! + * @brief Camera + * + * 實際上就是一個 \c Photo 加上一個 \c Rotation3D. + * 另外附有 fixedPoint, 可以用來定位時參考 + * + * @author cat_leopard + */ +template<class Pixel> +class Camera: public ObjBase { +public: + typedef IdentityPoints<int, double> FixedPoints2D; +private: + struct Myself { + Photo<Pixel> photo_; + Rotation3D<double> rot_; + FixedPoints2D fixed2D_; + + Myself() { + fixed2D_.dimension(2); + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + photo_ .copyFrom(b. photo_); + rot_ .copyFrom(b. rot_); + fixed2D_.copyFrom(b.fixed2D_); + return *this; + } + }; + + Self<Myself> const self; +public: + /*! + * @brief constructor + */ + Camera(): self(true) { + } + + /*! + * @brief copy constructor + */ + Camera(Camera const& b): self(false) { + copyFrom(b); + } + + /*! + * @brief destructor + */ + ~Camera() { + } + + /*! + * @brief 複製資料 + */ + Camera& copyFrom(Camera const& b) { + self().copyFrom(b.self); + return *this; + } + + /*! + * @brief 參照 + */ + Camera& referenceFrom(Camera const& b) { + self().referenceFrom(b.self); + return *this; + } + + /*! + * @brief 取得 photo + */ + Photo<Pixel> const& photo() const { + return self->photo_; + } + + /*! + * @brief 取得 photo (non-constant) + */ + Photo<Pixel>& photoGet() { + return self()->photo_; + } + + /*! + * @brief 設定 photo + */ + Photo<Pixel> const& photo(Photo<Pixel> const& pho) { + self()->photo_.copyFrom(pho); + return photo(); + } + + /*! + * @brief 取得rotation + */ + Rotation3D<double> const& rotation() const { + return self->rot_; + } + + /*! + * @brief 取得rotation (non-constant) + */ + Rotation3D<double>& rotationGet() { + return self()->rot_; + } + + /*! + * @brief 設定rotation + */ + Rotation3D<double> const& rotation(Rotation3D<double> const& rot) { + self()->rot_ = rot; + return rotation(); + } + + /*! + * @brief 取得所有FixedPoint + */ + FixedPoints2D const& fixedPoints2D() const { + return self->fixed2D_; + } + + /*! + * @brief 取得所有FixedPoint(non-constant reference) + */ + FixedPoints2D& fixedPoints2DGet() const { + return self()->fixed2D_; + } + + /*! + * @brief 設定FixedPoint + */ + FixedPoints2D const& fixedPoints2D(FixedPoints2D const& fps2d) const { + if (fps2d.dimension() == 2) { + self()->fixed2D_ = fps2d; + } + return fixedPoints2D(); + } + + /*! + * @brief 取得編號為i的fixed points 2d + */ + Vector<double> fixedPoints2D(int i) { + return self->fixed2D_.identityPoint(i); + } + + /*! + * @brief 詢問某點是否在底片範圍內 + */ + bool inside(Vector3D<double> p) const { + return self->photo_.inside( + Vector3D<double>(self->rot_.transformate(p.matrix()))); + } + + /*! + * @brief 取得底片color + */ + Pixel color(Vector3D<double> p) const { + return self->photo_.color( + Vector3D<double>(self->rot_.transformate(p.matrix()))); + } + + /*! + * @brief same as \c copyFrom(b) + */ + Camera& operator=(Camera const& b) { + return copyFrom(b); + } + + /*! @brief 將資料寫入檔案 + * + * @note 未完成 + */ + bool write(FILE* f, bool bin, unsigned int fg) const { + return false; + } + + /*! @brief 將資料讀入 + * + * @note 未完成 + */ + bool read(FILE* f, bool bin, unsigned int fg) { + return false; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的pointer + */ + ObjBase* create() const { + return new Camera(); + } + + /*! @brief 複製資料 + * + * 輸入型別是 \c ObjBase \c const* + * 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom + * + * @param [in] b 資料來源 + * @return this + */ + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(Camera*)b)); + } + + /*! @brief 回傳class的type + * + * @return \c char \c const\c * 形式的typename + */ + char const* ctype() const{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + + /*! @brief 回傳class的type + * + * @return \c std::string 形式的typename + */ + std::string type() const { + return std::string(ctype()); + } + + //////////////////////////////////////////////////////////////////// +private: + class BoundleAdjustment2D { + private: + class Parameters { + private: + std::vector<Camera>& cam_; + std::vector<Rotation3D<double> > rot_; + std::vector<PhotoProjection<double> > pho_; + struct Pair { + size_t i1_; + size_t i2_; + Vector<double> v1_; + Vector<double> v2_; + Pair(size_t a, size_t b, + Vector<double> const& v1, Vector<double> const& v2): + i1_(a), i2_(b), v1_(v1), v2_(v2) { + } + }; + std::vector<Pair> pairs_; + + void setParameters(Vector<double> const& v) { + size_t n = 0; + for (size_t i = 0, I = cam_.size(); i < I; ++i) { + pho_[i].focal(v(n++)); + for (size_t j = 0; j < 3; j++) { + rot_[i].theta(j, v(n++)); + } + } + for (size_t i = 0, I = pairs_.size(); i < I; ++i) { + pairs_[i].v1_.entry(2, pho_[pairs_[i].i1_].focal()); + pairs_[i].v2_.entry(2, pho_[pairs_[i].i2_].focal()); + } + } + Vector<double> getParameters() const { + Vector<double> ret(cam_.size() * 4, 0.0); + for (size_t i = 0, I = cam_.size(); i < I; ++i) { + ret.entry(i * 4, pho_[i].focal()); + for (size_t j = 0; j < 3; ++j) { + ret.entry(i * 4 + 1 + j, rot_[i].theta(j)); + } + } + return ret; + } + Vector<double> residureV() const { + Vector<double> ret(pairs_.size() * 3, 0.0); + for (size_t i = 0, I = pairs_.size(); i < I; ++i) { + size_t i_from = pairs_[i].i1_; + size_t i_to = pairs_[i].i2_; + Matrix<double> v_from(pairs_[i].v1_.matrix()); + Matrix<double> v_to (pairs_[i].v2_.matrix()); + Matrix<double> v_tr( + pho_[i_to].transformate( + rot_[i_to].transformate( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + ) + ); + Matrix<double> delta(v_to - v_tr); + for (size_t j = 0; j < 3; ++j) { + ret.entry(i * 3 + j, delta(j, 0)); + } + } + return ret; + } + public: + Parameters(std::vector<Camera>& cam): cam_(cam) { + rot_.resize(cam_.size()); + pho_.resize(cam_.size(), PhotoProjection<double>(3)); + for (size_t i = 0, I = cam_.size(); i < I; ++i) { + rot_[i].referenceFrom(cam_[i].rotation()); + pho_[i].focal(cam_[i].photo().focal()); + } + for (size_t i = 0, I = cam_.size(); i < I; ++i) { + std::map<int,Vector<double> >const& p1 = ( + cam_[i].fixedPoints2D().identityPoints()); + for (size_t j = 0; j < I; ++j) { + if (i == j) continue; + std::map<int,Vector<double> >const& p2 = ( + cam_[j].fixedPoints2D().identityPoints()); + for (std::map<int,Vector<double> >::const_iterator + it1 = p1.begin(); it1 != p1.end(); ++it1) { + for (std::map<int,Vector<double> >::const_iterator + it2 = p2.begin(); it2 != p2.end(); ++it2) { + if (it1->first != it2->first) continue; + Vector<double> v1(it1->second), v2(it2->second); + v1.dimension(3, 0.0); + v2.dimension(3, 0.0); + pairs_.push_back(Pair(i, j, v1, v2)); + } + } + } + } + } + Vector<double> init() const { + return getParameters(); + } + Vector<double> residure(Vector<double> const& v) const { + ((Parameters*)this)->setParameters(v); + return residureV(); + } + Matrix<double> jacobian(Vector<double> const& v) const { + //setParameters(v); + Matrix<double> ret(pairs_.size() * 3, v.dimension(), 0.0); + for (size_t i = 0, I = pairs_.size(); i < I; ++i) { + for (size_t j = 0, J = v.dimension(); j < J; ++j) { + size_t j0 = j / 4; + size_t dj = j % 4; + size_t i_from = pairs_[i].i1_; + size_t i_to = pairs_[i].i2_; + Matrix<double> v_from(pairs_[i].v1_.matrix()); + Matrix<double> v_to (pairs_[i].v2_.matrix()); + Matrix<double> v_tr (3, 1, 0.0); + if (j0 == i_from) { + if (dj == 0) { + v_tr = ( + pho_[i_to].jacobian( + rot_[i_to].transformate( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + ) + * + rot_[i_to].jacobian( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + * + rot_[i_from].jacobianInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + * + BallProjection<double>(3, 1.0).jacobian( + v_from + ).col(2) + ); + } + else { + v_tr = ( + pho_[i_to].jacobian( + rot_[i_to].transformate( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + ) + * + rot_[i_to].jacobian( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + * + rot_[i_from].jacobianInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ); + } + } + else if (j0 == i_to) { + if (dj == 0) { + v_tr = ( + pho_[i_to].jacobian( + rot_[i_to].transformate( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + ).col(2) + ); + } + else { + v_tr = ( + pho_[i_to].jacobian( + rot_[i_to].transformate( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ) + ) + ) + * + rot_[i_to].jacobian( + rot_[i_from].transformateInv( + BallProjection<double>(3, 1.0).transformate( + v_from + ) + ), + dj - 1 + ) + ); + } + } + for (size_t k = 0; k < 3; ++k) { + ret.entry(i * 3 + k, j, -v_tr(k, 0)); + } + } + } + return ret; + } + Matrix<double> identity(Vector<double> const& v) const { + //setParameters(v); + Matrix<double> ret(v.dimension(), v.dimension(), 0.0); + ret.identity(); + return ret; + } + double averageResidure() const { + Vector<double> res(residureV()); + double sum = 0; + for (size_t i = 0, I = res.dimension(); i < I; ++i) { + sum += res(i); + } + return sum / res.dimension(); + } + size_t dimensinonI() const { + return cam_.size() * 4; + } + size_t dimensionO() const { + return pairs_.size() * 3; + } + }; + class F { + private: + Parameters& p_; + public: + F(Parameters& p): p_(p) { + } + Vector<double> operator()(Vector<double> const& v) const { + return p_.residure(v); + } + }; + class J { + private: + Parameters& p_; + public: + J(Parameters& p): p_(p) { + } + Matrix<double> operator()(Vector<double> const& v) const { + return p_.jacobian(v); + } + }; + class I { + private: + Parameters& p_; + public: + I(Parameters& p): p_(p) { + } + Matrix<double> operator()(Vector<double> const& v) const { + return p_.identity(v); + } + }; + class Stop { + private: + Parameters& p_; + double t_; + public: + Stop(Parameters& p, double t): p_(p), t_(t) { + } + bool operator()(double r) const { + return (r < p_.dimensionO() * t_); + } + }; + public: + BoundleAdjustment2D() { + } + ~BoundleAdjustment2D() { + } + double operator()(std::vector<Camera>* cs, double threshold) const { + Parameters p(*cs); + Vector<double> v0(p.init()); + levenbergMarquardt(F(p), J(p), I(p), v0, Stop(p, threshold), 100000); + return p.averageResidure(); + } + }; +public: + /*! + * @brief 將數台camera用fixed points做boundle adjustment + * + * @param [in] cs 要調整的cameras + * @param [in] threshold 允許誤差值 + * @return 誤差值 + */ + static double boundleAdjustment2D(std::vector<Camera>* cs, double threshold) { + static BoundleAdjustment2D bdl; + return bdl(cs, threshold); + } +}; + +} + +#endif // gra_Camera_H__ diff --git a/meowpp/gra/FeaturePoint.h b/meowpp/gra/FeaturePoint.h new file mode 100644 index 0000000..4b80a3d --- /dev/null +++ b/meowpp/gra/FeaturePoint.h @@ -0,0 +1,193 @@ +#ifndef gra_FeaturePoint_H__ +#define gra_FeaturePoint_H__ + +#include "../oo/ObjBase.h" + +#include "../math/Vector.h" + +#include <string> +#include <typeinfo> +#include <cstdlib> +#include <cstdio> + +namespace meow { + +/*! + * @brief 特徵點 + * + * @author cat_leopard + */ +template<class Scalar, class Description> +class FeaturePoint: public ObjBase { +private: + Vector<Scalar> pos_; + Vector<Description> des_; +public: + /*! + * @brief constructor + */ + FeaturePoint() { + } + + /*! + * @brief constructor + */ + FeaturePoint(size_t pDim, size_t dDim): + pos_(pDim, Scalar(0)), des_(dDim, Description(0)) { + } + + /*! + * @brief constructor + */ + FeaturePoint(FeaturePoint const& fp): + pos_(fp.pos_), des_(fp.des_) { + } + + /*! + * @brief destructor + */ + ~FeaturePoint() { + } + + /*! + * @brief 複製 + */ + FeaturePoint& copyFrom(FeaturePoint const& fp) { + pos_.copyFrom(fp.pos_); + des_.copyFrom(fp.des_); + return *this; + } + + /*! + * @brief 參照 + */ + FeaturePoint& referenceFrom(FeaturePoint const& fp) { + pos_.referenceFrom(fp.pos_); + des_.referenceFrom(fp.des_); + return *this; + } + + /*! + * @brief 回傳position + */ + Vector<Scalar> const& position() const { + return pos_; + } + + /*! + * @brief 回傳description + */ + Vector<Description> const& description() const { + return des_; + } + + /*! + * @brief 修改position + */ + Vector<Scalar> const& position(Vector<Scalar> const& p) const { + pos_.copyFrom(p); + return position(); + } + + /*! + * @brief 修改description + */ + Vector<Description> const& description(Vector<Description> const& d) { + des_.copyFrom(d); + return description(); + } + + /*! + * @brief 回傳position的第i個scalar + */ + Scalar position(size_t index) const { + return pos_(index); + } + + /*! + * @brief 回傳description的第i個Description + */ + Description description(size_t i) const { + return des_(i); + } + + /*! + * @brief 修改position的第i個scalar + */ + Scalar position(size_t i, Scalar const& s) { + pos_.entry(i, s); + return position(i); + } + + /*! + * @brief 修改description的第i個Description + */ + Description description(size_t i, Description const& d) { + des_.entry(i, d); + return description(i); + } + + /*! + * @brief 取得position + */ + Vector<Scalar>& positionGet() { + return pos_; + } + + /*! + * @brief 取得description + */ + Vector<Description>& descriptionGet() { + return des_; + } + + /*! + * @brief same as copyFrom(fp) + */ + FeaturePoint& operator=(FeaturePoint const& fp) { + return copyFrom(fp); + } + + /*! + * @brief same as position(i) + */ + Scalar const& operator()(size_t i) const { + return position(i); + } + + /*! + * @brief same as description(i) + */ + Description operator[](size_t i) const { + return description(i); + } + + bool write(FILE* f, bool bin, unsigned int fg) const { + return false; + } + + bool read (FILE* f, bool bin, unsigned int fg) { + return false; + } + + ObjBase* create() const { + return new FeaturePoint(); + } + + ObjBase* copyFrom(ObjBase const& b) { + return &(copyFrom(*(FeaturePoint*)b)); + } + + char const* ctype() const { + static char const* ptr = typeid(*this).name(); + return ptr; + } + + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // gra_FeaturePoint_H__ diff --git a/meowpp/gra/FeaturePointsDetector.h b/meowpp/gra/FeaturePointsDetector.h new file mode 100644 index 0000000..415419a --- /dev/null +++ b/meowpp/gra/FeaturePointsDetector.h @@ -0,0 +1,26 @@ +#ifndef gra_FeaturePointsDetector_H__ +#define gra_FeaturePointsDetector_H__ + +#include "../oo/ObjBase.h" + +#include "FeaturePoint.h" +#include "Bitmap.h" + +#include <vector> + +namespace meow { + +template<class Pixel> +class FeaturePointsDetector: public ObjBase { +protected: + FeaturePointsDetector() { } +public: + virtual ~FeaturePointsDetector() { } + + virtual std::vector<FeaturePoint<double, double> > + detect(Bitmap<Pixel> const& __bitmap) const = 0; +}; + +} + +#endif // gra_FeaturePointsDetector_H__ diff --git a/meowpp/gra/FeaturePointsDetector_Harris.h b/meowpp/gra/FeaturePointsDetector_Harris.h new file mode 100644 index 0000000..25a46b2 --- /dev/null +++ b/meowpp/gra/FeaturePointsDetector_Harris.h @@ -0,0 +1,356 @@ +#ifndef gra_FeaturePointsDetector_Harris +#define gra_FeaturePointsDetector_Harris + +#include "FeaturePointsDetector.h" + +#include "Bitmap.h" +#include "FeaturePoint.h" +#include "FeaturePointsDetector.h" + +#include "../dsa/DisjointSet.h" + +#include "../Self.h" + +#include <vector> + + +namespace meow { + +/*! + * @brief Harris corner detect + * + * @author cat_leopard + */ +template<class Pixel> +class FeaturePointsDetector_Harris: public FeaturePointsDetector<Pixel> { +# define FPD_Harris FeaturePointsDetector_Harris +private: + struct Myself { + double ratioK_; + double thresholdR_; + double sizeW_; + double noiseN_; + double lightL_; + double featureG_; + size_t boundB_; + + Myself() { + ratioK_ = 0.03; + thresholdR_ = 0.001; + sizeW_ = 2.0; + noiseN_ = 3.0; + lightL_ = 30.0; + featureG_ = 3.0; + boundB_ = 10u; + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + ratioK_ = b.ratioK_ ; + thresholdR_ = b.thresholdR_ ; + sizeW_ = b.sizeW_ ; + noiseN_ = b.noiseN_ ; + lightL_ = b.lightL_ ; + featureG_ = b.featureG_ ; + boundB_ = b.boundB_ ; + return *this; + } + }; + + Self<Myself> const self; +public: + typedef FeaturePoint<double, double> MyFeaturePoint; + typedef std::vector<MyFeaturePoint> MyFeaturePoints; + //! @brief constructor 使用預設參數 + FPD_Harris(): self(true) { + self()->ratioK_ = 0.03; + self()->thresholdR_ = 0.001; + self()->sizeW_ = 2.0; + self()->noiseN_ = 3.0; + self()->lightL_ = 30.0; + self()->featureG_ = 3.0; + self()->boundB_ = 10u; + } + + //! @brief constructor 參數複製自另一個 FeaturePointsDetector_Harris + FPD_Harris(FPD_Harris const& fps): self(false) { + self().copyFrom(fps.self); + } + + //! @brief 解構子 + ~FPD_Harris() { + } + + //! @brief 複製 + FPD_Harris& copyFrom(FPD_Harris const& fps) { + self().copyFrom(fps.self); + return *this; + } + + //! @brief 參照 + FPD_Harris& referenceFrom(FPD_Harris const& fps) { + self().referenceFrom(fps.self); + return *this; + } + + //! @brief K + double paramK() const { + return self->ratioK_; + } + + //! @brief R + double paramR() const { + return self->thresholdR_; + } + + //! @brief W + double paramW() const { + return self->sizeW_; + } + + //! @brief N + double paramN() const { + return self->noiseN_; + } + + //! @brief G + double paramG() const { + return self->featureG_; + } + + //! @brief L + double paramL() const { + return self->lightL_; + } + + //! @brief bound + size_t paramB() const { + return self->boundB_; + } + + //! @brief K + double paramK(double k) { + self()->ratioK_ = k; + return paramK(); + } + + //! @brief R + double paramR(double r) { + self()->thresholdR_ = r; + return paramR(); + } + + //! @brief W + double paramW(double w) { + self()->sizeW_ = w; + return paramW(); + } + + //! @brief N + double paramN(double n){ + self()->noiseN_ = n; + return paramN(); + } + + //! @brief L + double paramL(double l) { + self()->lightL_ = l; + return paramL(); + } + + //! @brief G + double paramG(double g) { + self()->featureG_ = g; + return paramG(); + } + + //! @brief B + size_t paramB(size_t b) { + self()->boundB_ = b; + return paramB(); + } + + /*! @brief 找出特徵點 + * + * @param [in] bmp 要抓特徵點的點陣圖 + * @return \c std::vector<FeaturePoint<double,double>> 型態的一堆特徵點 + */ + MyFeaturePoints detect(Bitmap<Pixel> const& bmp) const { + Bitmap<Pixel> input = bmp; + + Bitmap<Pixel> input_gx(input.gradianceX(0, self->noiseN_)); + Bitmap<Pixel> input_gy(input.gradianceY(self->noiseN_, 0)); + + Bitmap<double> Ixx(input.height(), input.width(), 0.0); + Bitmap<double> Iyy(input.height(), input.width(), 0.0); + Bitmap<double> Ixy(input.height(), input.width(), 0.0); + for (ssize_t y = 0, Y = input.height(); y < Y; y++) { + for (ssize_t x = 0, X = input.width(); x < X; x++) { + Pixel gx(input_gx(y, x)); + Pixel gy(input_gy(y, x)); + Ixx.pixel(y, x, gx * gx); + Iyy.pixel(y, x, gy * gy); + Ixy.pixel(y, x, gx * gy); + } + } + + Ixx.gaussianed(self->sizeW_, self->sizeW_); + Iyy.gaussianed(self->sizeW_, self->sizeW_); + Ixy.gaussianed(self->sizeW_, self->sizeW_); + + Bitmap<double> R(input.height(), input.width(), 0.0); + Bitmap<bool> good(input.height(), input.width(), false); + ssize_t b = self->boundB_; + for (ssize_t y = b, Y = -b + input.height(); y < Y; y++) { + for (ssize_t x = b, X = -b + input.width(); x < X; x++) { + double det = Ixx(y, x) * Iyy(y, x) - squ(Ixy(y, x)); + double tra = Ixx(y, x) + Iyy(y, x); + double r = det - self->ratioK_ * squ(tra); + R.pixel(y, x, r); + good.pixel(y, x, (r >= self->thresholdR_)); + } + } + + DisjointSet dsj(input.size()); + ssize_t dy[2] = {0, 1}; + ssize_t dx[2] = {1, 0}; + for (ssize_t y = b, Y = -b + input.height(); y < Y; y++) { + for (ssize_t x = b, X = -b + input.width(); x < X; x++) { + if(good.pixel((size_t)y, (size_t)x)){ + for (size_t k = 0; k < 2u; k++) { + if (good.pixel((size_t)(y + dy[k]), (size_t)(x + dx[k]))) { + dsj.merge( y * input.width() + x, + (y + dy[k]) * input.width() + (x + dx[k])); + } + } + } + } + } + + std::vector<size_t> max_i(input.size()); + for (size_t i = 0, I = input.size(); i < I; i++) { + max_i[i] = i; + } + for (size_t i = 0, I = input.size(); i < I; i++) { + size_t ri = dsj.root(i); + if (R.pixel( i / input.width(), i % input.width()) > + R.pixel(max_i[ri] / input.width(), max_i[ri] % input.width())) { + max_i[ri] = i; + } + } + + input.gaussianed(self->featureG_, self->featureG_); + + MyFeaturePoints ret; + for (ssize_t y = b, Y = -b + input.height(); y < Y; y++) { + for (ssize_t x = b, X = -b + input.width(); x < X; x++) { + if (!good.pixel((size_t)y, (size_t)x)) { + continue; + } + size_t i = y * input.width() + x; + if (max_i[dsj.root(i)] != i) { + continue; + } + ssize_t dx[4] = {1, 0, -1, 0}; + ssize_t dy[4] = {0, 1, 0, -1}; + std::vector<double> desc; + for (ssize_t d = 1; d <= (ssize_t)self->boundB_; d++) { + std::vector<double> light; + size_t max_id = 0; + size_t x0 = x - d, y0 = y - d; + for (size_t k = 0; k < 4; k++) { + for (ssize_t n = 0; + n < (ssize_t)b * 2; + n++, x0 += dx[k], y0 += dy[k]){ + Pixel diff = input.pixel(y0, x0) - input.pixel(y, x) * 0.2; + light.push_back(diff * diff * self->lightL_); + if (light[max_id] < light[-1 + light.size()]) { + max_id = -1 + (ssize_t)light.size(); + } + } + } + for (ssize_t n = 0, N = light.size(); n < N; n++) { + desc.push_back((max_id + n) % N); + desc.push_back(light[(max_id + n) % N]); + } + } + MyFeaturePoint now(2, desc.size()); + now.position(0, x); + now.position(1, y); + now.description(Vector<double>(desc)); + ret.push_back(now); + } + } + return ret; + } + + //! @brief same as \c copyFrom(fps) + FPD_Harris& operator=(FPD_Harris const& fps) { + return copyFrom(fps); + } + + //! @brief same as \c detect(bmp) + MyFeaturePoints operator()(Bitmap<Pixel> const& bmp) const { + return detect(bmp); + } + + /*! @brief 寫到檔案裡 + * + * 未完成 + */ + bool write(FILE* f, bool bin, unsigned int fg) const { + // TODO + return false; + } + + /*! @brief 將資料讀入 + * + * 未完成 + */ + bool read (FILE* f, bool bin, unsigned int fg) { + // TODO + return false; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的FeaturePointsDetector_Harris<Pixel> + */ + ObjBase* create() const { + return (ObjBase*)new FPD_Harris<Pixel>(); + } + + /*! @brief 複製資料 + * + * 輸入型別是 \c ObjBase \c const* + * 這裡假設實體其實是 \c FeaturePointsDetector_Harris. + * 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom + * + * @param [in] b 資料來源 + * @return this + */ + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(FPD_Harris const*)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()); + } +# undef FPD_Harris +}; + +} + +#endif // gra_FeaturePointsDetector_Harris diff --git a/meowpp/gra/FeaturePointsMatch.h b/meowpp/gra/FeaturePointsMatch.h new file mode 100644 index 0000000..8b05632 --- /dev/null +++ b/meowpp/gra/FeaturePointsMatch.h @@ -0,0 +1,63 @@ +#ifndef gra_FeaturePointsMatch_H__ +#define gra_FeaturePointsMatch_H__ + +#include "FeaturePoint.h" + +#include "../oo/ObjBase.h" + +#include <utility> +#include <cstdlib> + +namespace meow { + +struct FeaturePointIndexPair { + std::pair<size_t, size_t> from; + std::pair<size_t, size_t> to; + + FeaturePointIndexPair() { + } + FeaturePointIndexPair(size_t ff, size_t fs, + size_t tf, size_t ts) { + from.first = ff; + from.second = fs; + to.first = tf; + to.second = ts; + } + bool operator==(FeaturePointIndexPair const& p) const { + return (from == p.from && to == p.to); + } +}; + +typedef std::vector<FeaturePointIndexPair> FeaturePointIndexPairs; + +template<class Scalar, class Description> +class FeaturePointsMatch: public ObjBase { +protected: + FeaturePointsMatch() { + } +public: + typedef std::vector<FeaturePoint<Scalar, Description> > FeaturePoints; + typedef std::vector<FeaturePoints > FeaturePointss; + + virtual ~FeaturePointsMatch() { + } + + virtual FeaturePointIndexPairs match(size_t dimension, + FeaturePoints const& from, + FeaturePoints const& to) const = 0; + + virtual FeaturePointIndexPairs match(size_t dimension, + FeaturePoints const& from, + FeaturePointss const& to) const = 0; + + virtual FeaturePointIndexPairs match(size_t dimension, + FeaturePointss const& from, + FeaturePointss const& to) const = 0; + + virtual FeaturePointIndexPairs match(size_t dimension, + FeaturePointss const& fpss) const = 0; +}; + +} + +#endif // gra_FeaturePointsMatch_H__ diff --git a/meowpp/gra/FeaturePointsMatch_K_Match.h b/meowpp/gra/FeaturePointsMatch_K_Match.h new file mode 100644 index 0000000..ca47d27 --- /dev/null +++ b/meowpp/gra/FeaturePointsMatch_K_Match.h @@ -0,0 +1,190 @@ +#ifndef gra_FeaturePointsMatch_K_Match_H__ +#define gra_FeaturePointsMatch_K_Match_H__ + +#include "../dsa/VP_Tree.h" + +#include "FeaturePointsMatch.h" + +#include "../Self.h" + + +#include "../oo/ObjBase.h" + +#include <cstdlib> + +namespace meow { + +template<class Scalar, class Description> +class FeaturePointsMatch_K_Match: +public FeaturePointsMatch<Scalar, Description> { +# define FPMKM FeaturePointsMatch_K_Match +public: + typedef std::vector<FeaturePoint<Scalar, Description> > FeaturePoints; + typedef std::vector<FeaturePoints > FeaturePointss; +private: + struct Node { + size_t id_; + size_t index_; + FeaturePointss const* ptr_; + + Node() { + } + Node(Node const& nd) { + id_ = nd.id_; + index_ = nd.index_; + ptr_ = nd.ptr_; + } + Node(size_t id, size_t index, FeaturePointss const* ptr) { + id_ = id; + index_ = index; + ptr_ = ptr; + } + ~Node() { + } + bool operator<(Node const& nd) const { + return (id_ < nd.id_); + } + Description operator[](size_t id) const { + return (*ptr_)[id_][index_][id]; + } + }; + struct Myself { + size_t k_; + Myself() { + k_ = 1; + } + ~Myself() { + } + Myself& copyFrom(Myself const& m) { + k_ = m.k_; + return *this; + } + }; + + Self<Myself> const self; +public: + FPMKM(): self(true) { + } + + FPMKM(FPMKM const& m): self(false) { + self().copyFrom(m.self); + } + + FPMKM(size_t k): self(true) { + self()->k_ = k; + } + + ~FPMKM() { + } + + FPMKM& copyFrom(FPMKM const& m) { + self().copyFrom(m.self); + return *this; + } + + FPMKM& referenceFrom(FPMKM const& m) { + self().referenceFrom(m.self); + return *this; + } + + size_t paramK() const { + return self->k_; + } + + size_t paramK(size_t k) { + self()->k_ = std::max(k, (size_t)1); + return paramK(); + } + + + FeaturePointIndexPairs match(size_t dimension, + FeaturePoints const& from, + FeaturePoints const& to) const { + return match(dimension, FeaturePointss(1, from), FeaturePointss(1, to)); + } + + + FeaturePointIndexPairs match(size_t dimension, + FeaturePoints const& from, + FeaturePointss const& to) const { + return match(dimension, FeaturePointss(1, from), to); + } + + FeaturePointIndexPairs match(size_t dimension, + FeaturePointss const& from, + FeaturePointss const& to) const { + VP_Tree<Node, Description> tree(dimension); + for (size_t i = 0, I = to.size(); i < I; i++) { + for (size_t j = 0, J = to[i].size(); j < J; j++) { + tree.insert(Node(i, j, &to)); + } + } + FeaturePointIndexPairs ret(from.size()); + for (size_t i = 0, I = from.size(); i < I; i++) { + for (size_t j = 0, J = from[i].size(); j < J; j++) { + Node now(i, j, &from); + std::vector<Node> tree_ret = tree.query(now, self->k_, true); + for (size_t k = 0, K = tree_ret.size(); k < K; k++) { + ret.push_back(FeaturePointIndexPair(i, j, + tree_ret[k].id_, + tree_ret[k].index_)); + } + } + } + return ret; + } + + FeaturePointIndexPairs match(size_t dimension, + FeaturePointss const& fpss) const { + FeaturePointIndexPairs ret(fpss.size()), add; + FeaturePointss to(fpss); + for (size_t i = 0, I = fpss.size(); i < I; i++) { + FeaturePoints tmp(to[i]); + to[i].clear(); + add = match(dimension, fpss[i], to); + for (size_t j = 0, J = add.size(); j < J; j++) { + ret.push_back(FeaturePointIndexPair(i , add[j].from.second, + add[j].to.first, add[j].to.second)); + } + to[i] = tmp; + } + return ret; + } + + FPMKM& operator=(FPMKM const& b) { + return copyFrom(b); + } + + + bool write(FILE* f, bool bin, unsigned int fg) const { + // TODO + return false; + } + + bool read (FILE* f, bool bin, unsigned int fg) { + // TODO + return false; + } + + ObjBase* create() const { + return new FPMKM(); + } + + ObjBase* copyFrom(ObjBase const* ptr) { + return &(copyFrom(*(FPMKM*)ptr)); + } + + char const* ctype() const { + static char const* ptr = typeid(*this).name(); + return ptr; + } + + std::string type() const { + return std::string(ctype()); + } +# undef FPMKM +}; + +} + +#endif // gra_FeaturePointsMatch_K_Match_H__ diff --git a/meowpp/gra/IdentityPoints.h b/meowpp/gra/IdentityPoints.h new file mode 100644 index 0000000..c33eb55 --- /dev/null +++ b/meowpp/gra/IdentityPoints.h @@ -0,0 +1,279 @@ +#ifndef gra_IdentityPoints_H__ +#define gra_IdentityPoints_H__ + +#include "../Self.h" + +#include "../math/Vector.h" + +#include "../oo/ObjBase.h" + +#include <map> + +#include <cstdlib> + +namespace meow { + +/*! + * @brief 把一個 \c std::map<ID,Vector<Scalar> > 包起來 + * + * @author cat_leopard + */ +template<class ID, class Scalar> +class IdentityPoints: public ObjBase { +public: + typedef typename std::map<ID, Vector<Scalar> > IdentityPointsMap; + typedef typename IdentityPointsMap:: iterator IdentityPointsMapIter; + typedef typename IdentityPointsMap::const_iterator IdentityPointsMapIterK; + +private: + struct Myself { + IdentityPointsMap points_; + size_t dimension_; + + Myself() { + dimension_ = 1; + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + points_ = b.points_; + dimension_ = b.dimension_; + return *this; + } + }; + + Self<Myself> const self; +public: + /*! + * @brief constructor + */ + IdentityPoints(): self(true) { + } + + /*! + * @brief constructor, 並且複製資料 + */ + IdentityPoints(IdentityPoints const& b): self(false) { + copyFrom(b); + } + + /*! + * @brief destructor + */ + ~IdentityPoints() { + } + + /*! + * @brief 複製資料 + */ + IdentityPoints& copyFrom(IdentityPoints const& b) { + self().copyFrom(b.self); + return *this; + } + + /*! + * @brief 參照 + */ + IdentityPoints& referenceFrom(IdentityPoints const& b) { + self().referenceFrom(b.self); + return *this; + } + + /*! + * @brief 清除一切identity points + */ + void clear() { + self()->points_.clear(); + } + + /*! + * @brief 回傳有幾個identity points + */ + size_t size() const { + return self->points_.size(); + } + + /*! + * @brief 回傳是否沒有identity points + */ + bool empty() const { + return (size() == 0u); + } + + /*! + * @brief 檢查某id是否有使用 + */ + bool exist(ID const& id) const { + return (self->points_.find(id) != self->points_.end()); + } + + /*! + * @brief 回傳dimension + */ + size_t dimension() const { + return self->dimension_; + } + + /*! + * @brief 設定dimension, 並且清空資料 + */ + size_t dimension(size_t dim) { + self()->dimension_ = dim; + clear(); + return dimension(); + } + + /*! + * @brief 設定dimension, 並且針對每個identity point指定重設dimension + */ + size_t dimension(size_t dim, Scalar const& init_value) { + self()->dimension_ = dim; + for (IdentityPointsMapIter + it = self()->points_.begin(); it != self()->points_.end(); ++it) { + it.second.dimension(dim, init_value); + } + return dimension(); + } + + /*! + * @brief 取得所有identity points + */ + IdentityPointsMap const& identityPoints() const { + return self->points_; + } + + /*! + * @brief 設定所有identity points + */ + IdentityPointsMap const& identityPoints(IdentityPointsMap const& points) { + clear(); + return identityPointsAdd(points); + } + + /*! + * @brief 加入identity Points + */ + IdentityPointsMap const& identityPointsAdd(IdentityPointsMap const& points) { + for (IdentityPointsMapIterK it = points.begin(); it != points.end(); ++it) { + identityPointAdd(it.first, it.second); + } + return identityPoints(); + } + + /*! + * @brief 移除identity Points + */ + IdentityPointsMap const& identityPointsDel(std::set<ID> const& ids) { + for (typename std::set<ID>::const_iterator + it = ids.begin(); it != ids.end(); ++it) { + identityPointDel(*it); + } + return identityPoints(); + } + + /*! + * @brief 取得一個identity point + */ + Vector<Scalar> identityPoint(ID const& id) const { + return (exist(id) ? self->points_.find(id)->second : Vector<Scalar>()); + } + + /*! + * @brief 修改一個identity point + */ + Vector<Scalar> identityPoint(ID const& id, Vector<Scalar> const& b) { + if (b.dimension() == self->dimension_ && exist(id)) { + self()->points_[id].copyFrom(b); + } + return identityPoint(id); + } + + /*! + * @brief 新增一個identity point + */ + Vector<Scalar> identityPointAdd(ID const& id, Vector<Scalar> const& b) { + if (b.dimension() == self->dimension_ && !exist(id)) { + self()->points_[id].copyFrom(b); + } + return identityPoint(id); + } + + /*! + * @brief 刪除一個identity point + */ + void identityPointDel(ID const& id) { + self()->points_.erase(id); + } + + /*! + * @brief 取得一個identity point, non-constant reference + */ + Vector<Scalar>& identityPointGet(ID const& id) { + return self()->points_[id]; + } + + /*! + * @brief same as \c copyFrom(b) + */ + IdentityPoints& operator=(IdentityPoints const& b) { + return copyFrom(b); + } + + /*! @brief 將資料寫入檔案 + * + * @note 未完成 + */ + bool write(FILE* f, bool bin, unsigned int fg) const { + return false; + } + + /*! @brief 將資料讀入 + * + * @note 未完成 + */ + bool read(FILE* f, bool bin, unsigned int fg) { + return false; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的Bitmap<Pixel> + */ + ObjBase* create() const { + return new IdentityPoints(); + } + + /*! @brief 複製資料 + * + * 輸入型別是 \c ObjBase \c const* + * 這裡假設實體其實是 \c Bitmap. + * 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom + * + * @param [in] b 資料來源 + * @return this + */ + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(IdentityPoints*)b)); + } + + /*! @brief 回傳class的type + * + * @return \c char \c const\c * 形式的typename + */ + char const* ctype() const{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + + /*! @brief 回傳class的type + * + * @return \c std::string 形式的typename + */ + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // gra_IdentityPoints_H__ diff --git a/meowpp/gra/Photo.h b/meowpp/gra/Photo.h new file mode 100644 index 0000000..15768aa --- /dev/null +++ b/meowpp/gra/Photo.h @@ -0,0 +1,407 @@ +#ifndef gra_Photo_H__ +#define gra_Photo_H__ + +#include "../Self.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 Photo 加上 \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& copyFrom(Myself const& b) { + bmp_ .copyFrom(b. bmp_); + c_ .copyFrom(b. c_); + proj_.copyFrom(b.proj_); + return *this; + } + }; + + 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(true) { + self()->proj_.focal(1.0); + } + + /*! + * @brief constructor + * + * 複製資料 + * + * @param [in] b 資料來源 + */ + Photo(Photo const& b): self(false) { + copyFrom(b); + } + + /*! + * @brief constructor + * + * 直接給定圖片, 焦距用猜的 + * + * @param [in] bmp 給定的圖片 + */ + Photo(Bitmap<Pixel> const& bmp): self(true) { + reset(bmp); + } + + /*! + * @brief constructor + * + * 直接給定圖片與焦距 + * + * @param [in] bmp 給定的圖片 + * @param [in] f 給定的焦距 + */ + Photo(Bitmap<Pixel> const& bmp, double f): self(true) { + 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(true) { + 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_ = 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 取得照片中心點底片座標 + * + * @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 { + 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 { + return false; + } + + /*! @brief 將資料讀入 + * + * @note 未完成 + */ + bool read(FILE* f, bool bin, unsigned int fg) { + return false; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的Bitmap<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{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + + /*! @brief 回傳class的type + * + * @return \c std::string 形式的typename + */ + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // gra_Photo_H__ diff --git a/meowpp/gra/ViewPort.h b/meowpp/gra/ViewPort.h new file mode 100644 index 0000000..c8ac734 --- /dev/null +++ b/meowpp/gra/ViewPort.h @@ -0,0 +1,19 @@ +#ifndef gra_ViewPort_H__ +#define gra_ViewPort_H__ + +#include "../oo/ObjBase.h" + +namespace meow { + +/*! + * @brief 未完待續 + */ +class ViewPort: public ObjBase { + +}; + + +} + +#endif // gra_ViewPort_H__ + diff --git a/meowpp/gra/WatchBall.h b/meowpp/gra/WatchBall.h new file mode 100644 index 0000000..c72e2a5 --- /dev/null +++ b/meowpp/gra/WatchBall.h @@ -0,0 +1,255 @@ +#ifndef gra_WatchBall_H__ +#define gra_WatchBall_H__ + +#include "Camera.h" + +#include "../Self.h" +#include "../geo/Vectors.h" +#include "../math/LinearTransformations.h" +#include "../oo/ObjBase.h" + +#include <cmath> +#include <vector> + +namespace meow { + +/*! + * @brief \b 多個camera, 一個offset, 一個rotation + * + * @author cat_leopard + */ +template<class Pixel> +class WatchBall: public ObjBase { +public: + typedef std::vector<Camera<Pixel> > Cameras; +private: + struct Myself { + Cameras cameras_; + Vector3D<double> offset_; + + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + cameras_ = b.cameras_; + offset_ = b. offset_; + return *this; + } + }; + + Self<Myself> const self; +public: + /*! + * @brief constructor + */ + WatchBall(): self(true) { + } + + /*! + * @brief copy constructor + */ + WatchBall(WatchBall const& b): self(false) { + copyFrom(b); + } + + /*! + * @brief destructor + */ + ~WatchBall() { + } + + /*! + * @brief copy data + */ + WatchBall& copyFrom(WatchBall const& b) { + self().copyFrom(b.self); + return *this; + } + + /*! + * @brief reference + */ + WatchBall& referenceFrom(WatchBall const& b) { + self().referenceFrom(b.self); + return *this; + } + + /*! + * @brief 取得有幾個camera + */ + size_t cameraSize() const { + return self->cameras_.size(); + } + + /*! + * @brief 取得 cameras + */ + Cameras const& cameras() const { + return self->cameras_; + } + + /*! + * @brief 取得 cameras (non-constant) + */ + Cameras& camerasGet() { + return self()->cameras_; + } + + /*! + * @brief 設定 camera + */ + Cameras const& cameras(Cameras const& c) { + self()->cameras_ = c; + return cameras(); + } + + /*! + * @brief 取得第i個camera + */ + Camera<Pixel> const& camera(size_t i) const { + return cameras()[i]; + } + + /*! + * @brief 取得第i個camera (non-constant reference) + */ + Camera<Pixel>& camera(size_t i) { + return cameras()[i]; + } + + /*! + * @brief 設定第i個camera + */ + Camera<Pixel> const& camera(size_t i, Camera<Pixel> const& c) { + cameras()[i] = c; + return camera(i); + } + + /*! + * @brief 取得offset + */ + Vector3D<double> const& offset() const { + return self->offset_; + } + + /*! + * @brief 取得offset (non-constant reference) + */ + Vector3D<double>& offset() { + return self()->offset_; + } + + /*! + * @brief 設定offset + */ + Vector3D<double> const& offset(Vector3D<double> const& ofs) { + self()->offset_ = ofs; + return offset(); + } + + /*! + * @brief 取得底片color + */ + Pixel color(Vector3D<double> p) const { + Vector3D<double> p2(p - offset()); + Pixel sum(0); + double ct = 0; + for (size_t i = 0, I = cameraSize(); i < I; ++i) { + if (camera(i).inside(p2)) { + sum = sum + camera(i).color(p2); + ++ct; + } + } + return (ct > 0 ? sum / ct : sum); + } + + /*! + * @brief 輸出展開圖 + * + * @param [in] radius 半徑 + */ + Bitmap<Pixel> expand(double radius) const { + radius = std::max(radius, 0.5); + size_t height = std::max<size_t>(1, 2.0 * radius); + size_t width = 2.0* PI * radius; + Bitmap<Pixel> ret(height, width, Pixel(0)); + for (size_t i = 0; i < height; ++i) { + for (size_t j = 0; j < width; ++j) { + double theta = (1.0 * j / width - 0.5) * 2 * PI; + double phi = asin(-(1.0 * i / height - 0.5) * 2.0); + ret.pixel(i, j, color(Vector3D<double>( + sin(-theta) * cos(phi), + sin(phi), + cos(-theta) * cos(phi) + ))); + } + } + return ret; + } + + /*! + * @brief same as \c copyFrom(b) + */ + WatchBall& operator=(WatchBall const& b) { + return copyFrom(b); + } + + /*! @brief 將資料寫入檔案 + * + * @note 未完成 + */ + bool write(FILE* f, bool bin, unsigned int fg) const { + return false; + } + + /*! @brief 將資料讀入 + * + * @note 未完成 + */ + bool read(FILE* f, bool bin, unsigned int fg) { + return false; + } + + /*! @brief new一個自己 + * + * @return 一個new出來的pointer + */ + ObjBase* create() const { + return new WatchBall(); + } + + /*! + * @brief 複製資料 + * + * 輸入型別是 \c ObjBase \c const* + * 事實上這個method就只是幫忙轉型然後呼叫原本的\c copyFrom + * + * @param [in] b 資料來源 + * @return this + */ + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(WatchBall*)b)); + } + + /*! @brief 回傳class的type + * + * @return \c char \c const\c * 形式的typename + */ + char const* ctype() const{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + + /*! @brief 回傳class的type + * + * @return \c std::string 形式的typename + */ + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // gra_WatchBall_H__ diff --git a/meowpp/math/!readme.asciidoc b/meowpp/math/!readme.asciidoc new file mode 100644 index 0000000..5ae6b89 --- /dev/null +++ b/meowpp/math/!readme.asciidoc @@ -0,0 +1,73 @@ + + +===== utility.h + +數學相關的小 function 雜七雜八的不知道歸類何處 + +.Functions +* noEPS() +* normalize() +* denormalize() +* ratioMapping() +* inRange() +* squ() +* cub() +* average() +* average() +* tAbs() + +.Constants +* PI + +===== Matrix.h + +.Classes +* `meow::Matrix<Entry>` + +===== Vector.h + +實作上將 *Matrix* 重新包裝 + +.Classes +* `meow::Vector<Scalar>` + +===== Transformation.h + +各種轉換的 Base Class, 這裡所謂的 *Transformation* 形式上不一定要是 Linear, +但原則上都是 *input a vector, output a vector* 其中input/output的dimension可以 +不同. + +.Classes +* `meow::Transformation<Scalar>` + +===== Transformations.h + +包含各種 *Non-Linear* transformation + +.Classes +* `meow::BallProjection<Scalar>` +* `meow::PhotoProjection<Scalar>` + +===== LinearTransformation.h + +各種 LinearTransformation 的Base Class, 繼承自 `meow::Transformation` + +.Classes +* `meow::LinearTransformation<Scalar>` + +===== LinearTransformations.h + +各種 *Linear* Transformation + +.Classes +* `meow::Rotation3D<Scalar>` + +===== methods.h + +一些數學方法 + +.Functions +* ransac() +* levenbergMarquardt() + + diff --git a/meowpp/math/LinearTransformation.h b/meowpp/math/LinearTransformation.h index f856f08..86724b7 100644 --- a/meowpp/math/LinearTransformation.h +++ b/meowpp/math/LinearTransformation.h @@ -6,22 +6,106 @@ #include <cstdlib> -namespace meow{ - template<class Scalar> - class LinearTransformation: public Transformation<Scalar>{ - protected: - Matrix<Scalar> _matrix; - - LinearTransformation(size_t __inputRows, size_t __outputRows, - size_t __psize): - Transformation(__inputRows, 1u, __outputRows, 1u, __psize), - _matrix(__outputRows, __inputCols, Scalar(0.0)){ - } - public: - virtual LinearTransformation(){ } - virtual Matrix<Scalar> const& matrix() const{ return _matrix; } - virtual Matrix<Scalar> invMatrix() const = 0; - }; +namespace meow { + +/*! + * @brief A base class for implementing kinds of linear transformations. + * + * Because all linear transformations belong to transformations, + * this class inherit to Transformation. + * + * @author cat_leopard + */ +template<class Scalar> +class LinearTransformation: public Transformation<Scalar> { +private: + Matrix<Scalar> matrix_; +protected: + /*! + * Constructor with input/output size gived + */ + LinearTransformation(size_t inputRows, size_t outputRows, size_t psize): + Transformation<Scalar>(inputRows, 1u, outputRows, 1u, psize), + matrix_(outputRows, inputRows, Scalar(0.0)) { + } + + /*! + * Constructor with input/output size gived and a inital matrix + */ + LinearTransformation(size_t inputRows, size_t outputRows, size_t psize, + Matrix<Scalar> const& m): + Transformation<Scalar>(inputRows, 1u, outputRows, 1u, psize), + matrix_(m) { + } + + /*! + * Constructor with another LinearTransformation + * + * @param [in] b another LinearTransformation + */ + LinearTransformation(LinearTransformation const& b): + Transformation<Scalar>(b), + matrix_(b.matrix_) { + } + + /*! + * @brief Copy settings, matrix from another LinearTransformation + * + * @param [in] b another LinearTransformation + */ + LinearTransformation& copyFrom(LinearTransformation const& b) { + Transformation<Scalar>::copyFrom(b); + matrix_.copyFrom(b.matrix_); + return *this; + } + + /*! + * @brief Reference settings, matrix from another LinearTransformation + * + * @param [in] b another LinearTransformation + */ + LinearTransformation& referenceFrom(LinearTransformation const& b) { + Transformation<Scalar>::referenceFrom(b); + matrix_.referenceFrom(b.matrix_); + return *this; + } + + /*! + * @brief Setup the matrix + * + * @param [in] m matrix + * @return new matrix + */ + Matrix<Scalar> const& matrix(Matrix<Scalar> const& m) { + matrix_.copyFrom(m); + return matrix_; + } +public: + /*! + * Destructor + */ + virtual ~LinearTransformation() { + } + + /*! + * @brief Return the matrix form of this transformation + * + * @return A matrix + */ + virtual Matrix<Scalar> const& matrix() const { + return matrix_; + } + + /*! + * @brief Return the inverse of the matrix form of this transformate + * + * @return A matrix (may be invalid) + */ + virtual Matrix<Scalar> matrixInv() const { + return matrix_.inverse(); + } +}; + } #endif // math_LinearTransformation_H__ diff --git a/meowpp/math/LinearTransformations.h b/meowpp/math/LinearTransformations.h index 4878834..c7295ab 100644 --- a/meowpp/math/LinearTransformations.h +++ b/meowpp/math/LinearTransformations.h @@ -2,37 +2,407 @@ #define math_LinearTransformations_H__ #include "LinearTransformation.h" +#include "Matrix.h" +#include "utility.h" +#include "../Self.h" +#include "../geo/Vectors.h" #include <cstdlib> -namespace meow{ - template<class Scalar> - class Rotation3D: public LinearTransformation<Scalar>{ - private: - Scalar _theta[3]; - void calcMatrix(); - public: - Rotation3D(); - - void axisTheta(Matrix<Scalar> const& __axis, Scalar const& __theta); - - Scalar parameter(size_t __i) const; - Scalar parameter(size_t __i, Scalar const& __s); - - Matrix<Scalar> transformate(Matrix<Scalar> const& __x) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x, - size_t __i) const; - - Matrix<Scalar> invTransformate(Matrix<Scalar> const& __x) const; - Matrix<Scalar> invJacobian (Matrix<Scalar> const& __x) const; - Matrix<Scalar> invJacobian (Matrix<Scalar> const& __x, - size_t __i) const; - - Matrix<Scalar> invMatrix() const; +namespace meow { + +/*! + * @brief Rotation a point/vector alone an axis with given angle in 3D world. + * + * @author cat_leopard + */ +template<class Scalar> +class Rotation3D: public LinearTransformation<Scalar> { +private: + struct Myself { + Vector3D<Scalar> theta_; + bool need_; + + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + theta_ = b.theta_; + need_ = b.need_; + return *this; + } }; -} + + Self<Myself> const& self; + + void calcMatrix() const { + if (self->need_) { + Vector3D<double> axis (self->theta_.normalize()); + double angle(self->theta_.length()); + double cs(cos(angle / 2.0)); + double sn(sin(angle / 2.0)); + + Matrix<Scalar> tmp(3, 3, Scalar(0.0)); + tmp.entry(0, 0, 2*(squ(axis.x())-1.0)*squ(sn) + 1); + tmp.entry(1, 1, 2*(squ(axis.y())-1.0)*squ(sn) + 1); + tmp.entry(2, 2, 2*(squ(axis.z())-1.0)*squ(sn) + 1); + tmp.entry(0, 1, 2*axis.x()*axis.y()*squ(sn) - 2*axis.z()*cs*sn); + tmp.entry(1, 0, 2*axis.y()*axis.x()*squ(sn) + 2*axis.z()*cs*sn); + tmp.entry(0, 2, 2*axis.x()*axis.z()*squ(sn) + 2*axis.y()*cs*sn); + tmp.entry(2, 0, 2*axis.z()*axis.x()*squ(sn) - 2*axis.y()*cs*sn); + tmp.entry(1, 2, 2*axis.y()*axis.z()*squ(sn) - 2*axis.x()*cs*sn); + tmp.entry(2, 1, 2*axis.z()*axis.y()*squ(sn) + 2*axis.x()*cs*sn); + ((Rotation3D*)this)->matrix(tmp); + self()->need_ = false; + } + } + +public: + /*! + * Constructor with no rotation + */ + Rotation3D(): LinearTransformation<Scalar>(3u, 3u, 3u), + self(true) { + self()->theta_.x(Scalar(0)); + self()->theta_.y(Scalar(0)); + self()->theta_.z(Scalar(0)); + self()->need_ = true; + calcMatrix(); + } + + /*! + * Constructor and copy data + */ + Rotation3D(Rotation3D const& b): LinearTransformation<Scalar>(b), + self(false) { + copyFrom(b); + } + + /*! + * Destructor + */ + ~Rotation3D() { + } + + /*! + * @brief Copy data + * + * @param [in] b another Rotation3D class. + * @return \c *this + */ + Rotation3D& copyFrom(Rotation3D const& b) { + LinearTransformation<Scalar>::copyFrom(b); + self().copyFrom(b.self); + return *this; + } + + /*! + * @brief Reference data + * + * @param [in] b another Rotation3D class. + * @return \c *this + */ + Rotation3D& referenceFrom(Rotation3D const& b) { + LinearTransformation<Scalar>::referenceFrom(b); + self().referenceFrom(b.self); + return *this; + } + + /*! + * @brief same as \c theta(i) + */ + Scalar parameter(size_t i) const { + return theta(i); + } + + /*! + * @brief same as \c theta(i, s) + */ + Scalar parameter(size_t i, Scalar const& s) { + return theta(i, s); + } + + /*! + * @brief Get the \c i -th theta + * + * \c i can only be 1, 2 or 3 + * + * @param [in] i index + * @return \c i -th theta + */ + Scalar const& theta(size_t i) const { + return self->theta_(i); + } + + /*! + * @brief Set the \c i -th theta + * + * \c i can only be 1, 2 or 3 + * + * @param [in] i index + * @param [in] s new theta value + * @return \c i -th theta + */ + Scalar const& theta(size_t i, Scalar const& s) { + if (theta(i) != s) { + if (i == 0) self()->theta_.x(s); + else if (i == 1) self()->theta_.y(s); + else if (i == 2) self()->theta_.z(s); + self()->need_ = true; + } + return theta(i); + } -#include "LinearTransformations.hpp" + /*! + * @brief Setting + * + * @param [in] axis axis + * @param [in] angle angle + */ + void axisAngle(Vector<Scalar> const& axis, Scalar const& angle) { + Vector<Scalar> n(axis.normalize()); + for (size_t i = 0; i < 3; i++) { + theta(i, n(i) * angle); + } + } + + /*! + * @brief Concat another rotation transformation + * @param [in] r another rotation transformation + */ + Rotation3D& add(Rotation3D const& r) { + for (size_t i = 0; i < 3; i++) { + theta(i, r.theta(i)); + } + return *this; + } + + /*! + * @brief Do the transformate + + * Assume: + * - The input vector is \f$ (x ,y ,z ) \f$ + * - The output vector is \f$ (x',y',z') \f$ + * - The parameters theta is\f$ \vec{\theta}=(\theta_x,\theta_y,\theta_z) \f$ + * . + * Then we have: + * \f[ + * \left[ \begin{array}{c} x' \\ y' \\ z' \\ \end{array} \right] + * = + * \left[ \begin{array}{ccc} + * 2(n_x^2 - 1) \sin^2\phi + 1 & + * 2n_x n_y \sin^2\phi - 2n_z\cos \phi\sin \phi & + * 2n_x n_z \sin^2\phi + 2n_y\cos \phi\sin \phi \\ + * 2n_y n_x \sin^2\phi + 2n_z\cos \phi\sin \phi & + * 2(n_y^2 - 1) \sin^2\phi + 1 & + * 2n_y n_z \sin^2\phi - 2n_x\cos \phi\sin \phi \\ + * 2n_z n_x \sin^2\phi - 2n_y\cos \phi\sin \phi & + * 2n_z n_y \sin^2\phi + 2n_x\cos \phi\sin \phi & + * 2(n_z^2 - 1) \sin^2\phi + 1 \\ + * \end{array} \right] + * \left[ \begin{array}{c} x \\ y \\ z \\ \end{array} \right] + * \f] + * Where: + * - \f$ \phi \f$ is the helf of length of \f$ \vec{\theta} \f$ , + * which means \f$ \phi = \frac{\left|\vec{\theta}\right|}{2} + * = \frac{1}{2}\sqrt{\theta_x^2 + \theta_y^2 + \theta_z^2} \f$ + * - \f$ \vec{n} \f$ is the normalized form of \f$ \vec{\theta} \f$ , + * which means \f$ \vec{n} = (n_x,n_y,n_z) = \vec{\theta} / 2\phi \f$ + * + * @param [in] x the input vector + * @return the output matrix + */ + Matrix<Scalar> transformate(Matrix<Scalar> const& x) const { + if (self->need_) calcMatrix(); + return LinearTransformation<Scalar>::matrix() * x; + } + + /*! + * @brief Return the jacobian matrix (derivate by the input vector) + * of this transformate + * + * The matrix we return is: + * \f[ + * \left[ \begin{array}{ccc} + * 2(n_x^2 - 1) \sin^2\phi + 1 & + * 2n_x n_y \sin^2\phi - 2n_z\cos \phi\sin \phi & + * 2n_x n_z \sin^2\phi + 2n_y\cos \phi\sin \phi \\ + * 2n_y n_x \sin^2\phi + 2n_z\cos \phi\sin \phi & + * 2(n_y^2 - 1) \sin^2\phi + 1 & + * 2n_y n_z \sin^2\phi - 2n_x\cos \phi\sin \phi \\ + * 2n_z n_x \sin^2\phi - 2n_y\cos \phi\sin \phi & + * 2n_z n_y \sin^2\phi + 2n_x\cos \phi\sin \phi & + * 2(n_z^2 - 1) \sin^2\phi + 1 \\ + * \end{array} \right] + * \f] + * Where the definition of \f$ \vec{n} \f$ and \f$ \phi \f$ + * is the same as the definition in the description of + * the method \b transformate() . + * + * @param [in] x the input vector (in this case it is a useless parameter) + * @return a matrix + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x) const { + if (self->need_) calcMatrix(); + return LinearTransformation<Scalar>::matrix(); + } + + /*! + * @brief Return the jacobian matrix of this transformate + * + * Here we need to discussion in three case: + * - \a i = 0, derivate by the x axis of the vector theta + * \f[ + * \left[ \begin{array}{ccc} + * 0 & 0 & 0 \\ + * 0 & 0 & -1 \\ + * 0 & 1 & 0 \\ + * \end{array} \right] + * \left[ \begin{array}{ccc} + * 2(n_x^2 - 1) \sin^2\phi + 1 & + * 2n_x n_y \sin^2\phi - 2n_z\cos \phi\sin \phi & + * 2n_x n_z \sin^2\phi + 2n_y\cos \phi\sin \phi \\ + * 2n_y n_x \sin^2\phi + 2n_z\cos \phi\sin \phi & + * 2(n_y^2 - 1) \sin^2\phi + 1 & + * 2n_y n_z \sin^2\phi - 2n_x\cos \phi\sin \phi \\ + * 2n_z n_x \sin^2\phi - 2n_y\cos \phi\sin \phi & + * 2n_z n_y \sin^2\phi + 2n_x\cos \phi\sin \phi & + * 2(n_z^2 - 1) \sin^2\phi + 1 \\ + * \end{array} \right] + * \left[ \begin{array}{c} x \\ y \\ z \\ \end{array} \right] + * \f] + * - \a i = 1, derivate by the y axis of the vector theta + * \f[ + * \left[ \begin{array}{ccc} + * 0 & 0 & 1 \\ + * 0 & 0 & 0 \\ + * -1 & 0 & 0 \\ + * \end{array} \right] + * \left[ \begin{array}{ccc} + * 2(n_x^2 - 1) \sin^2\phi + 1 & + * 2n_x n_y \sin^2\phi - 2n_z\cos \phi\sin \phi & + * 2n_x n_z \sin^2\phi + 2n_y\cos \phi\sin \phi \\ + * 2n_y n_x \sin^2\phi + 2n_z\cos \phi\sin \phi & + * 2(n_y^2 - 1) \sin^2\phi + 1 & + * 2n_y n_z \sin^2\phi - 2n_x\cos \phi\sin \phi \\ + * 2n_z n_x \sin^2\phi - 2n_y\cos \phi\sin \phi & + * 2n_z n_y \sin^2\phi + 2n_x\cos \phi\sin \phi & + * 2(n_z^2 - 1) \sin^2\phi + 1 \\ + * \end{array} \right] + * \left[ \begin{array}{c} x \\ y \\ z \\ \end{array} \right] + * \f] + * - \a i = 2, derivate by the z axis of the vector theta + * \f[ + * \left[ \begin{array}{ccc} + * 0 & -1 & 0 \\ + * 1 & 0 & 0 \\ + * 0 & 0 & 0 \\ + * \end{array} \right] + * \left[ \begin{array}{ccc} + * 2(n_x^2 - 1) \sin^2\phi + 1 & + * 2n_x n_y \sin^2\phi - 2n_z\cos \phi\sin \phi & + * 2n_x n_z \sin^2\phi + 2n_y\cos \phi\sin \phi \\ + * 2n_y n_x \sin^2\phi + 2n_z\cos \phi\sin \phi & + * 2(n_y^2 - 1) \sin^2\phi + 1 & + * 2n_y n_z \sin^2\phi - 2n_x\cos \phi\sin \phi \\ + * 2n_z n_x \sin^2\phi - 2n_y\cos \phi\sin \phi & + * 2n_z n_y \sin^2\phi + 2n_x\cos \phi\sin \phi & + * 2(n_z^2 - 1) \sin^2\phi + 1 \\ + * \end{array} \right] + * \left[ \begin{array}{c} x \\ y \\ z \\ \end{array} \right] + * \f] + * . + * Where \f$ (x,y,z) \f$ is the input vector, \f$ \vec{n}, \phi \f$ is the + * same one in the description of \b transformate(). + * + * @param [in] x the input vector + * @param [in] i the index of the parameters(theta) to dervite + * @return a matrix + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x, size_t i) const { + if (self->need_) calcMatrix(); + Matrix<Scalar> mid(3u, 3u, Scalar(0.0)); + if (i == 0) { + mid.entry(1, 2, Scalar(-1.0)); + mid.entry(2, 1, Scalar( 1.0)); + } + else if(i == 1) { + mid.entry(0, 2, Scalar( 1.0)); + mid.entry(2, 0, Scalar(-1.0)); + } + else { + mid.entry(0, 1, Scalar(-1.0)); + mid.entry(1, 0, Scalar( 1.0)); + } + return mid * LinearTransformation<Scalar>::matrix() * x; + } + + /*! + * @brief Do the inverse transformate + * + * @param [in] x the input vector + * @return the output vector + */ + Matrix<Scalar> transformateInv(Matrix<Scalar> const& x) const { + if (self->need_) calcMatrix(); + return LinearTransformation<Scalar>::matrix().transpose() * x; + } + + /*! + * @brief Return the jacobian matrix of the inverse form of this transformate + * + * @param [in] x the input vector + * @return a matrix + */ + Matrix<Scalar> jacobianInv(Matrix<Scalar> const& x) const { + if (self->need_) calcMatrix(); + return LinearTransformation<Scalar>::matrix().transpose(); + } + + /*! + * @brief Return the jacobian matrix of the inverse form of this transformate + * + * @param [in] x the input vector + * @param [in] i the index of the parameters(theta) to dervite + * @return a matrix + */ + Matrix<Scalar> jacobianInv(Matrix<Scalar> const& x, size_t i) const { + if (self->need_) calcMatrix(); + Matrix<Scalar> mid(3u, 3u, Scalar(0.0)); + if (i == 0) { + mid.entry(1, 2, Scalar(-1.0)); + mid.entry(2, 1, Scalar( 1.0)); + } + else if(i == 1) { + mid.entry(0, 2, Scalar( 1.0)); + mid.entry(2, 0, Scalar(-1.0)); + } + else { + mid.entry(0, 1, Scalar(-1.0)); + mid.entry(1, 0, Scalar( 1.0)); + } + return mid.transpose() * matrixInv() * x; + } + + /*! + * @brief Return the inverse matrix + * + * In this case, the inverse matrix is equal to the transpose of the matrix + * + * @return a matrix + */ + Matrix<Scalar> matrixInv() const { + if (self->need_) calcMatrix(); + return LinearTransformation<Scalar>::matrix().transpose(); + } + + //! @brief same as \c copyFrom(b) + Rotation3D& operator=(Rotation3D const& b) { + return copyFrom(b); + } +}; + +} #endif // math_LinearTransformations_H__ diff --git a/meowpp/math/Matrix.h b/meowpp/math/Matrix.h index 0a52d8c..b4b4853 100644 --- a/meowpp/math/Matrix.h +++ b/meowpp/math/Matrix.h @@ -1,74 +1,484 @@ #ifndef math_Matrix_H__ #define math_Matrix_H__ +#include "utility.h" -#include <cstdlib> +#include "../Self.h" #include <vector> +#include <algorithm> + +#include <cstdlib> -namespace meow{ - template<class Entry> - class Matrix{ - private: - size_t _rows; - size_t _cols; - std::vector<Entry> _entries; - // - size_t index(size_t __r, size_t __c) const; - public: - Matrix(); - Matrix(Matrix const& __m); - Matrix(size_t __rows, size_t __cols, Entry const& __entry); - ~Matrix(); - - Matrix& copy(Matrix const& __m); - - bool valid() const; - - size_t rows() const; - size_t cols() const; - size_t size(size_t __rows, size_t __cols); - - Entry const& entry(size_t __i, size_t __j) const; - Entry const& entry(size_t __i, size_t __j, Entry const& __entry); - void entries(size_t __rFirst, size_t __rLast, - size_t __cFirst, size_t __cLast, - Entry const& __entry); - - Matrix subMatrix(size_t __rFirst, size_t __rLast, - size_t __cFirst, size_t __cLast) const; - Matrix row(size_t __row) const; - Matrix col(size_t __col) const; - - Matrix positive() const; - Matrix negative() const; - - Matrix add(Matrix const& __m) const; - Matrix sub(Matrix const& __m) const; - Matrix mul(Matrix const& __m) const; - - Matrix mul(Entry const& __s) const; - Matrix div(Entry const& __s) const; - - Matrix identity () const; - Matrix& identitied(); - - Matrix transpose () const; - Matrix& transposed(); - - Matrix& operator=(Matrix const& __m); - Entry const& operator()(size_t __i, size_t __j) const; - Entry & operator()(size_t __i, size_t __j); - Matrix operator+() const; - Matrix operator-() const; - Matrix operator+(Matrix const& __m) const; - Matrix operator-(Matrix const& __m) const; - Matrix operator*(Matrix const& __m) const; - Matrix operator*(Entry const& __s) const; - Matrix operator/(Entry const& __s) const; +namespace meow { +/*! + * @brief \b matrix + * + * @author cat_leopard + */ +template<class Entry> +class Matrix { +private: + struct Myself { + size_t rows_; + size_t cols_; + std::vector<Entry> entries_; + Myself(): rows_(0), cols_(0), entries_(0) { + } + ~Myself() { + } + size_t index(size_t r, size_t c) const { + return r * cols_ + c; + } + Myself& copyFrom(Myself const& m) { + rows_ = m. rows_; + cols_ = m. cols_; + entries_ = m.entries_; + return *this; + } }; -} + Self<Myself> const self; +public: + /*! + * @brief constructor + * + * Create an empty matrix with size \b 0x0. + * In other world, create an \b invalid matrix + */ + Matrix(): self(true) { } + + /*! + * @brief constructor + * + * Copy data from another one + * + * @param [in] m another matrix + */ + Matrix(Matrix const& m): self(false) { self().copyFrom(m.self); } + + /*! + * @brief constructor + * + * Create an \a r x \a c matrix with all entry be \a e + * + * @param [in] r number of rows + * @param [in] c number of columns + * @param [in] e inital entry + */ + Matrix(size_t r, size_t c, Entry const& e): self(true) { reset(r, c, e); } + + //! @brief destructor + ~Matrix() { } + + /*! + * @brief copy + * + * Copy data from another matrix + * + * @param [in] m matrix + * @return *this + */ + Matrix& copyFrom(Matrix const& m) { + self().copyFrom(m.self); + return *this; + } + + /*! + * @brief reference + * + * Reference itself to another matrix + * + * @param [in] m matrix + * @return *this + */ + Matrix& referenceFrom(Matrix const& m) { + self().referenceFrom(m.self); + return *this; + } + + //! @brief reset the size of the matrix to \a r x \a c with entry all be \a e + void reset(size_t r, size_t c, Entry const& e) { + self()->rows_ = r; + self()->cols_ = c; + self()->entries_.clear(); + self()->entries_.resize(r * c, e); + } + + //! @brief Return whether it is a \b valid matrix + bool valid() const { + return (rows() > 0 && cols() > 0); + } + + //! @brief Return number of rows + size_t rows() const { + return self->rows_; + } + + //! @brief Return number of cols + size_t cols() const { + return self->cols_; + } + + //! @brief Return number of rows times number of cols + size_t size() const { + return rows() * cols(); + } + + /*! + * @brief resize the matrix such that number of rows become \a r. + * + * New created entry will be \a e + * + * @param [in] r new number of rows + * @param [in] e inital entry + * @return new number of rows + */ + size_t rows(size_t r, Entry const& e) { + if (r != rows()) { + self()->entries_.resize(r * cols(), e); + self()->rows_ = r; + } + return rows(); + } + + /*! + * @brief resize the matrix such that number of cols become \a c + * + * New created entry will be \a e + * + * @param [in] c new number of columns + * @param [in] e inital entry + * @return new number of columns + */ + size_t cols(size_t c, Entry const& e) { + if (c != cols()) { + Self<Myself> const old(false); + old().copyFrom(self); + self()->entries_.resize(rows() * c); + self()->cols_ = c; + for (size_t i = 0, I = rows(); i < I; i++) { + size_t j, J1 = std::min(old->cols_, cols()), J2 = cols(); + for (j = 0; j < J1; j++) + self()->entries_[self->index(i, j)] = old->entries_[old->index(i, j)]; + for (j = J1; j < J2; j++) + self()->entries_[self->index(i, j)] = e; + } + } + return cols(); + } + + /*! + * @brief resize + * + * Resize to \a r x \a c, with new created entry be \a e + * + * @param [in] r number of rows + * @param [in] c number of rows + * @param [in] e inital entry + * @return \a r * \a c + */ + size_t size(size_t r, size_t c, Entry const& e) { + cols(c, e); + rows(r, e); + return rows() * cols(); + } + + //! @brief Access the entry at \a r x \a c + Entry entry(size_t r, size_t c) const { + return self->entries_[self->index(r, c)]; + } + + //! @brief Change the entry at \a r x \a c + Entry entry(size_t r, size_t c, Entry const& e) { + self()->entries_[self->index(r, c)] = e; + return entry(r, c); + } -#include "Matrix.hpp" + /*! + * @brief Change the entries from \a rFirst x \a cFirst to \a rLast x \a cLast + * + * @param [in] rFirst + * @param [in] rLast + * @param [in] cFirst + * @param [in] cLast + * @param [in] e value + * @return void + */ + void entries(ssize_t rFirst, ssize_t rLast, + ssize_t cFirst, ssize_t cLast, + Entry const& e) { + for (ssize_t r = rFirst; r <= rLast; r++) { + for (ssize_t c = cFirst; c <=cFirst; c++) { + entry(r, c, e); + } + } + } + + /*! + * @brief Return a \a rLast-rFirst+1 x \a cLast-cFirst+1 matrix + * + * With value be the entries from \a rFirst x \a cFirst to \a rLast x \a cLast + * + * @param [in] rFirst + * @param [in] rLast + * @param [in] cFirst + * @param [in] cLast + * @return a matrix + */ + Matrix subMatrix(size_t rFirst, size_t rLast, + size_t cFirst, size_t cLast) const { + if (rFirst > rLast || cFirst > cLast) return Matrix(); + if (rFirst == 0 || cFirst == 0) { + Matrix ret(*this); + ret.size(rLast + 1, cLast + 1, Entry(0)); + return ret; + } + Matrix ret(rLast - rFirst + 1, cLast - cFirst + 1, entry(rFirst, cFirst)); + for (size_t r = rFirst; r <= rLast; r++) + for (size_t c = cFirst; c <= cLast; c++) + ret.entry(r - rFirst, c - cFirst, entry(r, c)); + return ret; + } + + //! @brief Return the \a r -th row + Matrix row(size_t r) const { + return subMatrix(r, r, 0, cols() - 1); + } + + //! @brief Return the \a c -th column + Matrix col(size_t c) const { + return subMatrix(0, rows() - 1, c, c); + } + + //! @brief return +\a (*this) + Matrix positive() const { + return *this; + } + + //! @brief return -\a (*this) + Matrix negative() const { + Matrix ret(*this); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + ret.entry(r, c, -ret.entry(r, c)); + return ret; + } + + /*! @brief return \a (*this) + \a m. + * + * If the size not match, it will return an invalid matrix + */ + Matrix add(Matrix const& m) const { + if (rows() != m.rows() || cols() != m.cols()) return Matrix(); + Matrix ret(*this); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + ret.entry(r, c, ret.entry(r, c) + m.entry(r, c)); + return ret; + } + + /*! @brief return \a (*this) - \a m. + * + * If the size not match, it will return an invalid matrix + */ + Matrix sub(Matrix const& m) const { + if (rows() != m.rows() || cols() != m.cols()) return Matrix(); + Matrix ret(*this); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + ret.entry(r, c, ret.entry(r, c) - m.entry(r, c)); + return ret; + } + + /*! @brief return \a (*this) times \a m. + * + * If the size not match, it will return an invalid matrix + */ + Matrix mul(Matrix const& m) const { + if (cols() != m.rows()) return Matrix(); + Matrix ret(rows(), m.cols(), Entry(0)); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = m.cols(); c < C; c++) + for (size_t k = 0, K = cols(); k < K; k++) + ret.entry(r, c, ret.entry(r, c) + entry(r, k) * m.entry(k, c)); + return ret; + } + + //! @brief return \a (*this) times \a s. \a s is a scalar + Matrix mul(Entry const& s) const { + Matrix ret(*this); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + ret.entry(r, c, ret.entry(r, c) * s); + return ret; + } + + //! @brief return \a (*this) / \a s. \a s is a scalar + Matrix div(Entry const& s) const { + Matrix ret(*this); + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + ret.entry(r, c, ret.entry(r, c) / s); + return ret; + } + + //! @brief Return a identity matrix with size equal to itself + Matrix identity() const { + Matrix ret(*this); + ret.identitied(); + return ret; + } + + /*! + * @brief Let itself be an identity matrix + * + * Our definition of Identity matrix is 1 for entry(i, i) and 0 otherwise. + */ + Matrix& identitied() { + for (size_t r = 0, R = rows(); r < R; r++) + for (size_t c = 0, C = cols(); c < C; c++) + entry(r, c, (r == c ? Entry(1) : Entry(0))); + return *this; + } + + /*! + * @brief Return a matrix which is an inverse matrix of \a (*this) + * + * If inverse matrix doesn't exist, it will return a invalid matrix + */ + Matrix inverse() const { + if (rows() != cols() || rows() == 0) return Matrix<Entry>(); + Matrix tmp(rows(), cols() * 2, Entry(0)); + for (size_t r = 0, R = rows(); r < R; r++) { + for (size_t c = 0, C = cols(); c < C; c++) { + tmp.entry(r, c, entry(r, c)); + tmp.entry(r, c + cols(), (r == c ? Entry(1) : Entry(0))); + } + } + tmp.triangulared(); + for (ssize_t r = rows() - 1; r >= 0; r--) { + if (tmp(r, r) == Entry(0)) return Matrix<Entry>(); + for (ssize_t r2 = r - 1; r2 >= 0; r2--) { + Entry rat(-tmp.entry(r2, r) / tmp.entry(r, r)); + for (size_t c = r, C = tmp.cols(); c < C; c++) { + tmp.entry(r2, c, tmp.entry(r2, c) + rat * tmp(r, c)); + } + } + Entry rat(tmp.entry(r, r)); + for (size_t c = cols(), C = tmp.cols(); c < C; c++) { + tmp.entry(r, c - cols(), tmp.entry(r, c) / rat); + } + } + tmp.size(cols(), rows(), Entry(0)); + return tmp; + } + + //! @brief let itself become itself's inverse matrix + Matrix& inversed() { + copyFrom(inverse()); + return *this; + } + + //! @brief return itself's transpose matrix + Matrix transpose () const { + Matrix ret(cols(), rows(), Entry(0)); + for (size_t r = 0, R = cols(); r < R; r++) + for (size_t c = 0, C = rows(); c < C; c++) + ret.entry(r, c, entry(c, r)); + return ret; + } + + //! @brief Let itself become itself's transpose matrix + Matrix& transposed() { + copyFrom(transpose()); + return *this; + } + + //! @brief return a matrix which is the triangular form of \a (*this) + Matrix triangular() const { + Matrix<Entry> ret(*this); + ret.triangulared(); + return ret; + } + + //! @brief triangluar itself + Matrix& triangulared() { + for (size_t r = 0, c = 0, R = rows(), C = cols(); r < R && c < C; r++) { + ssize_t maxR; + for ( ; c < C; c++) { + maxR = -1; + for (size_t r2 = r; r2 < R; r2++) + if (maxR == -1 || tAbs(entry(r2, c)) > tAbs(entry(maxR, c))) + maxR = r2; + if (entry(maxR, c) != Entry(0)) break; + } + if (c >= C) break; + if (maxR != (ssize_t)r) { + for (size_t c2 = c; c2 < C; c2++) + std::swap(self()->entries_[self->index( r, c2)], + self()->entries_[self->index(maxR, c2)]); + } + for (size_t r2 = r + 1; r2 < R; r2++) { + Entry rati = -entry(r2, c) / entry(r, c); + entry(r2, c, Entry(0)); + for (size_t c2 = c + 1; c2 < C; c2++) + entry(r2, c2, entry(r2, c2) + entry(r, c2) * rati); + } + } + return *this; + } + + //! @brief same as \a copyFrom + Matrix& operator=(Matrix const& m) { + return copyFrom(m); + } + + //! @brief same as \a entry(r,c) + Entry operator()(size_t r, size_t c) const { + return entry(r, c); + } + + //! @brief same as \a entry(r,c,e) + Entry operator()(size_t r, size_t c, Entry const& e) { + return entry(r, c, e); + } + + //! @brief same as \a positive() + Matrix operator+() const { + return positive(); + } + + //! @brief same as \a negative() + Matrix operator-() const { + return negative(); + } + + //! @brief same as \a add(m) + Matrix operator+(Matrix const& m) const { + return add(m); + } + + //! @brief same as \a sub(m) + Matrix operator-(Matrix const& m) const { + return sub(m); + } + + //! @brief same as \a mul(m) + Matrix operator*(Matrix const& m) const { + return mul(m); + } + + //! @brief same as \a mul(m) + Matrix operator*(Entry const& s) const { + return mul(s); + } + + //! @brief same as \a div(s) + Matrix operator/(Entry const& s) const { + return div(s); + } +}; + +} #endif // math_Matrix_H__ diff --git a/meowpp/math/Transformation.h b/meowpp/math/Transformation.h index abf2649..0d9ee05 100644 --- a/meowpp/math/Transformation.h +++ b/meowpp/math/Transformation.h @@ -2,58 +2,234 @@ #define math_Transformation_H__ #include "Matrix.h" +#include "../Self.h" #include <cstdlib> -namespace meow{ - template<class Scalar> - class Transformation{ - private: - size_t _inputRows; - size_t _inputCols; - size_t _outputRows; - size_t _outputCols; - size_t _psize; - protected: - Transformation(size_t __inputRows, size_t __inputCols, - size_t __outputRows, size_t __outputCols, - size_t __psize): - _inputRows (__inputRows), - _inputCols (__inputCols), - _outputRows(__outputRows), - _outputCols(__outputCols), - _psize(__psize){ - } - public: - virtual Transformation(){ } - - size_t inputRows() const{ return _inputRows; } - size_t inputCols() const{ return _inputCols; } - size_t outputRows() const{ return _outputRows; } - size_t outputCols() const{ return _outputCols; } - size_t parameterSize() const{ return _psize; } - - virtual Scalar parameter(size_t __i) const = 0; - virtual Scalar parameter(size_t __i, Scalar const& __s) = 0; - - virtual Matrix<Scalar> transformate(Matrix<Scalar> const& __x) const = 0; - virtual Matrix<Scalar> jacobian (Matrix<Scalar> const& __x) const = 0; - virtual Matrix<Scalar> jacobian (Matrix<Scalar> const& __x, - size_t __i) const = 0; - - virtual bool inversable() const{ return false; } - - virtual Matrix<Scalar> invTransformate(Matrix<Scalar> const& __x) const{ - return Matrix<Scalar>(); - } - virtual Matrix<Scalar> invJacobian(Matrix<Scalar> const& __x) const{ - return Matrix<Scalar>(); - } - virtual Matrix<Scalar> invJacobian(Matrix<Scalar> const& __x, - size_t __i) const{ - return Matrix<Scalar>(); - } +namespace meow { + +/*! + * @brief A base class for implementing kinds of transformations. + * + * We define that the input and output form of our transformations all be + * \b matrix . Some advance methods such as calculating jacobian matrix + * will order that the input form must be a vector. + * @author cat_leopard + */ +template<class Scalar> +class Transformation { +private: + struct Myself { + size_t inputRows_; + size_t inputCols_; + size_t outputRows_; + size_t outputCols_; + size_t psize_; + + Myself& copyFrom(Myself const& b) { + inputRows_ = b. inputRows_; + inputCols_ = b. inputCols_; + outputRows_ = b.outputRows_; + outputCols_ = b.outputCols_; + psize_ = b.psize_; + return *this; + } }; + + Self<Myself> const self; +protected: + /*! + * Construct and setup + * @param [in] inputRows number of rows of the input matrix. + * @param [in] inputCols number of columns of the input matrix. + * @param [in] outputRows number of rows of the output matrix. + * @param [in] outputCols number of columns of the output matrix. + * @param [in] psize number of parameters + */ + Transformation(size_t inputRows, size_t inputCols, + size_t outputRows, size_t outputCols, + size_t psize): self(true) { + self()-> inputRows_ = inputRows; + self()-> inputCols_ = inputCols; + self()->outputRows_ = outputRows; + self()->outputCols_ = outputCols; + self()->psize_ = psize; + } + + /*! + * Construct and copy setings from another transformation class. + * @param [in] b Specify where to copy the informations. + */ + Transformation(Transformation const& b): self(false) { + copyFrom(b); + } + + /*! + * @brief Copy from the specified one + * + * @param [in] b The specified one + * @return \c *this + */ + Transformation& copyFrom(Transformation const& b) { + self().copyFrom(b.self); + return *this; + } + + /*! + * @brief Ceference from the specified one + * + * @param [in] b The specified one + * @return \c *this + */ + Transformation& referenceFrom(Transformation const& b) { + self().referenceFrom(b.self); + return *this; + } +public: + /*! + * Destructor + */ + virtual ~Transformation() { + } + + /*! + * @brief Return the number of rows of the input matrix. + * + * @return Number of rows. + */ + size_t inputRows() const { + return self->inputRows_; + } + + /*! + * @brief Return the number of columns of the input matrix. + * + * @return Number of columns. + */ + size_t inputCols() const { + return self->inputCols_; + } + + /*! + * @brief Return the number of rows of the output matrix. + * + * @return Number of rows. + */ + size_t outputRows() const { + return self->outputRows_; + } + + /*! + * @brief Return the number of columns of the output matrix. + * + * @return Number of columns. + */ + size_t outputCols() const { + return self->outputCols_; + } + + /*! + * @brief Return the number of parameters. + * + * @return Number of parameters. + */ + size_t parameterSize() const { + return self->psize_; + } + + /*! + * @brief Get the \a i -th parameter. + * + * @param [in] i The index of the specified parameter. + * @note It's a pure virtual method. + */ + virtual Scalar parameter(size_t i) const = 0; + + /*! + * @brief Setup the \a i -th parameter. + * + * @param [in] i The index of the specified parameter. + * @param [in] s The new value to the specified parameter. + * @note It's a pure virtual method. + */ + virtual Scalar parameter(size_t i, Scalar const& s) = 0; + + /*! + * @brief Do transformate. + * + * @param [in] x The input matrix. + * @note It's a pure virtual method. + */ + virtual Matrix<Scalar> transformate(Matrix<Scalar> const& x) const = 0; + + /*! + * @brief Calculate the jacobian matrix (derivate by the input matrix) + * of the transformation. + * + * Consider the case of a non-differentiable + * transformation might be implemented, we return an empty matrix + * now instead of making it be a pure virtual method. + * @param [in] x The input matrix. + * @return An empty matrix. + */ + virtual Matrix<Scalar> jacobian(Matrix<Scalar> const& x) const { + return Matrix<Scalar>(); + } + + /*! + * @brief Calculate the jacobian matrix (derivate by the \a i -th parameter) + * of the transformation. + * + * Consider the case of a non-differentiable transformation might be + * implemented, we return an empty matrix now instead of making it be + * a pure virtual method. + * @param [in] x The input matrix. + * @param [in] i The index of the specified parameter. + * @return An empty matrix. + */ + virtual Matrix<Scalar> jacobian(Matrix<Scalar> const& x, size_t i) const { + return Matrix<Scalar>(); + } + + /*! + * @brief Return whether this transformation is inversable or not + * + * @return \c false + */ + virtual bool inversable() const { return false; } + + /*! + * @brief Do the inverse transformation + * + * @param [in] x The input matirx + * @return An empty matrix + */ + virtual Matrix<Scalar> transformateInv(Matrix<Scalar> const& x) const { + return Matrix<Scalar>(); + } + + /*! + * @brief Return the jacobian matrix of the inverse transformation + * + * @param [in] x The input matirx + * @return An empty matrix + */ + virtual Matrix<Scalar> jacobianInv(Matrix<Scalar> const& x) const { + return Matrix<Scalar>(); + } + + /*! + * @brief Return the jacobian matrix of the inverse transformation + * + * @param [in] x The input matirx + * @param [in] i The index of the specified parameter. + * @return An empty matrix + */ + virtual Matrix<Scalar> jacobianInv(Matrix<Scalar> const& x, size_t i) const { + return Matrix<Scalar>(); + } +}; + } #endif // math_Transformation_H__ diff --git a/meowpp/math/Transformations.h b/meowpp/math/Transformations.h index 3b20ebe..b712558 100644 --- a/meowpp/math/Transformations.h +++ b/meowpp/math/Transformations.h @@ -2,52 +2,555 @@ #define math_Transformations_H__ #include "Transformation.h" +#include "Matrix.h" +#include "utility.h" +#include "../Self.h" #include <cstdlib> -namespace meow{ - template<class Scalar> - class BallProjection: public Transformation<Scalar>{ - private: - Scalar _radius; - size_t _dimension; - public: - BallProjection(size_t __dimension); - BallProjection(size_t __dimension, Scalar const& __radius); - - Scalar parameter(size_t __i) const; - Scalar parameter(size_t __i, Scalar const& __s); - - Scalar radius() const; - Scalar radius(Scalar const& __radius); - - Matrix<Scalar> transformate(Matrix<Scalar> const& __x) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x ) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x, size_t __i) const; +namespace meow { + +/*! + * @brief A ball projection is to project the given vector to a hyper-sphere + * + * Assume: + * - The dimension of a ball projection is \f$ N \f$ + * - The radius of the hyper-sphere is \f$ R \f$ + * . + * Then the transformation is like below: \n + * \f[ + * \left[ + * \begin{array}{c} + * x_1 \\ + * x_2 \\ + * x_3 \\ + * . \\ + * . \\ + * . \\ + * x_N \\ + * \end{array} + * \right] + * \stackrel{transformate}{\rightarrow} + * \left[ + * \begin{array}{c} + * \frac{x_1 \times R}{L} \\ + * \frac{x_2 \times R}{L} \\ + * \frac{x_3 \times R}{L} \\ + * . \\ + * . \\ + * . \\ + * \frac{x_N \times R}{L} \\ + * \end{array} + * \right] \\ + * \f] + * where \f$ L=\sqrt{x_1^2 + x_2^2 + x_3^2 + ... + x_N^2 } \f$ + * @author cat_leopard + */ +template<class Scalar> +class BallProjection: public Transformation<Scalar> { +private: + struct Myself { + Scalar radius_; + size_t dimension_; + + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + radius_ = b.radius_; + dimension_ = b.dimension_; + return *this; + } }; + Self<Myself> const self; +public: + /*! + * Constructor, copy settings from given BallProjection + * @param [in] b another ball projection class + */ + BallProjection(BallProjection const& b): + Transformation<Scalar>(b), self(false) { + copyFrom(b); + } + + /*! + * Constructor and setup, radius = 1 + * @param [in] d Dimension of the input/output vector + */ + BallProjection(size_t d): self(true), + Transformation<Scalar>(d, 1, d, 1, 1) { + self()->dimension_ = d; + radius(1); + } + + /*! + * Constructor and setup + * @param [in] d Dimension of the input/output vector + * @param [in] r Radius of the hyper-sphere + */ + BallProjection(size_t d, Scalar const& r): + Transformation<Scalar>(d, 1, d, 1, 1), self(true) { + self()->dimension_ = d; + radius(r); + } + + /*! + * @brief Copy settings from another one + * @param [in] b Another one + * @return \c *this + */ + BallProjection& copyFrom(BallProjection const& b) { + Transformation<Scalar>::copyFrom(b); + copyFrom(b); + return *this; + } + + /*! + * @brief Reference settings from another one + * @param [in] b Another one + * @return \c *this + */ + BallProjection& referenceFrom(BallProjection const& b) { + Transformation<Scalar>::referenceFrom(b); + referenceFrom(b); + return *this; + } + + /*! + * @brief same as \c radius() + */ + Scalar parameter(size_t i) const { + return radius(); + } + + /*! + * @brief same as \c radius(s) + */ + Scalar parameter(size_t i, Scalar const& s) { + return radius(s); + } + + /*! + * @brief Return the value of the radius + */ + Scalar radius() const { + return self->radius_; + } + + /*! + * @brief Setup the radius + * + * @param [in] r New value of the radius + * @return New radius + */ + Scalar radius(Scalar const& r) { + self()->radius_ = r; + return radius(); + } + + /*! + * @brief Get the dimension of this projection + */ + size_t dimension() const { + return self->dimension_; + } + + + /*! + * @brief Project the input vector(s) onto the hyper-sphere and return it. + * + * If the number of columns of the input matrix is larger than 1, this + * method will think that you want to transform multiple vector once + * and the number of columns of the output matrix will be the same of + * the number of columns of the input one. + * + * @param [in] x The input matrix. + * @return The output matrix. + * @note Take into account that too much safty checking will lead to + * inefficient, this method will not checking whether the dimension + * of the input vector/matrix is right. So be sure the data is valid + * before you call this method. + */ + Matrix<Scalar> transformate(Matrix<Scalar> const& x) const { + Matrix<Scalar> ret(x); + for (size_t c = 0, C = ret.cols(); c < C; c++) { + Scalar sum(0); + for (size_t i = 0; i < self->dimension_; i++) { + sum = sum + squ(ret(i, c)); + } + Scalar len(sqrt(double(sum))); + for (size_t i = 0; i < self->dimension_; i++) { + ret(i, c, ret(i, c) * radius() / len); + } + } + return ret; + } + + /*! + * @brief Return the jacobian matrix (derivate by the input vector) + * of this projection. + * + * This method only allow a vector-like matrix be input. + * Assume: + * - The dimension of a ball projection is \f$ N \f$ + * - The length of the input vector is \f$ L=\sqrt{x_1^2+x_2^2+...+x_N^2} \f$ + * - The radius of the hyper-sphere is \f$ R \f$ + * . + * Then the jacobian matrix is like below: \n + * \f[ + * \frac{R}{L^3} \times \left[ + * \begin{array}{ccccc} + * L^2-x_1^2 & -x_1x_2 & -x_1x_3 & ... & -x_1x_N \\ + * -x_2x_1 & L^2-x_2^2 & -x_2x_3 & ... & -x_2x_N \\ + * -x_3x_1 & -x_3x_2 & L^2-x_3^2 & ... & -x_3x_N \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * -x_Nx_1 & -x_Nx_2 & -x_Nx_3 & ... & L^2-x_N^2 \\ + * \end{array} + * \right] + * \f] + * + * @param [in] x The input matrix. + * @return The output matrix. + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x) const { + Scalar sum(0); + for(size_t i = 0, I = dimension(); i < I; ++i) + sum = sum + squ(x(i, 0)); + Scalar len(sqrt(double(sum))); + Matrix<Scalar> ret(dimension(), dimension(), Scalar(0.0)); + for(size_t i = 0, I = dimension(); i < I; ++i) + for(size_t j = 0; j < I; ++j) + if (i == j) { + ret(i, j, radius() * (squ(len) - squ(x(i, 0))) / cub(len)); + } + else { + ret(i, j, radius() * (-x(i, 0) * x(j, 0) / cub(len))); + } + return ret; + } - template<class Scalar> - class PhotoProjection: public Transformation<Scalar>{ - private: - Scalar _focal; - size_t _dimension; - public: - PhotoProjection(size_t __dimension); - PhotoProjection(size_t __dimension, Scalar const& __focal); - - Scalar parameter(size_t __i) const; - Scalar parameter(size_t __i, Scalar const& __s); - - Scalar focal() const; - Scalar focal(Scalar const& __focal); - - Matrix<Scalar> transformate(Matrix<Scalar> const& __x) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x ) const; - Matrix<Scalar> jacobian (Matrix<Scalar> const& __x, size_t __i) const; + /*! + * @brief Return the jacobian matrix (derivate by radius) of this projection. + * + * This method only allow a vector-like matrix be input. + * Assume: + * - The dimension of a ball projection is \f$ N \f$ + * - The length of the input vector is \f$ L=\sqrt{x_1^2+x_2^2+...+x_N^2} \f$ + * - The radius of the hyper-sphere is \f$ R \f$ + * . + * Then the jacobian matrix is like below: \n + * \f[ + * R \times \left[ + * \begin{array}{c} + * \frac{x_1}{L} \\ + * \frac{x_2}{L} \\ + * \frac{x_3}{L} \\ + * . \\ + * . \\ + * . \\ + * \frac{x_N}{L} \\ + * \end{array} + * \right] + * \f] + * + * @param [in] x The input matrix. + * @param [in] i Useless parameter + * @return The output matrix. + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x, size_t i) const { + Matrix<Scalar> ret(dimension(), 1, Scalar(0.0)); + Scalar sum(0); + for(size_t i = 0, I = dimension(); i < I; i++) { + sum = sum + squ(x(i, 0)); + } + return ret / Scalar(sqrt(double(sum))); + } + + /*! + * @brief Same as \c copyFrom(b) + */ + BallProjection& operator=(BallProjection const& b) { + return copyFrom(b); + } + + /*! + * @brief Same as \c transformate(v) + */ + Matrix<Scalar> operator()(Matrix<Scalar> const& v) const { + return transformate(v); + } +}; + + +/*! + * @brief A \b photo \b projection is a kind of transformation that project + * point/vector to a flat \b photo + * + * Assume: + * - The dimension of a photo projection is \f$ N \f$ + * - The length of the input vector is \f$ L \f$ + * - The focal length is \f$ f \f$ + * . + * Then transformation is like below: \n + * \f[ + * \left[ + * \begin{array}{c} + * x_1 \\ + * x_2 \\ + * x_3 \\ + * . \\ + * . \\ + * . \\ + * x_N \\ + * \end{array} + * \right] + * \stackrel{transformate}{\rightarrow} + * \left[ + * \begin{array}{c} + * \frac{-x_1 \times f}{x_N} \\ + * \frac{-x_2 \times f}{x_N} \\ + * \frac{-x_3 \times f}{x_N} \\ + * . \\ + * . \\ + * . \\ + * -f \\ + * \end{array} + * \right] \\ + * \f] + * i.e. projecte the vector onto the plane \f$ x_N = -f \f$. + * + * @author cat_leopard + */ +template<class Scalar> +class PhotoProjection: public Transformation<Scalar> { +private: + struct Myself { + Scalar focal_; + size_t dimension_; + + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + focal_ = b.focal_; + dimension_ = b.dimension_; + return *this; + } }; -} + + Self<Myself> const& self; +public: + /*! + * Constructor, focal = 1 + */ + PhotoProjection(size_t dimension): + Transformation<Scalar>(dimension, 1, dimension, 1, 1), self(true) { + self()->dimension_ = dimension; + focal(1); + } + + /*! + * Constructor + */ + PhotoProjection(size_t dimension, Scalar const& f): + Transformation<Scalar>(dimension, 1, dimension, 1, 1), self(true) { + self()->dimension_ = dimension; + focal(f); + } + + /*! + * Constructor, copy settings from another PhotoProjection. + */ + PhotoProjection(PhotoProjection const& p): + Transformation<Scalar>(p), self(false) { + self().copyFrom(p.self); + } + + /*! + * Copy settings from another one + * @param [in] b another one + * @return \c *this + */ + PhotoProjection& copyFrom(PhotoProjection const& b) { + Transformation<Scalar>::copyFrom(b); + self().copyFrom(b.self); + return *this; + } + + /*! + * Reference settings from another one + * @param [in] b another one + * @return \c *this + */ + PhotoProjection& referenceFrom(PhotoProjection const& b) { + Transformation<Scalar>::referenceFrom(b); + self().referenceFrom(b.self); + return *this; + } + + /*! + * @brief Same as \c focal() + */ + Scalar parameter(size_t i) const { + return focal(); + } + + /*! + * @brief Same as \c focal(s) + */ + Scalar parameter(size_t i, Scalar const& s){ + return focal(s); + } + + /*! + * @brief Get the focal length + * @return Focal length + */ + Scalar focal() const { + return self->focal_; + } -#include "Transformations.hpp" + /*! + * @brief Set the focal length + * + * @param [in] f New focal length + * @return New focal length + */ + Scalar focal(Scalar const& f){ + self()->focal_ = f; + return focal(); + } + + /*! + * @brief Get the dimension of this projection + */ + size_t dimension() const { + return self->dimension_; + } + + /*! + * @brief Project the input vector(s) onto the plane + * + * The equation of the plane is \f$ x_N = -f \f$, where the \f$ N \f$ + * is the dimension of this projection and f is the focal length. \n + * If the number of columns of the input matrix is larger than 1, this + * method will think that you want to transform multiple vector once + * and the number of columns of the output matrix will be the same of + * the number of columns of the input one. + * + * @param [in] x The input matrix. + * @return The output matrix. + * @note Take into account that too much safty checking will lead to + * inefficient, this method will not checking whether the dimension + * of the input vector/matrix is right. So be sure the data is valid + * before you call this method. + */ + Matrix<Scalar> transformate(Matrix<Scalar> const& x) const { + Matrix<Scalar> ret(x); + for (size_t c = 0, C = ret.cols(); c < C; c++) { + for (size_t i = 0, I = dimension(); i < I; ++i) { + ret(i, c, -ret(i, c) * focal() / ret(I - 1, c)); + } + } + return ret; + } + + /*! + * @brief Return the jacobian matrix (derivate by the input vector) + * of this projection. + * + * This method only allow a vector-like matrix be input. + * Assume: + * - The dimension of this projection is \f$ N \f$ + * - The length of the input vector is \f$ L=\sqrt{x_1^2+x_2^2+...+x_N^2} \f$ + * - The focal length of this projection is \f$ f \f$ + * . + * Then the jacobian matrix is like below: \n + * \f[ + * f \times + * \left[ + * \begin{array}{ccccc} + * \frac{-1}{x_N} & 0 & 0 & ... & \frac{1}{x_N^2} \\ + * 0 & \frac{-1}{x_N} & 0 & ... & \frac{1}{x_N^2} \\ + * 0 & 0 & \frac{-1}{x_N} & ... & \frac{1}{x_N^2} \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * 0 & 0 & 0 & ... & 0 \\ + * \end{array} + * \right] + * \f] + * + * @param [in] x The input matrix. + * @return The output matrix. + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x) const{ + Matrix<Scalar> ret(dimension(), dimension(), Scalar(0.0)); + for(ssize_t i = 0, I = (ssize_t)dimension() - 1; i < I; i++){ + ret(i, i, -focal() / x(I, 0) ); + ret(i, dimension() - 1, focal() / squ(x(I, 0))); + } + return ret; + } + + /*! + * @brief Return the jacobian matrix (derivate by the focus length) + * of this projection. + * + * This method only allow a vector-like matrix be input. + * Assume: + * - The dimension of this projection is \f$ N \f$ + * - The length of the input vector is \f$ L=\sqrt{x_1^2+x_2^2+...+x_N^2} \f$ + * - The focal length of this projection is \f$ f \f$ + * . + * Then the jacobian matrix is like below: \n + * \f[ + * f \times + * \left[ + * \begin{array}{c} + * \frac{-x_1}{x_N} \\ + * \frac{-x_2}{x_N} \\ + * \frac{-x_3}{x_N} \\ + * . \\ + * . \\ + * . \\ + * -1 \\ + * \end{array} + * \right] + * \f] + * + * @param [in] x The input matrix. + * @param [in] i Useless parameter + * @return The output matrix. + */ + Matrix<Scalar> jacobian(Matrix<Scalar> const& x, size_t i) const{ + Matrix<Scalar> ret(dimension(), 1, Scalar(0.0)); + for(size_t i = 0, I = dimension(); i < I; ++i) { + ret(i, 0, -x(i, 0) / x(I - 1, 0)); + } + return ret; + } + + /*! + * @brief Same as \c copyFrom(b) + */ + PhotoProjection& operator=(PhotoProjection const& b) { + return copyFrom(b); + } + + /*! + * @brief Same as \c transformate(v) + */ + Matrix<Scalar> operator()(Matrix<Scalar> const& v) const { + return transformate(v); + } +}; + +} #endif // Transformations_H__ diff --git a/meowpp/math/Vector.h b/meowpp/math/Vector.h new file mode 100644 index 0000000..d387c2b --- /dev/null +++ b/meowpp/math/Vector.h @@ -0,0 +1,276 @@ +#ifndef math_Vector_H__ +#define math_Vector_H__ + +#include "../Self.h" +#include "Matrix.h" +#include "utility.h" + +#include <vector> + +#include <cmath> + +namespace meow { + +/*! + * @brief \b vector + * + * @author cat_leopard + */ +template<class Scalar> +class Vector { +private: + Matrix<Scalar> matrix_; +public: + /*! + * @brief constructor + * + * With \b dimension=0, which means \b invalid. + */ + Vector(){ + } + + /*! + * @brief constructor + * + * Copy from another vector + * + * @param [in] v another vector + */ + Vector(Vector const& v) { + matrix_.copyFrom(v.matrix_); + } + + /*! + * @brief constructor + * + * From matrix's first column + * + * @param [in] m matrix + */ + Vector(Matrix<Scalar> const& m) { + matrix_.copyFrom(m.col(0)); + } + + /*! + * @brief constructor + * + * From matrix's \a i-th column + * + * @param [in] m matrix + * @param [in] i i-th + */ + Vector(Matrix<Scalar> const& m, size_t i) { + matrix_.copyFrom(m.col(i)); + } + + /*! + * @brief constructor + * + * Copy from another std::vector + * + * @param [in] v vector + */ + Vector(std::vector<Scalar> const& v) { + matrix_.size(v.size(), 1, Scalar(0)); + for (size_t i = 0, I = v.size(); i < I; i++) { + matrix_.entry(i, 0, v[i]); + } + } + + /*! + * @brief constructor + * + * setup dimension and inital value + * + * @param [in] d dimension + * @param [in] e inital value + */ + Vector(size_t d, Scalar const& e) { + matrix_.reset(d, 1, e); + } + + //! @brief destructor + ~Vector(){ + } + + //! @brief copy from ... + Vector& copyFrom(Vector const& v) { + matrix_.copyFrom(v.matrix_); + return *this; + } + + //! @brief reference from ... + Vector& referenceFrom(Vector const& v) { + matrix_.referenceFrom(v.matrix_); + return *this; + } + + //! @brief Return a \a dimension x 1 matrix form of it + Matrix<Scalar> const& matrix() const { + return matrix_; + } + + //! @brief return dimension + size_t dimension() const { + return matrix_.rows(); + } + + /*! + * @brief resize the dimension + * + * @param [in] d new dimension + * @param [in] s inital entry + * @return new dimension + */ + size_t dimension(size_t d, Scalar const& s) { + matrix_.rows(d, s); + return dimension(); + } + + /*! + * @brief Return whether \c dimension>0 is true or not + * @return \c true/false + */ + bool valid() const { + return (dimension() > 0); + } + + //! @brief return \a i -th entry + Scalar entry(size_t i) const { + return matrix_.entry(i, 0); + } + + /*! + * @brief change \a i -th entry + * + * @param [in] i i-th + * @param [in] s new value + */ + Scalar entry(size_t i, Scalar const& s) { + matrix_.entry(i, 0, s); + return entry(i); + } + + /*! + * @brief change \a i -th to \a j -th entries + * + * @param [in] i i-th + * @param [in] j j-th + * @param [in] s new value + */ + void entries(size_t i, size_t j, Scalar const& s) { + for (size_t it = i; it <= j; it++) { + matrix_.entry(it, 0, s); + } + } + + //! @brief subvector form i-th to j-th + Vector subVector(size_t i, size_t j) { + return Vector(matrix_.subMatrix(i, 0, j, 0)); + } + + //! @brief return +\a (*this) + Vector positive() const { + return *this; + } + + //! @brief return -\a (*this) + Vector negative() const { + return Vector(matrix_.negative()); + } + + //! @brief return \a (*this)+v + Vector add(Vector const& v) const { + return Vector(matrix_.add(v.matrix_)); + } + + //! @brief return \a (*this)-v + Vector sub(Vector const& v) const { + return Vector(matrix_.sub(v.matrix_)); + } + + //! @brief return \a (*this)*s , where s is a scalar + Vector mul(Scalar const& s) const { + return Vector(matrix_.mul(s)); + } + + //! @brief return \a (*this)/s , where s is a scalar + Vector div(Scalar const& s) const { + return Vector(matrix_.div(s)); + } + + //! @brief dot + Scalar dot(Vector const& v) const { + return matrix_.transpose().mul(v.matrix_).entry(0, 0); + } + + //! @brief sqrt of \a length2 + Scalar length() const { + return Scalar(sqrt((double)length2())); + } + + //! @brief same as \a (*this).dot(*this) + Scalar length2() const { + return dot(*this); + } + + //! @brief return a normalize form of itself + Vector normalize() const { + return div(length()); + } + + //! @brief Let itself be normalize form + Vector& normalized() { + copyFrom(normalize()); + return *this; + } + + //! @brief same as copyFrom + Vector& operator=(Vector const& v) { + return copyFrom(v); + } + + //! @brief same as entry(i) + Scalar operator()(size_t i) const { + return entry(i); + } + + //! @brief same as positive() + Vector operator+() const { + return positive(); + } + + //! @brief same as negative() + Vector operator-() const { + return negative(); + } + + //! @brief same as add(v) + Vector operator+(Vector const& v) const { + return add(v); + } + + //! @brief same as sub(v) + Vector operator-(Vector const& v) const { + return sub(v); + } + + //! @brief same as dot(v) + Scalar operator*(Vector const& v) const { + return dot(v); + } + + //! @brief same as mul(s) + Vector operator*(Scalar const& s) const { + return mul(s); + } + + //! @brief same as div(s) + Vector operator/(Scalar const& s) const { + return div(s); + } +}; + +} + +#endif // math_Vector_H__ diff --git a/meowpp/math/methods.h b/meowpp/math/methods.h new file mode 100644 index 0000000..4a47d33 --- /dev/null +++ b/meowpp/math/methods.h @@ -0,0 +1,181 @@ +#ifndef math_methods_H__ +#define math_methods_H__ + +#include "Matrix.h" +#include "Vector.h" +#include "utility.h" + +#include <cstdlib> +#include <vector> + +namespace meow { + +/*! + * @brief Run the \b RANSAC method to approach the best solution. + * + * \b RANdom \b SAmple \b Consensus is an iterative method to estimate + * parameters of a mathematical model from a set of observed data which + * contains \c outliers. \n + * Each iterator it will choose a subset of elements, the smallest set which can + * form a valid parameters, from the data set. And then calculate how many + * elements in the whole data set is inliers. After iterator much times, + * we just say the best solution is the parameters that has the much + * inliers elements in whole iterators. + * + * Assume: + * - We need at least \f$ N \f$ element to form a valid parameters. + * - The probability of choosing a right element from data set each time is + * \f$ p_0 \f$. + * - We want the probability of our solution actually being the best solution + * be \f$ P \f$. + * - We need to iterator \f$ M \f$ times. + * . + * Then we can estimate the number of iterations \f$ M \f$ : + * \f[ + * \begin{aligned} + * & (1 - p_0^N)^M \leq(1 - P) \\ + * \Rightarrow & M \log(1 - p_0^N) \leq \log(1 - P) \\ + * \Rightarrow & M \geq \frac{\log(1 - p)}{\log(1 - p_0^N)},~~ + * \because (1-p_0^N<1 \Rightarrow \log(1-p_0^N)<0) + * \end{aligned} + * \f] + * + * So in this function we choose + * \f$ M = \lceil \frac{\log(1 - P)}{\log(1 - p_0^N)} \rceil \f$ + * + * @param [in] data The whole data sett + * @param [in] w Weight function to give a floating number for a given + * parameters which means how best this solution is. Negitave + * number means invalid parameters. + * @param [in] N \f$ N \f$, defined above + * @param [in] p0 \f$ p_0 \f$, defined above + * @param [in] P \f$ P \f$, defined above + * @return solution. + * + * @author cat_leopard + */ +template<class Data, class WeightingClass> +inline std::vector<Data> ransac(std::vector<Data> const& data, + WeightingClass const& w, + size_t N, + double p0, double P) { + if (data.size() < N) { + return std::vector<Data>(); + } + double ww = -1.0; + std::vector<Data> ret; + for (double count = ceil(log(1.0 - P) / log(1.0 - pow(p0, N))); + count > 0.0; count -= 1.0) { + std::vector<Data> sample; + std::vector<int> index(N); + for (size_t i = 0; i < N; i++) { + for (bool ok = false; !ok; ) { + index[i] = rand() % data.size(); + ok = true; + for (size_t j = 0; ok && j < i; j++) + if (index[i] == index[j]) + ok = false; + } + sample.push_back(data[index[i]]); + } + double w_now = w(sample, data); + if (w_now < 0) { + count += 0.5; + continue; + } + if (ww < w_now) { + ret = sample; + ww = w_now; + } + } + return ret; +} + + +/*! + * @brief Run the \b Levenberg-Marquardt method to solve a non-linear + * least squares problem. + * + * Assume: + * - The function we want to optimize is + * \f$ F: \mathbb{R} ^N \mapsto \mathbb{R}^M \f$ + * - We want to find the best solution \f$ v \f$ such that + * \f$ F(v)^T F(v) = 0\f$. But there is a gived threshold + * \f$ \epsilon \f$, we can just find a \f$ v \f$ such that + * \f$ F(v)^T F(v) < \epsilon \f$, which is mush easier. + * - User gived a initiial vector \f$ v_0 \f$ + * . + * Then we just iteratilly find \f$ v_1, v_2, v_3, v_4... \f$ until a + * vector \f$ v_k \f$ satisified that \f$ F(v_k)^TF(v_k)<\epsilon \f$ . + * And each iterator we have: + * \f[ + * v_{i+1} = v_i + (J(v_i)^TJ(v_i)+\lambda I_{N\times N})^{-1} J(v_i)^T F(v_i) + * \f] + * Where \f$ J(v) \f$ is a jacobian matrix defined below: + * \f[ + * J(v) = \frac{d}{dv}F(v) = + * \left[ \begin{array}{ccccc} + * \frac{\partial F_1(v)}{\partial v_1} & + * \frac{\partial F_1(v)}{\partial v_2} & + * \frac{\partial F_1(v)}{\partial v_3} & + * ... & + * \frac{\partial F_1(v)}{\partial v_N} \\ + * \frac{\partial F_2(v)}{\partial v_1} & + * \frac{\partial F_2(v)}{\partial v_2} & + * \frac{\partial F_2(v)}{\partial v_3} & + * ... & + * \frac{\partial F_2(v)}{\partial v_N} \\ + * \frac{\partial F_3(v)}{\partial v_1} & + * \frac{\partial F_3(v)}{\partial v_2} & + * \frac{\partial F_3(v)}{\partial v_3} & + * ... & + * \frac{\partial F_3(v)}{\partial v_N} \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * . & . & . & & . \\ + * \frac{\partial F_M(v)}{\partial v_1} & + * \frac{\partial F_M(v)}{\partial v_2} & + * \frac{\partial F_M(v)}{\partial v_3} & + * ... & + * \frac{\partial F_M(v)}{\partial v_N} \\ + * \end{array} \right] + * \f] + * And \f$ \lambda \f$ is a magic number.... + * @param [in] func \f$ F \f$, a function(class with \c operator() ) + * which input a vector and the output the squares errors. + * @param [in] jaco \f$ J \f$, a function which input a vector + * and then output \b func derivate by the vector + * @param [in] iden \f$ \lambda I_{N \times N} \f$, defined above + * @param [in] init \f$ v_0 \f$Initial vector + * @param [in] stop A function return a boolean which means the error is + * acceptable or not, so \f[ + * S_{top}(v) = \begin{cases} + * true & if~F(v)<\epsilon \\ + * false & else + * \end{cases} + * \f] + * @param [in] counter To prevent infinit loop. + * @return a vector which means the best solution this function found. + * + * @author cat_leopard + */ +template<class Scalar, class F, class J, class I, class Stop> +inline Vector<Scalar> levenbergMarquardt(F const& func, + J const& jaco, + I const& iden, + Vector<Scalar> const& init, + Stop const& stop, + int counter = -1) { + Vector<Scalar> ans(init); + for(Vector<Scalar> rv; + !stop((rv = func(ans)).length2()) && counter != 0; counter--) { + Matrix<Scalar> j(jaco(ans)), jt(j.transpose()); + Matrix<Scalar> i(iden(ans)); + ans = ans - Vector<Scalar>((jt * j + i).inverse() * jt * rv.matrix()); + } + return ans; +} + +} + +#endif // math_methods_H__ diff --git a/meowpp/math/utility.h b/meowpp/math/utility.h index 9bfbb7a..43b4785 100644 --- a/meowpp/math/utility.h +++ b/meowpp/math/utility.h @@ -2,81 +2,138 @@ #define math_utility_H__ #include <cstdlib> +#include <vector> +#include <algorithm> +#include <cmath> namespace meow{ - static const double PI = 3.14159265358979323846264338327950288; - //# - //# === meow:: *Functios* in math/utility.h - //# - //# [options="header",width="100%",cols="1>s,5<,1<,10<",grid="rows"] - //# |============================================================== - //# |Name | Parameters | Return_Type | Description - - //# |noEPS<T> |(T `value`, T `eps` = 1e-9) | T | - //# 如果abs(輸入的數值) < eps, 則回傳0, 否則回傳輸入的數值 - template<class T> - inline T noEPS(T value, T eps = 1e-9); - - - //# |normalize<T> |(T `lower`, T `upper`, \ T value) - //# | T | `(value - lower) / (upper - lower)` - template<class T> - inline T normalize(T lower, T upper, T value); - - - //# |denormalize<T> |(T `lower`, T `upper`, - //# \ T `ratio`) | T | `lower + (upper - lower) * ratio` - template<class T> - inline T denormalize(T lower, T upper, T ratio); - - - //# |ratioMapping<T>|(T `l1`, T `u1`, - //# \T `m1`, T `l2`,\T `u2`) - //# | T | `denormalize(l2, u2, normalize(l1, u1, m1))` - template<class T> - inline T ratioMapping(T l1, T u1, T m1, T l2, T u2); - - - //# |inRange<T> |(T const& `mn`, T const& `mx`, \ T const& `v`) | T | - //# `std::max(mn, std::min(mx, v))` - template<class T> - inline T inRange(T const& mn, T const& mx, T const& v); - - - //# |squ<T> |(T const& `x`) | T| `x * x` - template<class T> - inline T squ(T const& x); - - - //# |cub<T> |(T const& `x`) | T| `x * x * x` - template<class T> - inline T cub(T const& x); +//! 圓周率... +static const double PI = 3.14159265358979323846264338327950288; - //# |average<T>|(T const& `beg`, T const& `end`, \ double `sigs`)| T| - //# 只將 `sigs` 個標準差以內的數據拿來取平均 - template<class T> - inline double average(T const& beg, T const& end, double sigs); - - - //# |average<T>|(T const& `beg`, T const& `end`, - //# \ T const& `p`, double `sigs`)| T| 同上, 不過這次用 `p` 來加權平均 - template<class T> - inline double average(T const& beg, T const& end, T const& p, double sigs); - - //# |============================================================== - - - //# - //# [NOTE] - //# ==================================== - //# * 額外附贈一個 `const double PI = 3.141592653589......` - //# ==================================== - //# - //# ''' - //# +/*! + * @brief 如果abs(輸入的數值) < eps, 則回傳0, 否則回傳輸入的數值 + */ +template<class T> +inline T noEPS(T value, T eps = 1e-9){ + T epsp((eps < T(0)) ? -eps : eps); + return ((value < -epsp || value > epsp) ? value : T(0)); } -#include "utility.hpp" +/*! + * @brief \c (value-lower)/(upper-lower) + */ +template<class T> +inline T normalize(T lower, T upper, T value){ + return (value - lower) / (upper - lower); +} + +/*! + * @brief \c (lower+_ratio*(upper-lower)) + */ +template<class T> +inline T denormalize(T lower, T upper, T _ratio){ + return lower + _ratio * (upper - lower); +} + +/*! + * @brief \c denormalize(l2,u2,normalize(l1,u1,m1)) + */ +template<class T> +inline T ratioMapping(T l1, T u1, T m1, T l2, T u2){ + return denormalize(l2, u2, normalize(l1, u1, m1)); +} + +/*! + * @brief \c std::min(mx,std::max(mn,v)) + */ +template<class T> +inline T inRange(T const& mn, T const& mx, T const& v){ + return std::min(mx, std::max(mn, v)); +} + +/*! + * @brief \c x*x + */ +template<class T> +inline T squ(T const& x){ + return x * x; +} + +/*! + * @brief \c x*x*x + */ +template<class T> +inline T cub(T const& x){ + return x * x * x; +} + +/*! + * @brief 只將 \c sigs 個標準差以內的數據拿來取平均 + */ +template<class T> +inline double average(T const& beg, T const& end, double sigs){ + int N = 0; + double av = 0; + for(T it = beg; it != end; it++, N++){ + av += *it; + } + av /= N; + double sig = 0; + for(T it = beg; it != end; it++){ + sig += (*it - av) * (*it - av); + } + sig = sqrt(sig / N); + double lower = av - sig * sigs, upper = av + sig * sigs; + double ret = 0, retn = 0; + for(T it = beg; it != end; it++){ + if(lower <= *it && *it <= upper){ + ret += *it; + retn++; + } + } + return ret / retn; +} + +/*! + * @brief 只將 \c sigs 個標準差以內的數據拿來取平均, 不過這次用 \c p 來加權平均 + */ +template<class T> +inline double average(T const& beg, T const& end, T const& p, double sigs){ + int N = 0; + double ps = 0; + for(T it = beg, ip = p; it != end; it++, N++, ip++){ + ps += *ip; + } + double av = 0; + for(T it = beg, ip = p; it != end; it++, ip++){ + av += *it * *ip / ps; + } + double sig = 0; + for(T it = beg, ip = p; it != end; it++, ip++){ + sig += *ip / ps * (*it - av) * (*it - av); + } + sig = sqrt(sig); + double lower = av - sig * sigs, upper = av + sig * sigs; + double ret = 0, retn = 0; + for(T it = beg, ip = p; it != end; it++, ip++){ + if(lower <= *it && *it <= upper){ + ret += *it * *ip; + retn += *ip; + } + } + if(retn <= 1e-10) return av; + return ret / retn; +} + +/*! + * @brief 就只是個取絕對值 + */ +template<class T> +inline T tAbs(T const& t){ + return (t < 0 ? -t : t); +} + +} #endif // math_utility_H__ diff --git a/meowpp/oo/!readme.asciidoc b/meowpp/oo/!readme.asciidoc new file mode 100644 index 0000000..2e91ca5 --- /dev/null +++ b/meowpp/oo/!readme.asciidoc @@ -0,0 +1,32 @@ + +物件相關 + +===== ObjBase.h + +.Classes +* `meow::ObjBase` + +===== ObjTypes.h + +.Classes +* `meow::ObjType` +* `meow::ObjInt` +* `meow::ObjSizeT` +* `meow::ObjDouble` +* `meow::ObjString` + +===== ObjArray.h + +.Classes +* `meow::ObjArray` + +===== ObjDictionary.h + +.Classes +* `meow::ObjDictionary` + +===== ObjSelector.h + +.Classes +* `meow::ObjSelector<SID>` + diff --git a/meowpp/oo/ObjArray.h b/meowpp/oo/ObjArray.h new file mode 100644 index 0000000..4417524 --- /dev/null +++ b/meowpp/oo/ObjArray.h @@ -0,0 +1,166 @@ +#ifndef oo_ObjArray_H__ +#define oo_ObjArray_H__ + +#include "ObjBase.h" + +#include "../Self.h" + +#include <vector> +#include <string> +#include <typeinfo> + +#include <cstdio> +#include <cstdlib> + +namespace meow { + +/*! + * @brief 純粹把 \c std::vector 包起來, 變成繼承自 ObjBase + * + * @author cathook + */ +template<class T> +class ObjArray: public ObjBase { +private: + struct Myself { + std::vector<T> array_; + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + array_ = b.array_; + return *this; + } + }; + Self<Myself> const self; +public: + ObjArray(): self(true) { + } + + ObjArray(ObjArray const& a): self(false) { + self().copyFrom(a.self); + } + + ObjArray(std::vector<T> const& a): self(true) { + self()->array_ = a; + } + + ObjArray(size_t sz, T const& e): self(true) { + self()->array_.resize(sz, e); + } + + ~ObjArray() { + } + + ObjArray& copyFrom(ObjArray const& a) { + self().copyFrom(a.self); + return *this; + } + + ObjArray& referenceFrom(ObjArray const& a) { + self().referenceFrom(a.self); + return *this; + } + + size_t size() const { + return self->array_.size(); + } + bool empty() const { + return self->array_.empty(); + } + + size_t size(size_t res, T const& i) { + self()->array_.resize(res, i); + return size(); + } + + size_t size(size_t res) { + self()->array_.resize(res); + return size(); + } + + void clear() { + self()->array_.clear(); + } + + T const& entry(size_t i) const { + return self->array_[i]; + } + T const& entry(size_t i, T const& e) { + self()->array_[i] = e; + return entry(i); + } + + T const& putBack(T const& e) { + self()->array_.push_back(e); + return entry(size() - 1); + } + + bool popBack() { + if (empty()) return false; + self()->array_.pop_back(); + return true; + } + + ObjArray& operator=(ObjArray const& a) { + return copyFrom(a); + } + + T const& operator[](size_t i) const { + return self->array_[i]; + } + + T& operator[](size_t i) { + return self()->array_[i]; + } + + bool write(FILE* f, bool bin, unsigned int fg) const { + size_t sz = size(); + if (bin) { + if (fwrite(&sz, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fprintf(f, "%lu\n", sz) < 1) return false; + } + for (size_t i = 0; i < sz; i++) { + if (self->array_[i].write(f, bin, fg) == false) return false; + } + return true; + } + + bool read(FILE* f, bool bin, unsigned int fg) { + size_t sz; + if (bin) { + if (fread(&sz, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fscanf(f, "%lu\n", &sz) < 0) return false; + } + size(sz); + for (size_t i = 0; i < sz; i++) { + if (self()->array_[i].read(f, bin, fg) == false) return false; + } + return true; + } + + ObjBase* create() const { + return new ObjArray(); + } + + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*b)); + } + + char const* ctype() const { + return typeid(*this).name(); + } + + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // oo_ObjArray_H__ diff --git a/meowpp/oo/ObjBase.h b/meowpp/oo/ObjBase.h index 3aa99d7..974fdf1 100644 --- a/meowpp/oo/ObjBase.h +++ b/meowpp/oo/ObjBase.h @@ -7,33 +7,37 @@ #include <string> namespace meow{ - class ObjBase{ - protected: - ObjBase(){ } - public: - virtual ~ObjBase(){ } - // - virtual bool read (FILE* f, bool bin, unsigned int fg){ return false; } - virtual bool write(FILE* f, bool bin, unsigned int fg){ return false; } - // - virtual ObjBase* create() const{ return NULL; } - // - virtual char const* ctype() const{ - static char const* ptr = typeid(*this).name(); - return ptr; - } - virtual std::string type() const{ - return std::string(ctype()); - } - // - static char const* ctypeBase(){ - static char const* ptr = typeid(ObjBase).name(); - return ptr; - } - static std::string typeBase(){ - return std::string(ctypeBase()); - } - }; + +/*! + * @brief 一切物件的Base, 並要求每個物件都要有read, write, create, ... 等功能 + * + * @author cathook + */ +class ObjBase { +protected: + ObjBase(){ } +public: + virtual ~ObjBase(){ } + // + virtual bool write(FILE* f,bool bin,unsigned int fg) const { return false; } + virtual bool read(FILE* f,bool bin,unsigned int fg) { return false; } + // + virtual ObjBase* create() const { return NULL; } + virtual ObjBase* copyFrom(ObjBase const* b) { (*this) = (*b); return this; } + // + virtual char const* ctype() const{ + static char const* ptr = typeid(*this).name(); + return ptr; + } + virtual std::string type() const{ return std::string(ctype()); } + // + static char const* ctypeBase(){ + static char const* ptr = typeid(ObjBase).name(); + return ptr; + } + static std::string typeBase(){ return std::string(ctypeBase()); } +}; + } #endif // oo_ObjBase_H__ diff --git a/meowpp/oo/ObjDictionary.h b/meowpp/oo/ObjDictionary.h new file mode 100644 index 0000000..39e103e --- /dev/null +++ b/meowpp/oo/ObjDictionary.h @@ -0,0 +1,158 @@ +#ifndef oo_ObjDictionary_H__ +#define oo_ObjDictionary_H__ + +#include "ObjBase.h" + +#include "../Self.h" + +#include <string> +#include <typeinfo> +#include <map> + +#include <cstdio> +#include <cstdlib> + +namespace meow { + +/*! + * @brief 純粹把 \c std::map 包起來, 變成繼承自 ObjBase + * + * @author cathook + */ +template<class Key, class Value> +class ObjDictionary: public ObjBase { +private: + struct Myself { + std::map<Key, Value> dictionary_; + Myself() { + } + ~Myself() { + } + Myself& copyFrom(Myself const& b) { + dictionary_ = b.dictionary_; + return *this; + } + }; + Self<Myself> const self; +public: + ObjDictionary(): self(true) { + } + + ObjDictionary(ObjDictionary const& d): self(false) { + self.copyFrom(b.self); + } + + ObjDictionary(std::map<Key, Value> const& d): self(true) { + self()->dictionary_ = d; + } + + ~ObjDictionary() { + } + + ObjDictionary& copyFrom(ObjDictionary const& d) { + self().copyFrom(d.self); + return *this; + } + + ObjDictionary& referenceFrom(ObjDictionary const& d) { + self().referenceFrom(d.self); + return *this; + } + + size_t size() const { + return self->dictionary_.size(); + } + bool empty() const { + return self->dictionary_.empty(); + } + + void clear() { + self()->dictionary_.clear(); + } + + std::map<Key, Value>::const_iterator end() const { + return self->dictionary_.end(); + } + + std::map<Key, Value>::iterator end() { + return self()->dictionary_.end(); + } + + std::map<Key, Value>::const_iterator find(Key const& k) const { + return self->dictionary_.find(k); + } + + std::map<Key, Value>::iterator find(Key const& k) { + return self()->dictionary_.find(k); + } + + bool exist(Key const& k) const { + return (find() != end()); + } + + void insert(Key const& k, Value const& v) { + self->dictionary_.insert(std::pair<Key, Value>(k, v)); + } + + ObjDictionary& operator=(ObjDictionary const& a) { + return copyFrom(a); + } + + Value& operator[](Key const& k) { + return self()->dictionary_[k]; + } + + bool write(FILE* f, bool bin, unsigned int fg) const { + size_t sz = size(); + if (bin) { + if (fwrite(&sz, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fprintf(f, "%lu\n", sz) < 1) return false; + } + for (std::map<Key, Value>::const_iterator + it = self->dictionary_.begin(); it != self->dictionary_.end(); ++it) { + if (it->first .write(f, bin, fg) == false) return false; + if (it->second.write(f, bin, fg) == false) return false; + } + return true; + } + + bool read(FILE* f, bool bin, unsigned int fg) { + size_t sz; + if (bin) { + if (fread(&sz, sizeof(size_t), 1, f) < 1) return false; + } + else { + if (fscanf(f, "%lu\n", &sz) < 0) return false; + } + for (size_t i = 0; i < sz; i++) { + Key k; + Value v; + if (k.read(f, bin, fg) == false) return false; + if (v.read(f, bin, fg) == false) return false; + insert(k, v); + } + return true; + } + + ObjBase* create() const { + return new ObjDictionary(); + } + + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(*(ObjDictionary*)b)); + } + + char const* ctype() const { + return typeid(*this).name(); + } + + std::string type() const { + return std::string(ctype()); + } +}; + +} + +#endif // oo_ObjDictionary_H__ diff --git a/meowpp/oo/ObjProperties.h b/meowpp/oo/ObjProperties.h new file mode 100644 index 0000000..1d1ac64 --- /dev/null +++ b/meowpp/oo/ObjProperties.h @@ -0,0 +1,55 @@ +#ifndef oo_ObjProperties_H__ +#define oo_ObjProperties_H__ + +#include "ObjBase.h" + +#include <cstdlib> + +namespace meow { + +template<size_t SID> + +//! 目前擺爛中 +class ObjProperties: public ObjBase { +private: +public: + ObjProperties(); + + ObjProperties(ObjProperties const& p); + + virtual ~ObjProperties(); + + size_t propertySize() const; + + bool propertyEmpty() const; + + void propertyClear(); + + ObjBase const* property(std::string name) const; + + ObjBase* property(std::string name); + + bool propertyAdd(std::string name, ObjBase* obj, bool autoRemove); + + bool propertyDel(std::string name); + + ObjProperties& properties() const; + + ObjProperties& properties(ObjProperties const& p); + + bool write(FILE* f, bool bin, unsigned int fg) const; + + bool read(FILE* f, bool bin, unsigned int fg); + + ObjBase* create() const; + + ObjBase* copyFrom(ObjBase const* b); + + char const* ctype() const; + + std::string type() const; +}; + +} + +#endif // oo_ObjProperties_H__ diff --git a/meowpp/oo/ObjSelector.h b/meowpp/oo/ObjSelector.h index 58debcb..7664a11 100644 --- a/meowpp/oo/ObjSelector.h +++ b/meowpp/oo/ObjSelector.h @@ -3,60 +3,211 @@ #include "ObjBase.h" +#include <utility> +#include <vector> +#include <string> +#include <map> + #include <cstdlib> +#include <cstdio> namespace meow{ - template<size_t id> - class ObjSelector{ - private: - typedef std::map <std::string, ObjBase*> Funcs; - typedef std::vector<std::string > Types; - // - static Funcs& funcs(){ - static Funcs f; - return f; - } - // - std::string name; - ObjBase* ptr; - public: - ObjSelector( ObjBase* o){ add(name = o->type(), ptr = o); } - ObjSelector(std::string n, ObjBase* o){ add(name = n , ptr = o); } - ~ObjSelector(){ del(name); } - // - static void add(std::string n, ObjBase* b){ - del(n); - funcs()[n] = b; - } - static void del(std::string s){ - if(funcs().find(s) != funcs().end()){ - delete funcs()[s]; - funcs().erase(s); - } - } - static ObjBase const* access(std::string s){ - if(funcs().find(s) == funcs().end()) return NULL; - return funcs()[s]; + +/*! + * @brief 利用register的概念, 達到runtime用string選擇要new的class + * + * @author cathook + */ +template<size_t id> //!< 讓程式可以有不只一個 \c ObjSelector +class ObjSelector { +private: + struct Info { + ObjSelector* parent_; + ObjBase const* pointer_; + bool autoDelete_; + // + Info(ObjSelector* parent, + ObjBase const* ptr, + bool autoDelete) { + parent_ = parent; + pointer_ = ptr; + autoDelete_ = autoDelete; + } + ~Info() { + if (autoDelete_) { + delete pointer_; } - static ObjBase* get(std::string s){ - if(funcs().find(s) == funcs().end() || funcs()[s] == NULL) - return NULL; - return funcs()[s]->create(); + if (parent_ != NULL) { + parent_->me_.second = NULL; } - static std::string find(ObjBase* o){ - for(Funcs::iterator it = funcs().begin(); it != funcs().end(); it++) - if(it->second != NULL && it->second->type() == o->type()){ - return it->first; - } - return std::string(); + } + }; + friend struct Info; + + typedef typename std::map<std::string, Info*> Funcs; + typedef typename std::map<std::string, Info*>::iterator FuncsIterator; + + static Funcs& funcs() { + static Funcs f; + return f; + } + static Info* add(std::string name, + ObjSelector* parent, + ObjBase* ptr, + bool autoDelete) { + Info* info = new Info(parent, ptr, autoDelete); + del(name); + funcs()[name] = info; + return info; + } + + std::pair<std::string, Info*> me_; +public: + /*! + * @brief 新增(註冊) 一個Class (必須要繼承自 \c ObjBase) 並且給定其Name + */ + static void add(std::string name, ObjBase* obj, bool autoDelete) { + add(name, NULL, obj, autoDelete); + } + + /*! + * @brief 新增(註冊) 一個Class (必須要繼承自 \c ObjBase) 並且默認type為name + */ + static void add(ObjBase* obj, bool autoDelete) { + add(obj->type(), NULL, obj, autoDelete); + } + + /*! + * @brief 依照name刪除之前註冊過得Class + */ + static void del(std::string name) { + if (funcs().find(name) != funcs().end()) { + delete funcs()[name]; + funcs().erase(name); + } + } + + /*! + * @brief 取得之前註冊過得Class + */ + static ObjBase const* get(std::string name) { + if (funcs().find(name) == funcs().end()) return NULL; + return funcs()[name]->pointer_; + } + + /*! + * @brief 回傳一個之前註冊過得Class new出來的實體 + */ + static ObjBase* create(std::string name) { + ObjBase const* ptr = get(name); + if(ptr == NULL) return NULL; + return ptr->create(); + } + + /*! + * @brief 利用type檢查是否有註冊過同種類的Class + */ + static bool exist(ObjBase* obj) { + for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++) { + if (it->second->pointer_ == obj || + (it->second->pointer_ != NULL && + it->second->pointer_->type() == obj->type())) { + return true; } - static Types lst(){ - Types ret; - for(Funcs::iterator it = funcs().begin(); it != funcs().end(); it++) - ret.push_back(it->first); - return ret; + } + return false; + } + + /*! + * @brief 利用type尋找name + */ + static std::string name(ObjBase* obj) { + for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++) { + if (it->second->pointer_ == obj || + (it->second->pointer_ != NULL && + it->second->pointer_->type() == obj->type())) { + return it->first; } - }; + } + return std::string(); + } + + /*! + * @brief 回傳所有註冊過的name + */ + static std::vector<std::string> names() { + std::vector<std::string> ret; + for (FuncsIterator it = funcs().begin(); it != funcs().end(); it++) + ret.push_back(it->first); + return ret; + } + + /*! + * @brief 宣告一個ObjSelector實體, 並且註冊一個 ObjBase + */ + ObjSelector(std::string name, ObjBase* obj, bool autoDelete) { + me_.first = name; + me_.second = add(me_.first, this, obj, autoDelete); + } + + /*! + * @brief 宣告一個ObjSelector實體, 並且註冊一個 ObjBase + */ + ObjSelector(ObjBase* obj, bool autoDelete) { + me_.first = obj->type(); + me_.second = add(me_.first, this, obj, autoDelete); + } + + //! 解構子 + ~ObjSelector() { + if (me_.second != NULL) { + del(me_.first); + } + } + + /*! + * @brief 將一個物件寫到檔案裡(該物件必須要有註冊過) + */ + static bool write(FILE* f, bool binary, ObjBase* obj, unsigned int fg) { + if (!exist(obj)) return false; + char const* nme = name(obj).c_str(); + size_t len = strlen(nme); + if (binary) { + if (fwrite(&len, sizeof(size_t ), 1, f) < 1) return false; + if (fwrite(nme , sizeof(char ), len, f) < len) return false; + if (fwrite(&fg , sizeof(unsigned int), 1, f) < 1) return false; + } else { + if (fprintf(f, "%s %u\n", nme, fg) < 2) return false; + } + return obj->write(f, binary, fg); + } + + /*! + * @brief 從檔案中讀取一個物件(該物件必須要有註冊過) + */ + static ObjBase* read(FILE* f, bool binary) { + char name[2048]; + size_t len; + unsigned int fg; + if (binary) { + if (fread(&len, sizeof(size_t ), 1, f) < 1) return NULL; + if (fread(name, sizeof(char ), len, f) < len) return NULL; + if (fread(&fg , sizeof(unsigned int), 1, f) < 1) return NULL; + name[len] = '\0'; + } else { + if (fscanf(f, "%s %u", name, &fg) < 2) return NULL; + } + ObjBase* ret = create(std::string(name)); + if (ret != NULL && ret->read(f, binary, fg) == false) { + delete ret; + ret = NULL; + } + return ret; + } +}; + +static const size_t kGlobalSeletorID = 0; + } #endif // oo_ObjSelector_H__ diff --git a/meowpp/oo/ObjTypes.h b/meowpp/oo/ObjTypes.h new file mode 100644 index 0000000..4092d63 --- /dev/null +++ b/meowpp/oo/ObjTypes.h @@ -0,0 +1,202 @@ +#ifndef oo_ObjType_H__ +#define oo_ObjType_H__ + +#include "../Self.h" +#include "ObjBase.h" + +#include <cstdlib> +#include <cstdio> + +namespace meow { + +/*! + * @brief 純粹把給定的 \c Type 包起來, 變成繼承自 ObjBase + * + * @author cathook + */ +template<class Type, class ReaderWriter> +class ObjType: public ObjBase { +private: + struct Myself { + Type data_; + Myself() { + } + ~Myself() { + } + Myself copyFrom(Myself const& b) { + data_ = b.data_; + } + }; + Self<data_> const self; +public: + //! @brief constructor + ObjType(): self(true) { + } + + //! @brief constructor, 並且copy資料 + ObjType(ObjType const& a): self(false) { + self().copyFrom(a.self); + } + + //! @brief constructor, 並且給值 + ObyType(Type const& t): self(true) { + self().data_ = t; + } + + ~ObjType() { + } + + ObjType& copyFrom(ObjType const& a) { + self().copyFrom(a.self); + return *this; + } + + ObjType& referenceFrom(ObjType const& a) { + self().referenceFrom(a.self); + return *this; + } + + Type const& access() const { + return self->data_; + } + + Type& modify() { + return self()->data_; + } + + ObjType& operator=(ObjType const& a) { + return copyFrom(a); + } + + Type const& operator()() const { + return access(); + } + + Type& operator()() { + return modify(); + } + + bool write(FILE* f, bool bin, unsigned int fg) const { + return ReaderWriter::write(f, bin, fg, self->data_); + } + + bool read(FILE* f, bool bin, unsigned int fg) { + return ReaderWriter::read(f, bin, fg, &(self()->data_)); + } + + ObjBase* create() const { + return new ObjType(); + } + + ObjBase* copyFrom(ObjBase const* b) { + return &(copyFrom(&(ObjType const*)b)); + } + + char const* ctype() const { + static char const* ptr = typeid(*this).name(); + return ptr; + } + + std::string type() const { + return std::string(ctype()); + } +}; + +class ReaderWriter_int { +public: + static bool write(FILE* f, bool bin, unsigned int fg, int const& k) { + if (bin) { + return (fwrite(&k, sizeof(k), 1, f) == 1); + } + else { + return (fprintf(f, "%d\n", k) == 1); + } + } + static bool read(FILE* f, bool bin, unsigned int fg, int* k) { + if (bin) { + return (fread(k, sizeof(k), 1, f) == 1); + } + else { + return (fscanf(f, "%d", k) == 1); + } + } +}; + +class ReaderWriter_size_t { +public: + static bool write(FILE* f, bool bin, unsigned int fg, size_t const& k) { + if (bin) { + return (fwrite(&k, sizeof(k), 1, f) == 1); + } + else { + return (fprintf(f, "%lu\n", k) == 1); + } + } + static bool read(FILE* f, bool bin, unsigned int fg, size_t* k) { + if (bin) { + return (fread(k, sizeof(k), 1, f) == 1); + } + else { + return (fscanf(f, "%lu", k) == 1); + } + } +}; + +class ReaderWriter_double { +public: + static bool write(FILE* f, bool bin, unsigned int fg, double const& k) { + if (bin) { + return (fwrite(&k, sizeof(k), 1, f) == 1); + } + else { + return (fprintf(f, "%.15f\n", k) == 1); + } + } + static bool read(FILE* f, bool bin, unsigned int fg, double* k) { + if (bin) { + return (fread(k, sizeof(k), 1, f) == 1); + } + else { + return (fscanf(f, "%lf", k) == 1); + } + } +}; + +class ReaderWriter_string { +public: + static bool write(FILE* f, bool bin, unsigned int fg, std::string const& k) { + size_t len = k.size(); + char const* buf = k.c_str(); + if (bin) { + if (fwrite(&len, sizeof(len) , 1, f) < 1) return false; + if (fwrite( buf, sizeof(char), len, f) < len) return false; + } + else { + if (fprintf(f, "%s\n", buf) < 1) return false; + } + return true; + } + static bool read(FILE* f, bool bin, unsigned int fg, std::string* k) { + size_t len; + char buf[2048]; + if (bin) { + if (fread(&len, sizeof(len) , 1, f) < 1) return false; + if (fread( buf, sizeof(char), len, f) < len) return false; + buf[len] = '\0'; + } + else { + if (fscanf(f, "%s", buf) < 1) return false; + } + (*k) = buf; + return true; + } +}; + +typedef ObjType<int , ReaderWriter_int > ObjInt; +typedef ObjType<size_t , ReaderWriter_size_t> ObjSizeT; +typedef ObjType<double , ReaderWriter_double> ObjDouble; +typedef ObjType<std::string, ReaderWriter_string> ObjString; + +} + +#endif // oo_ObjType_H__ diff --git a/meowpp/utility.h b/meowpp/utility.h index 14b7af1..b430dd8 100644 --- a/meowpp/utility.h +++ b/meowpp/utility.h @@ -2,74 +2,180 @@ #define utility_H__ #include <cstdlib> +#include <cstring> +#include <cstdio> +#include <cstdarg> #include <string> -namespace meow{ - //# - //# === meow:: *Functios* in utility.h - //# - //# [options="header",width="100%",cols="1>s,5<,1<,10<",grid="rows"] - //# |============================================================== - //# |Name | Parameters | Return_Type | Description - - - //# |stringPrintf |(char const * `fmt`, ...) | std::string - //# |Format print to C++ string and return it - inline std::string stringPrintf(char const * fmt, ...); - - - //# |stringReplace |(std::string `str`,\std::string const& - //# `from`,\std::string const& `to`) | std::string - //# |Return a string like `str`, but all `from` be replaced by `to` - inline std::string stringReplace(std::string str, - std::string const& from, - std::string const& to); - - - //# |cstringEndWith |(char const* `str`,\int `n`, ...) | bool - //# |Return whether `str` is end with one of the c-string you specify in - //# the parameters or not - inline bool cstringEndWith(char const* str, int n, ...); - - - //# |debugPrintf |(char const* `fmt`, ...) | void - //# |Print debug message (file name, line number, ...etc) when `DEBUG` is - //# defined -#define debugPrintf(...) \ - meow::debugPrintf_(\ - __FILE__,\ - __PRETTY_FUNCTION__,\ - __LINE__,\ - meow::stringPrintf(__VA_ARGS__).c_str()) - inline void debugPrintf_(char const* file, - char const* func, - size_t line, - char const* msg); - - - //# |messagePrintf |(int `level_change`,\char const* `fmt`, ...) | void - //# |階層式的訊息輸出 - inline void messagePrintf(int level_change, char const* fmt, ...); +namespace meow { - - //# |filenameCompare |(std::string const& `f1`, std::string const& `f2`)|void - //# | 依照 `a0.txt < a1.txt < a2.txt < a10.txt` 的字串比較方法比較字串 - inline bool filenameCompare(std::string const& f1, std::string const& f2); - - - //# |============================================================== - - //# - //# [NOTE] - //# ==================================== - //# * `stringReplace()` 不是用什麼好方法寫的因此執行效率很低請別虐待它. - //# ==================================== - //# - //# ''' - //# +/*! + * @brief 類似C的printf, 不過是將格式化的字串丟到 \c std::string 裡回傳 + * + * @param [in] fmt,... 同printf + * @return 一個 \c std::string + * @warning 目前格式化字串最長只支援8191個字元 + */ +inline std::string stringPrintf(char const * fmt, ...){ + char str[8192]; + va_list args; + va_start(args, fmt); + vsnprintf(str, 8192, fmt, args); + va_end(args); + return std::string(str); } -#include "utility.hpp" +/*! + * @brief 將輸入字串中的某個pattern取代成另一個pattern + * + * @param [in] str 輸入的字串 + * @param [in] from 要被取代的pattern + * @param [in] to 將要取代的pattern + * @return 取代後的字串 + * @warning 有礙於目前實作方法很低級暴力, 時間複雜度神高 + */ +inline std::string stringReplace(std::string str, + std::string const& from, + std::string const& to){ + std::string out = str; + int len = from.length(); + for(size_t pos; (pos = out.find(from)) != std::string::npos; ){ + out.replace(pos, len, to); + } + return out; +} + +/*! + * @brief 檢查給定字串的結尾是否符合給定的數個patterns中的一個 + * + * @param [in] str 愈檢查的字串 + * @param [in] n pattern數 + * @param [in] ... 各種pattern + * @return \c true/false 表示 \b 是否有符合 + * @note 參數中所有的字串都是用\c cstring 來傳遞, + * 也就是 \c char \c const* 型態 + */ +inline bool cstringEndWith(char const* str, int n, ...){ + int len = strlen(str); + va_list args; + va_start(args, n); + for(int i = 0; i < n; i++){ + char const* arg = va_arg(args, char const*); + int arglen = strlen(arg); + if(arglen <= len && strcmp(str + len - arglen, arg) == 0){ + return true; + } + } + va_end(args); + return false; +} + +/*! + * @brief 若DEBUG有被define過, 將字串印到stderr, 並且附上檔名行號與所在函數名 + * + * @param [in] str 要輸出的字串, 必須是c string, 即 \c char \c const* 型態 + * @return 無 + * @note 這是一個 \b macro + */ +#define debugPrintf(str) \ +debugPrintf_(\ + __FILE__,\ + __FUNCTION__,\ + __LINE__,\ + str) +inline void debugPrintf_(char const* file, + char const* func, + size_t line, + char const* msg){ +#ifdef DEBUG + fprintf(stderr, "%s[%d] %s >> %s", file, line, func, msg); +#endif // DEBUG +} + +/*! + * @brief 階層式輸出 + * + * 像是printf, 但多了一個 \b 巢狀 的概念, 例如: + * + * @code + * message1(level = 0) + * message2(level = 1) + * information1(level = 2) + * information2(level = 2) + * ... ok(for message2) + * message3(level = 1) ... ok + * information3(level = 1) + * message4(level = 1) + * message5(level = 2) ... ok + * message6(level = 2) ... ok + * information4(level = 2) + * ... ok(for message4) + * ... ok(for message5) + * @endcode + * @param [in] level_change 分以下三種情況: + * - == 0, 只是印出一個information + * - == 1, 印出一個message, 並且level++ + * - == -1, 表示此訊息相對應最近一次level++的那個message + * @param [in] fmt, ... 跟printf一樣 + * @return 無 + */ +inline void messagePrintf(int level_change, char const* fmt, ...){ + static int level = 0; + static int last_level = -5; + char str[8192]; + va_list args; + va_start(args, fmt); + vsnprintf(str, 8192, fmt, args); + va_end(args); + if(last_level == 1 && level_change == -1){ + printf(" ...%s\n", str); + }else{ + if(last_level == 1) printf("\n"); + int level2 = level + (level_change == -1 ? -1 : 0); + for(int i = 0; i < level2; i++) printf("| "); + printf("%s%s", (level_change == -1 ? "..." : ""), str); + if(level_change != 1) printf("\n"); + } + level += level_change; + last_level = level_change; + fflush(stdout); +} + +/*! + * @brief 將兩個字串用人類習慣的檔名排序方式排序 + * + * 例如 a1 \< a2 \< a3 \< a10 \< a12 \< a20, + * 而不是 a1 \< a10 \< a12 \< a2 \< a20 \< a3 + * + * @param [in] f1 第一個字串 + * @param [in] f2 第二個字串 + * @return \c true/false 表 \b f1是否該排在f2前面 + */ +inline bool filenameCompare(std::string const& f1, std::string const& f2){ + char const* s1 = f1.c_str(); + char const* s2 = f2.c_str(); + int l1 = f1.length(); + int l2 = f2.length(); + int i1, i2; + for(i1 = i2 = 0; i1 < l1 || i2 < l2; i1++, i2++){ + if(isdigit(s1[i1]) && isdigit(s2[i2])){ + int n1 = atoi(s1 + i1); + int n2 = atoi(s2 + i2); + if(n1 != n2){ + return (n1 < n2); + } + while(i1 + 1 < l1 && isdigit(s1[i1 + 1])) i1++; + while(i2 + 1 < l2 && isdigit(s2[i2 + 1])) i2++; + }else{ + if(s1[i1] != s2[i2]){ + return s1[i1] < s2[i2]; + } + } + } + return false; +} + +} // meow #endif // utility_H__ |