aboutsummaryrefslogtreecommitdiffstats
path: root/meowpp
diff options
context:
space:
mode:
authorcathook <b01902109@csie.ntu.edu.tw>2014-06-01 13:56:57 +0800
committercathook <b01902109@csie.ntu.edu.tw>2014-06-01 13:56:57 +0800
commitd5052f1c296dddf51b3e83d59bf3e3c1952cb2d0 (patch)
tree16f7920c5079e0aefcf9509d2dbab59c464d42bd /meowpp
parentbd58f63900410ec4764031f2e6de2d75e91434b3 (diff)
downloadmeow-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')
-rw-r--r--meowpp/!readme.asciidoc20
-rw-r--r--meowpp/Self.h230
-rw-r--r--meowpp/Usage.h601
-rw-r--r--meowpp/colors/!readme.asciidoc78
-rw-r--r--meowpp/colors/Color3_Space.h78
-rw-r--r--meowpp/colors/HSL_Space.h188
-rw-r--r--meowpp/colors/HSV_Space.h188
-rw-r--r--meowpp/colors/RGB_Space.h168
-rw-r--r--meowpp/colors/YUV_Space.h131
-rw-r--r--meowpp/dsa/!readme.asciidoc57
-rw-r--r--meowpp/dsa/BinaryIndexTree.h155
-rw-r--r--meowpp/dsa/DisjointSet.h192
-rw-r--r--meowpp/dsa/HashTable.h217
-rw-r--r--meowpp/dsa/KD_Tree.h425
-rw-r--r--meowpp/dsa/MergeableHeap.h260
-rw-r--r--meowpp/dsa/SegmentTree.h274
-rw-r--r--meowpp/dsa/SplayTree.h1366
-rw-r--r--meowpp/dsa/VP_Tree.h461
-rw-r--r--meowpp/geo/!readme.asciidoc12
-rw-r--r--meowpp/geo/Vectors.h510
-rw-r--r--meowpp/gra/Bitmap.h411
-rw-r--r--meowpp/gra/Camera.h544
-rw-r--r--meowpp/gra/FeaturePoint.h193
-rw-r--r--meowpp/gra/FeaturePointsDetector.h26
-rw-r--r--meowpp/gra/FeaturePointsDetector_Harris.h356
-rw-r--r--meowpp/gra/FeaturePointsMatch.h63
-rw-r--r--meowpp/gra/FeaturePointsMatch_K_Match.h190
-rw-r--r--meowpp/gra/IdentityPoints.h279
-rw-r--r--meowpp/gra/Photo.h407
-rw-r--r--meowpp/gra/ViewPort.h19
-rw-r--r--meowpp/gra/WatchBall.h255
-rw-r--r--meowpp/math/!readme.asciidoc73
-rw-r--r--meowpp/math/LinearTransformation.h116
-rw-r--r--meowpp/math/LinearTransformations.h424
-rw-r--r--meowpp/math/Matrix.h536
-rw-r--r--meowpp/math/Transformation.h272
-rw-r--r--meowpp/math/Transformations.h581
-rw-r--r--meowpp/math/Vector.h276
-rw-r--r--meowpp/math/methods.h181
-rw-r--r--meowpp/math/utility.h197
-rw-r--r--meowpp/oo/!readme.asciidoc32
-rw-r--r--meowpp/oo/ObjArray.h166
-rw-r--r--meowpp/oo/ObjBase.h58
-rw-r--r--meowpp/oo/ObjDictionary.h158
-rw-r--r--meowpp/oo/ObjProperties.h55
-rw-r--r--meowpp/oo/ObjSelector.h245
-rw-r--r--meowpp/oo/ObjTypes.h202
-rw-r--r--meowpp/utility.h232
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__