diff --git a/include/aos.hpp b/include/aos.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d34f9ec9800af6b0ccdbec8357d04f126812b6ad
--- /dev/null
+++ b/include/aos.hpp
@@ -0,0 +1,701 @@
+#ifndef TRAVEL_OSEG_H
+#define TRAVEL_OSEG_H
+//
+// Created by Minho Oh & Euigon Jung on 1/31/22.
+// We really appreciate Hyungtae Lim and Prof. Hyun Myung! :)
+//
+#pragma once
+#include <vector>
+#include <algorithm>
+#include <iostream>
+#include <list>
+#include <numeric>
+#include <random>
+#include <chrono>
+#include <forward_list>
+#include <boost/optional.hpp>
+
+#include <ros/ros.h>
+#include <pcl/point_cloud.h>
+#include <pcl/point_types.h>
+#include <pcl/common/io.h>
+
+using namespace std;
+
+namespace travel {
+
+    class AOSNode {
+        public:
+            uint start;
+            uint end;
+            int label;
+
+            AOSNode() : label(-1) {} 
+    };
+
+    class Point {
+        public:
+            float x,y,z;
+            uint idx;
+            bool valid = false;
+            int label;
+    };
+
+    template <typename T>
+    class ObjectCluster {
+        private:
+            uint16_t max_label_;
+            vector<forward_list<Point*>> clusters_;
+            forward_list<Point*> empty_;
+            vector<int> valid_cnt_;
+            vector<vector<AOSNode>> nodes_;
+            vector<float> vert_angles_;
+            vector<vector<Point>> range_mat_;
+            vector<vector<Point>> emptyrange_mat_;
+
+            // parameters
+            int VERT_SCAN;
+            int HORZ_SCAN;
+            float MIN_RANGE;
+            float MAX_RANGE;
+            float HORZ_MERGE_THRES;
+            float VERT_MERGE_THRES;
+            int VERT_SCAN_SIZE;
+            int HORZ_SCAN_SIZE;
+            int HORZ_SKIP_SIZE;
+            int HORZ_EXTENSION_SIZE;
+            int DOWNSAMPLE;
+            float MIN_VERT_ANGLE;
+            float MAX_VERT_ANGLE;
+            boost::optional<int> MIN_CLUSTER_SIZE;
+            boost::optional<int> MAX_CLUSTER_SIZE;
+
+            // test
+            // vector<vector<vector<std::pair<float, float>>>> point_angle_;
+
+        public:
+            ObjectCluster() {
+                empty_ = {};
+            }
+
+            ~ObjectCluster() {}
+
+            void setParams(int _vert_scan, int _horz_scan, float _min_range, float _max_range,
+                            float _min_vert_angle, float _max_vert_angle,
+                            float _horz_merge_thres, float _vert_merge_thres, int _vert_scan_size,
+                            int _horz_scan_size, int _horz_extension_size, int _horz_skip_size,
+                            int _downsample,
+                            boost::optional<int> _min_cluster_size=boost::none, 
+                            boost::optional<int> _max_cluster_size=boost::none) {
+                            
+                std::cout<<""<<std::endl;
+                ROS_WARN("Set ObjectSeg Parameters");
+
+                VERT_SCAN = _vert_scan;
+                ROS_WARN("vert scan: %d", VERT_SCAN);
+                
+                HORZ_SCAN = _horz_scan;
+                ROS_WARN("horz scan: %d", HORZ_SCAN);
+                
+                MIN_RANGE = _min_range;
+                ROS_WARN("min range: %f", MIN_RANGE);
+
+                MAX_RANGE = _max_range;
+                ROS_WARN("max range: %f", MAX_RANGE);
+                
+                HORZ_MERGE_THRES = _horz_merge_thres;
+                ROS_WARN("horz merge thres: %f", HORZ_MERGE_THRES);
+                
+                VERT_MERGE_THRES = _vert_merge_thres;
+                ROS_WARN("vert merge thres: %f", VERT_MERGE_THRES);
+
+                VERT_SCAN_SIZE = _vert_scan_size;
+                ROS_WARN("vert scan size: %d", VERT_SCAN_SIZE);
+                
+                HORZ_SKIP_SIZE = _horz_skip_size;
+                ROS_WARN("horz skip size: %d", HORZ_SKIP_SIZE);
+                
+                HORZ_SCAN_SIZE = _horz_scan_size;
+                ROS_WARN("horz scan size: %d", HORZ_SCAN_SIZE);
+
+                HORZ_EXTENSION_SIZE = _horz_extension_size;
+                ROS_WARN("horz extension size: %d", HORZ_EXTENSION_SIZE);
+
+                DOWNSAMPLE = _downsample;
+                ROS_WARN("downsample: %d", DOWNSAMPLE);
+                
+                MAX_VERT_ANGLE = _max_vert_angle; 
+                MIN_VERT_ANGLE = _min_vert_angle;
+                ROS_WARN("Max vertical angle : %f", MAX_VERT_ANGLE);
+                ROS_WARN("Min vertical angle : %f", MIN_VERT_ANGLE);
+
+                float resolution = (float) (MAX_VERT_ANGLE - MIN_VERT_ANGLE) / (float)(VERT_SCAN-1);
+                ROS_WARN("Vertical resolution: %f", resolution);
+
+
+                for(int i = 0; i < _vert_scan; i++)
+                    vert_angles_.push_back(_min_vert_angle + i*resolution);
+
+                range_mat_.resize(VERT_SCAN, vector<Point>(HORZ_SCAN));
+
+                valid_cnt_.resize(VERT_SCAN, 0);
+                nodes_.resize(VERT_SCAN, vector<AOSNode>());
+
+                if (_min_cluster_size)
+                    MIN_CLUSTER_SIZE = _min_cluster_size;
+            
+                if (_max_cluster_size)
+                    MAX_CLUSTER_SIZE = _max_cluster_size;
+            }
+
+            void segmentObjects(boost::shared_ptr<pcl::PointCloud<T>> cloud_in, 
+                            boost::shared_ptr<pcl::PointCloud<T>> cloud_out, 
+                            vector<float> *vert_angles=nullptr) {
+                // 0. reset
+                max_label_ = 1;
+                clusters_.clear();
+                clusters_.push_back(empty_);
+                clusters_.push_back(empty_);
+                std::fill(valid_cnt_.begin(), valid_cnt_.end(), 0);
+                std::for_each(range_mat_.begin(), range_mat_.end(), [](vector<Point>& inner_vec) {
+                    std::fill(inner_vec.begin(), inner_vec.end(), Point());
+                });
+                // 1. do spherical projection
+                auto start = chrono::steady_clock::now();
+                sphericalProjection(cloud_in);
+                auto end = chrono::steady_clock::now();
+                // cloud_out->points.resize(cloud_in->points.size());
+                printf("Creating range map: %ld ms\n", chrono::duration_cast<chrono::milliseconds>(end-start).count());
+
+                // 2. begin clustering
+                start = chrono::steady_clock::now();
+                for (int channel = 0; channel < VERT_SCAN; channel++) {
+                    auto horz_start = chrono::steady_clock::now();
+                    horizontalUpdate(channel);
+                    auto horz_end = chrono::steady_clock::now();
+                    auto vert_start = chrono::steady_clock::now();
+                    verticalUpdate(channel);
+                    auto vert_end = chrono::steady_clock::now();
+                    // printf("Channel %d: pt count: %d, node count: %d, horz: %ld ms, vert: %ld ms\n", channel, 
+                    //     valid_cnt_[channel], nodes_[channel].size(),
+                    //     chrono::duration_cast<chrono::milliseconds>(horz_end-horz_start).count(),
+                    //     chrono::duration_cast<chrono::milliseconds>(vert_end-vert_start).count());
+                }
+                end = chrono::steady_clock::now();
+                printf("Processing clustering: %ld ms\n", chrono::duration_cast<chrono::milliseconds>(end-start).count());
+
+                // 3. post-processing (clusters -> labels)
+                start = chrono::steady_clock::now();
+                labelPointcloud(cloud_in, cloud_out);
+                end = chrono::steady_clock::now();
+                printf("Post-processing: %ld ms\n", chrono::duration_cast<chrono::milliseconds>(end-start).count());
+            }
+
+            void sphericalProjection(boost::shared_ptr<pcl::PointCloud<T>> cloud_in) {
+                boost::shared_ptr<pcl::PointCloud<T>> valid_cloud = boost::make_shared<pcl::PointCloud<T>>();
+                valid_cloud->points.reserve(cloud_in->points.size());
+                int ring_idx = -1, row_idx = -1, col_idx = -1;
+                float range;
+                Point point;
+                int range_chcker = 0;
+                int valid_checker = 0;
+                int count_range, count_downsample, count_out_of_row, count_out_of_col, count_out_of_mat;
+                count_range = count_downsample = count_out_of_row = count_out_of_col = count_out_of_mat = 0;
+                int count_pass = 0;
+                for (size_t i = 0; i < cloud_in->points.size(); i++) {
+                    
+                    range = getRange(cloud_in->points[i]);
+                    if (range < MIN_RANGE || range > MAX_RANGE){
+                        count_range++;
+                        continue;
+                    }
+                    row_idx = getRowIdx(cloud_in->points[i]);
+                    if (row_idx % DOWNSAMPLE != 0) {
+                        count_downsample++;
+                        continue;
+                    }
+                    if (row_idx < 0 || row_idx >= VERT_SCAN) {
+                        count_out_of_row++;
+                        continue;
+                    }
+                    col_idx = getColIdx(cloud_in->points[i]);
+                    if (col_idx < 0 || col_idx >= HORZ_SCAN) {
+                        count_out_of_col++;
+                        continue;
+                    }
+                    if (range_mat_[row_idx][col_idx].valid){
+                        count_out_of_mat ++;
+                        continue;
+                    } else {
+                        count_pass++;
+                        point.x = cloud_in->points[i].x;
+                        point.y = cloud_in->points[i].y;
+                        point.z = cloud_in->points[i].z;
+                        point.valid = true;
+                        point.idx = valid_cloud->points.size();
+                        valid_cloud->points.push_back(cloud_in->points[i]);
+                        range_mat_[row_idx][col_idx] = point;
+                        assert(range_mat_[row_idx][col_idx].valid == true);
+                        valid_cnt_[row_idx]++;
+                    }
+                }
+                printf("Input cloud size: %d\n", (int)cloud_in->points.size());
+                printf("Projected cloud size: %d\n", (int)valid_cloud->points.size());
+                *cloud_in = *valid_cloud;
+            }
+
+            void labelPointcloud(boost::shared_ptr<pcl::PointCloud<T>> cloud_in,
+                                boost::shared_ptr<pcl::PointCloud<T>> cloud_out) {
+                uint cnt = 0;
+                uint pt_cnt = 0;
+                uint max = 2000;
+                cloud_out->points.reserve(cloud_in->points.size());
+
+                // generate random number
+                std::vector<size_t> valid_indices;
+                for (size_t i = 0; i < clusters_.size(); i++) {
+                    if (std::distance(clusters_[i].begin(), clusters_[i].end()) > 0) {
+                        valid_indices.push_back(i);
+                        cnt++;
+                    }
+                }
+                std::list<uint16_t> l(cnt);
+                std::iota(l.begin(), l.end(), 1);
+                std::vector<std::list<uint16_t>::iterator> v(l.size());
+                std::iota(v.begin(), v.end(), l.begin());
+                std::shuffle(v.begin(), v.end(), std::mt19937{std::random_device{}()}); 
+
+                for (size_t i = 0; i < valid_indices.size(); i++) {
+                    uint16_t label = *v[i];
+                    size_t idx = valid_indices[i];
+                    int point_size = std::distance(clusters_[idx].begin(), clusters_[idx].end());             
+                    if (MIN_CLUSTER_SIZE && MAX_CLUSTER_SIZE) {
+                        if (point_size < MIN_CLUSTER_SIZE || point_size > MAX_CLUSTER_SIZE)
+                            continue;
+                    }
+
+                    for (auto &p : clusters_[idx]) {
+                        if (p->valid) {
+                            assert(cloud_in->points[p->idx].x == p->x);
+                            assert(cloud_in->points[p->idx].y == p->y);
+                            assert(cloud_in->points[p->idx].z == p->z);
+                            T point = cloud_in->points[p->idx];
+                            point.intensity = label;
+                            cloud_out->points.push_back(point);
+                            pt_cnt++;
+                        } else {
+                            ROS_ERROR("point invalid");
+                        }
+                    }
+                }
+                // assert(cloud_in->points.size() == cloud_out->points.size());
+                printf("# of cluster: %d, point: %d\n", cnt, pt_cnt);
+            }
+            
+            void horizontalUpdate(int channel) {
+                nodes_[channel].clear();
+                // no point in the channel
+                if (valid_cnt_[channel] <= 0){
+                    return;
+                } 
+
+                int first_valid_idx = -1;
+                int last_valid_idx = -1;
+                int start_pos = -1;
+                int pre_pos = -1;
+                int end_pos = -1;
+                
+                AOSNode node;
+                bool first_node = false;
+                for(int j = 0; j < HORZ_SCAN; j++) {
+                    if (!range_mat_[channel][j].valid) 
+                        continue;
+                    if (!first_node) {
+                        first_node = true;
+                        // update index
+                        first_valid_idx = j;
+                        start_pos = j;
+                        pre_pos = j;
+                        // update label
+                        max_label_++;
+                        clusters_.push_back(empty_);
+                        range_mat_[channel][j].label = max_label_;
+                        // push a new node
+                        node.start = start_pos;
+                        node.end = j;
+                        node.label = range_mat_[channel][j].label;
+                        nodes_[channel].push_back(node);
+                        clusters_[node.label].insert_after(clusters_[node.label].cbefore_begin(), &range_mat_[channel][j]);
+                    } else {
+                        auto &cur_pt = range_mat_[channel][j];
+                        auto &pre_pt = range_mat_[channel][pre_pos]; 
+                        if (pointDistance(cur_pt, pre_pt) < HORZ_MERGE_THRES) {
+                            // update existing node
+                            pre_pos = j; 
+                            cur_pt.label = pre_pt.label;
+                            nodes_[channel].back().end = j;
+                        } else { 
+                            // update index
+                            start_pos = j;
+                            pre_pos = j;
+                            // update label
+                            max_label_++;
+                            clusters_.push_back(empty_);
+                            cur_pt.label = max_label_;
+                            // push new node
+                            node.start = start_pos;
+                            node.end = j;
+                            node.label = range_mat_[channel][j].label;
+                            nodes_[channel].push_back(node);
+                            assert(range_mat_[channel][j].label == cur_pt.label);
+                        }
+                        clusters_[cur_pt.label].insert_after(clusters_[cur_pt.label].cbefore_begin(), &range_mat_[channel][j]);
+                    }
+                }
+                last_valid_idx = pre_pos;
+
+                // merge last and first points
+                if (nodes_[channel].size() > 2) {
+                    auto &p_0 = range_mat_[channel][first_valid_idx];
+                    auto &p_l = range_mat_[channel][last_valid_idx];
+                    if (pointDistance(p_0, p_l) < HORZ_MERGE_THRES) {
+                        if (p_0.label == 0) 
+                            printf("Ring merge to label 0\n");
+                        if (p_0.label != p_l.label) {
+                            nodes_[channel].back().label = p_0.label;
+                            mergeClusters(p_l.label, p_0.label);
+                        }
+                    }
+                }
+
+                // merge skipped nodes due to occlusion
+                if (nodes_[channel].size() > 2) {
+                    uint16_t cur_label = 0, target_label = 0;
+                    for (size_t i = 0; i < nodes_[channel].size()-1; i++){
+                        for (size_t j = i+1; j < nodes_[channel].size(); j++) {
+                            auto &node_i = nodes_[channel][i];
+                            auto &node_j = nodes_[channel][j];
+                            
+                            if (node_i.label == node_j.label)
+                                continue;
+
+                            int end_idx = node_i.end;
+                            int start_idx = node_j.start;
+                            float dist = pointDistance(range_mat_[channel][end_idx], range_mat_[channel][start_idx]);
+                            if (dist < HORZ_MERGE_THRES) {
+                                if (node_i.label > node_j.label) {
+                                    target_label = node_j.label;
+                                    cur_label = node_i.label;
+                                    node_i.label = target_label;
+                                } else {
+                                    target_label = node_i.label;
+                                    cur_label = node_j.label;
+                                    node_j.label = target_label;
+                                }
+                                mergeClusters(cur_label, target_label);
+                                // if (DEBUG)
+                                //     printf("merge two labels despite occlusion: %d %d\n", cur_label, target_label);
+                            }
+                            if (j-i >= HORZ_SKIP_SIZE)
+                                break; 
+                        }
+                    }
+                }   
+            }
+            
+            void verticalUpdate(int channel) {
+                // Iterate each point of this channel to update the labels.
+                int point_size = valid_cnt_[channel];
+                // Current scan line is emtpy, do nothing.
+                if(point_size == 0) return;
+                // Skip first scan line
+                if(channel == 0) return;
+
+                int prev_channel = channel - 1;
+
+                for(int n = 0; n < nodes_[channel].size(); n++) {
+                    
+                    auto &cur_node = nodes_[channel][n];
+
+                    for (int l = prev_channel; l >= channel - VERT_SCAN_SIZE; l -= 1) {
+                        
+                        if (l < 0) // out of range
+                            break;
+
+                        if (valid_cnt_[l] == 0) {
+                            // if (DEBUG)
+                            //     printf("No valid points in the channel\n");
+                            continue;
+                        }
+
+                        // binary search lower bound
+                        // lower_bnd inclusive
+                        int N = nodes_[l].size();
+                        int first = 0;
+                        int last = N - 1;
+                        int lower_bnd = 0;
+                        int mid = 0;
+                        
+                        while (first <= last) {
+                            mid = (first + last) / 2;
+                            auto &prev_node = nodes_[l][mid];
+                            if (overlap(cur_node, prev_node) || cur_node.end < prev_node.start){
+                                lower_bnd = mid;
+                                last = mid - 1;
+                            } else {
+                                first = mid + 1;
+                            }
+                        } 
+
+                        // binary search upper bound
+                        // exclusive but gives -1 if end of list
+                        first = 0;
+                        last = N - 1;
+                        mid = 0;
+                        int upper_bnd = 0;
+
+                        while (first <= last) {
+                            mid = (first + last) / 2;
+                            auto &prev_node = nodes_[l][mid];
+                            if (overlap(cur_node, prev_node) || prev_node.end < cur_node.start) {
+                                upper_bnd = mid;
+                                first = mid + 1;
+                            } else {
+                                last = mid - 1;
+                            }
+                        }
+                        upper_bnd = upper_bnd + 1;
+
+                        // loop through overlapped nodes
+                        for (size_t idx = lower_bnd; idx < upper_bnd; idx++) {
+
+                            auto &ovl_node = nodes_[l][idx];
+
+                            if (ovl_node.label == cur_node.label) {
+                                // if (DEBUG)
+                                //     printf("same label: %d, %d\n", ovl_node.label, cur_node.label);
+                                continue;
+                            }
+
+                            // if (DEBUG)
+                            //     printf("overlapped: %d, %d\n", ovl_node.start, ovl_node.end);
+
+                            int iter_start_idx = -1;
+                            int iter_end_idx = -1;
+
+                            if (ovl_node.start <= cur_node.start && cur_node.end <= ovl_node.end) { 
+                                // cur_node inside prev_node
+                                iter_start_idx = cur_node.start;
+                                iter_end_idx = cur_node.end;
+                            } else if (cur_node.start <= ovl_node.start && ovl_node.end <= cur_node.end) {
+                                // prev_node inside cur_node 
+                                iter_start_idx = ovl_node.start;
+                                iter_end_idx = ovl_node.end;
+                            } else if (cur_node.start < ovl_node.start && cur_node.end >= ovl_node.start && cur_node.end <= ovl_node.end) {
+                                // tail of cur_node with head of prev_node 
+                                iter_start_idx = ovl_node.start;
+                                iter_end_idx = cur_node.end;
+                            } else if (ovl_node.start <= cur_node.start && cur_node.start <= ovl_node.end && cur_node.end > ovl_node.end) {
+                                // head of cur_node with tail of prev_node
+                                iter_start_idx = cur_node.start;
+                                iter_end_idx = ovl_node.end;
+                            } else {
+                                // overlapped within search window size, use euclidean distance directly
+                                if (ovl_node.end < cur_node.start) {
+                                    if (nodeDistance(cur_node, ovl_node, range_mat_[channel][cur_node.start], range_mat_[l][ovl_node.end])) {
+                                        // if (DEBUG) {
+                                        //     printf("Merge by euclidean distance: cur: %f;%f;%f, prev: %f;%f;%f\n",
+                                        //             range_mat_[channel][cur_node.start].x, range_mat_[channel][cur_node.start].y, range_mat_[channel][cur_node.start].z,
+                                        //             range_mat_[l][ovl_node.end].x, range_mat_[l][ovl_node.end].y, range_mat_[l][ovl_node.end].z);
+                                        // }
+                                    }
+                                } else if (cur_node.end < ovl_node.start) {
+                                    if (nodeDistance(cur_node, ovl_node, range_mat_[channel][cur_node.end], range_mat_[l][ovl_node.start])) {
+                                        // if (DEBUG) {
+                                        //     printf("Merge by euclidean distance: cur: %f;%f;%f, prev: %f;%f;%f\n",
+                                        //             range_mat_[channel][cur_node.end].x, range_mat_[channel][cur_node.end].y, range_mat_[channel][cur_node.end].z,
+                                        //             range_mat_[l][ovl_node.start].x, range_mat_[l][ovl_node.start].y, range_mat_[l][ovl_node.start].z);
+                                        // }
+                                    }
+                                }
+                                continue;
+                            }
+                            
+                            // if (DEBUG)
+                            //     printf("overlapping: %d %d\n", iter_start_idx, iter_end_idx);
+
+                            // iterate through overlapping indices
+                            uint16_t cur_label = 0, target_label = 0;                
+                            bool merged = false;
+                            int cur_start_left = iter_start_idx;
+                            int cur_start_right = iter_start_idx;
+                            int cur_end_left = iter_end_idx;
+                            int cur_end_right = iter_end_idx;
+
+                            while (1) {
+                                if (cur_start_right > cur_end_left && cur_start_left < iter_start_idx - HORZ_SCAN_SIZE && cur_end_right > iter_end_idx + HORZ_SCAN_SIZE) // end of search
+                                    break;
+                                if (mergeNodes(cur_node, ovl_node, channel, l, cur_start_left))
+                                    break;
+                                if (cur_start_left != cur_end_left) { // more than one overlapping cur_node point 
+                                    if (mergeNodes(cur_node, ovl_node, channel, l, cur_end_left))
+                                        break;
+                                }
+                                if (cur_start_left != cur_start_right) { // not the first iteration
+                                    if (mergeNodes(cur_node, ovl_node, channel, l, cur_start_right))
+                                        break;
+                                }
+                                if (cur_end_left != cur_end_right) { // not the first iteration
+                                    if (mergeNodes(cur_node, ovl_node, channel, l, cur_end_right))
+                                        break;
+                                }
+                                cur_start_left--;
+                                cur_start_right++;
+                                cur_end_left--;
+                                cur_end_right++;
+                            }
+                        }
+                    }
+                }
+            }
+            
+            bool overlap(AOSNode &first_node, AOSNode &second_node) {
+                int new_start = first_node.start - HORZ_EXTENSION_SIZE;
+                int new_end = first_node.end + HORZ_EXTENSION_SIZE;
+                if (new_start <= second_node.start && second_node.start <= new_end) 
+                    return true;
+                else if (new_start <= second_node.end && second_node.end <= new_end)
+                    return true;
+                else if (second_node.start <= new_start && new_end <= second_node.end)
+                    return true;
+                return false;
+            }
+
+            bool mergeNodes(AOSNode &first_node, AOSNode &second_node, int cur_channel, int prev_channel, int query_idx) {
+                if (query_idx >= first_node.start && query_idx <= first_node.end) {
+                    if (range_mat_[cur_channel][query_idx].valid) {
+                        int left_idx = query_idx;
+                        int right_idx = query_idx;
+                        while (1) {
+                            if (left_idx <= query_idx - HORZ_SCAN_SIZE && right_idx >= query_idx + HORZ_SCAN_SIZE)
+                                break;
+
+                            if (left_idx >= second_node.start && left_idx <= second_node.end && range_mat_[prev_channel][left_idx].valid) {
+                                if (nodeDistance(first_node, second_node, range_mat_[cur_channel][query_idx], range_mat_[prev_channel][left_idx])) {
+                                    // if (DEBUG)
+                                    //     printf("query: %d, left_idx: %d\n", query_idx, left_idx); 
+                                    return true;
+                                }
+                            } 
+
+                            if (right_idx <= second_node.end && right_idx >= second_node.start && range_mat_[prev_channel][right_idx].valid) {
+                                if (nodeDistance(first_node, second_node, range_mat_[cur_channel][query_idx], range_mat_[prev_channel][right_idx])) {
+                                    // if (DEBUG)
+                                    //     printf("query: %d, right_idx: %d\n", query_idx, right_idx); 
+                                    return true;
+                                }
+                            }
+                            left_idx--;
+                            right_idx++;
+                        }
+                    }
+                }
+                return false;
+            }
+
+            float pointDistance(Point pt1, Point pt2) {
+                return sqrt((pt1.x - pt2.x)*(pt1.x-pt2.x) + (pt1.y-pt2.y)*(pt1.y-pt2.y));
+            }
+
+            bool nodeDistance(AOSNode &first_node, AOSNode &second_node, Point &first_point, Point &second_point) {
+                uint16_t cur_label, target_label = 0;
+
+                if (first_node.label == second_node.label)
+                    return false;
+
+                if (pointDistance(first_point, second_point) < VERT_MERGE_THRES) {
+                    // if (DEBUG) {
+                    //     printf("cur: %f;%f;%f, prev: %f;%f;%f distance: %f\n", first_point.x, first_point.y, first_point.z, second_point.x, second_point.y, second_point.z, pointDistance(first_point, second_point));
+                    //     printf("Two nodes merged: %d, %d\n", first_node.label, second_node.label);
+                    // }
+
+                    if (first_node.label > second_node.label) {
+                        cur_label = first_node.label;
+                        target_label = second_node.label;
+                        first_node.label = target_label;
+                    } else {
+                        cur_label = second_node.label;
+                        target_label = first_node.label;
+                        second_node.label = target_label;
+                    }
+                    mergeClusters(cur_label, target_label);
+                    return true;
+                }
+                return false;
+            }
+
+            float getRange(T pt) {
+                return sqrt(pt.x*pt.x+pt.y*pt.y+pt.z*pt.z);
+            }
+
+            int getRowIdx(T pt) {
+                float angle = atan2(pt.z, sqrt(pt.x*pt.x+pt.y*pt.y))*180/M_PI;
+                auto iter_geq = std::lower_bound(vert_angles_.begin(), vert_angles_.end(), angle);
+                int row_idx;
+
+                if (iter_geq == vert_angles_.begin()) {
+                    row_idx = 0;
+                } else {
+                    float a = *(iter_geq - 1);
+                    float b = *(iter_geq);
+                    if (fabs(angle-a) < fabs(angle-b)){
+                        row_idx = iter_geq - vert_angles_.begin() - 1;
+                    } else {
+                        row_idx = iter_geq - vert_angles_.begin();
+                    }
+                }
+                return row_idx;
+            }
+
+            int getRowIdx(T pt, vector<float> vert_angles) {
+                float angle = atan2(pt.z, sqrt(pt.x*pt.x+pt.y*pt.y))*180/M_PI;
+                auto iter_geq = std::lower_bound(vert_angles.begin(), vert_angles.end(), angle);
+                int row_idx;
+
+                if (iter_geq == vert_angles.begin()) {
+                    row_idx = 0;
+                } else {
+                    float a = *(iter_geq - 1);
+                    float b = *(iter_geq);
+                    if (fabs(angle-a) < fabs(angle-b)){
+                        row_idx = iter_geq - vert_angles.begin() - 1;
+                    } else {
+                        row_idx = iter_geq - vert_angles.begin();
+                    }
+                }
+                return row_idx;
+            }
+
+            int getColIdx(T pt) {
+                float horizonAngle = atan2(pt.x, pt.y) * 180 / M_PI;
+                static float ang_res_x = 360.0/float(HORZ_SCAN);
+                int col_idx = -round((horizonAngle-90.0)/ang_res_x) + HORZ_SCAN/2;
+                if (col_idx >= HORZ_SCAN)
+                    col_idx -= HORZ_SCAN;
+                return col_idx;
+            }
+
+            void mergeClusters(uint16_t cur_label, uint16_t target_label) {
+                if(cur_label == 0 || target_label == 0){
+                    printf("Error merging runs cur_label:%u target_label:%u", cur_label, target_label);
+                }
+                for(auto &p : clusters_[cur_label]){
+                    p->label = target_label;
+                }
+                clusters_[target_label].insert_after(clusters_[target_label].cbefore_begin(), clusters_[cur_label].begin(),clusters_[cur_label].end() );
+                clusters_[cur_label].clear();
+            }
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/include/tgs.hpp b/include/tgs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..99746f90981556222b38c2e78d52b3fe4ecae89f
--- /dev/null
+++ b/include/tgs.hpp
@@ -0,0 +1,1643 @@
+#ifndef TRAVEL_GSEG_H
+#define TRAVEL_GSEG_H
+//
+// Created by Minho Oh & Euigon Jung on 1/31/22.
+// We really appreciate Hyungtae Lim and Prof. Hyun Myung! :)
+//
+#include <iostream>
+#include <Eigen/Dense>
+#include <vector>
+#include <random>
+#include <mutex>
+#include <thread>
+#include <chrono>
+#include <math.h>
+#include <fstream>
+#include <memory>
+#include <signal.h>
+
+#include <ros/ros.h>
+#include <pcl_ros/point_cloud.h>
+#include <pcl_conversions/pcl_conversions.h>
+#include <pcl/filters/filter.h>
+#include <pcl/common/centroid.h>
+#include <pcl/filters/voxel_grid.h>
+#include <tf/transform_listener.h>
+#include <tf/tf.h>
+#include <tf/transform_broadcaster.h>
+
+#include <jsk_recognition_msgs/PolygonArray.h>
+#include <visualization_msgs/Marker.h>
+#include <visualization_msgs/MarkerArray.h>
+
+#include <sensor_msgs/PointCloud2.h>
+#include <nav_msgs/Odometry.h>
+#include <nav_msgs/Path.h>
+#include <opencv2/opencv.hpp>
+
+namespace travel {
+
+    #define PTCLOUD_SIZE 132000
+    #define NODEWISE_PTCLOUDSIZE 5000
+
+    #define UNKNOWN 1
+    #define NONGROUND 2
+    #define GROUND 3
+
+    using Eigen::MatrixXf;
+    using Eigen::JacobiSVD;
+    using Eigen::VectorXf;
+
+    template <typename PointType>
+    bool point_z_cmp(PointType a, PointType b){
+        return a.z<b.z;
+    }
+
+    struct TriGridIdx {
+        int row, col, tri;
+    };
+
+    struct TriGridEdge {
+        std::pair<TriGridIdx, TriGridIdx> Pair;
+        bool is_traversable;
+    };
+
+    template <typename PointType>
+    struct TriGridNode {
+        int node_type;
+        pcl::PointCloud<PointType> ptCloud;
+
+        bool is_curr_data;
+        
+        // planar model
+        Eigen::Vector3f normal;
+        Eigen::Vector3f mean_pt;
+        double d;
+
+        Eigen::Vector3f singular_values;
+        Eigen::Matrix3f eigen_vectors;
+        double weight;
+
+        double th_dist_d;
+        double th_outlier_d;
+
+        // graph_searching
+        bool need_recheck;
+        bool is_visited;
+        bool is_rejection;
+        int check_life;
+        int depth;
+    };
+
+    struct TriGridCorner {
+        double x, y;
+        std::vector<double> zs;
+        std::vector<double> weights;
+    };
+
+    template <typename PointType>
+    using GridNode = std::vector<TriGridNode<PointType>>;
+
+    template <typename PointType>
+    using TriGridField = std::vector<std::vector<GridNode<PointType>>>;
+
+    template <typename PointType>
+    class TravelGroundSeg{
+    private:
+        // ros::NodeHandle node_handle_;
+        pcl::PCLHeader cloud_header_;
+        std_msgs::Header msg_header_;
+
+        // ros::Publisher pub_trigrid_nodes_;
+        // ros::Publisher pub_trigrid_edges_;
+        // ros::Publisher pub_trigrid_corners_;
+        // ros::Publisher pub_tgseg_ground_cloud;
+        // ros::Publisher pub_tgseg_nonground_cloud;
+        // ros::Publisher pub_tgseg_outliers_cloud;
+        
+        jsk_recognition_msgs::PolygonArray viz_trigrid_polygons_;
+        visualization_msgs::Marker viz_trigrid_edges_;
+        pcl::PointCloud<pcl::PointXYZ> viz_trigrid_corners_;
+
+        bool REFINE_MODE_;
+        bool VIZ_MDOE_;
+        
+        double MAX_RANGE_;
+        double MIN_RANGE_;
+        double TGF_RESOLUTION_;
+
+        int NUM_ITER_;
+        int NUM_LRP_;
+        int NUM_MIN_POINTS_;
+        
+        double TH_SEEDS_;
+        double TH_DIST_;
+        double TH_OUTLIER_;
+        
+        double TH_NORMAL_;
+        double TH_WEIGHT_;
+        double TH_LCC_NORMAL_SIMILARITY_;
+        double TH_LCC_PLANAR_MODEL_DIST_;
+        double TH_OBSTACLE_HEIGHT_;
+
+        TriGridField<PointType> trigrid_field_;
+        std::vector<TriGridEdge> trigrid_edges_;
+        std::vector<std::vector<TriGridCorner>> trigrid_corners_;
+        std::vector<std::vector<TriGridCorner>> trigrid_centers_;
+
+        pcl::PointCloud<PointType> empty_cloud_;
+        TriGridNode<PointType>  empty_trigrid_node_;
+        GridNode<PointType> empty_grid_nodes_;
+        TriGridCorner empty_trigrid_corner_;
+        TriGridCorner empty_trigrid_center_;
+
+        pcl::PointCloud<PointType> ptCloud_tgfwise_ground_;
+        pcl::PointCloud<PointType> ptCloud_tgfwise_nonground_;
+        pcl::PointCloud<PointType> ptCloud_tgfwise_outliers_;
+        pcl::PointCloud<PointType> ptCloud_tgfwise_obstacle_;
+        pcl::PointCloud<PointType> ptCloud_nodewise_ground_;
+        pcl::PointCloud<PointType> ptCloud_nodewise_nonground_;
+        pcl::PointCloud<PointType> ptCloud_nodewise_outliers_;
+        pcl::PointCloud<PointType> ptCloud_nodewise_obstacle_;
+
+    public:
+        // TravelGroundSeg(ros::NodeHandle* nh):node_handle_(*nh){
+        TravelGroundSeg(){
+            // Init ROS related
+            ROS_INFO("Inititalizing Traversable Ground Segmentation...");
+
+            // pub_trigrid_nodes_  = node_handle_.advertise<jsk_recognition_msgs::PolygonArray>("/travelgseg/nodes", 1);
+            // pub_trigrid_edges_  = node_handle_.advertise<visualization_msgs::Marker>("/travelgseg/edges", 1);
+            // pub_trigrid_corners_= node_handle_.advertise<pcl::PointCloud<pcl::PointXYZ>>("/travelgseg/corners", 1);
+            
+            // pub_tgseg_ground_cloud    = node_handle_.advertise<sensor_msgs::PointCloud2>("/travelgseg/ground_cloud", 1);
+            // pub_tgseg_nonground_cloud = node_handle_.advertise<sensor_msgs::PointCloud2>("/travelgseg/nonground_cloud", 1);
+            // pub_tgseg_outliers_cloud   = node_handle_.advertise<sensor_msgs::PointCloud2>("/travelgseg/outlier_cloud", 1);
+        };
+
+        void setParams(const double max_range, const double min_range, const double resolution, 
+                            const int num_iter, const int num_lpr, const int num_min_pts, const double th_seeds, 
+                            const double th_dist, const double th_outlier, const double th_normal, const double th_weight, 
+                            const double th_lcc_normal_similiarity, const double th_lcc_planar_model_dist, const double th_obstacle,
+                            const bool refine_mode, const bool visualization) {
+            std::cout<<""<<std::endl;
+            ROS_INFO("Set TRAVEL_GSEG Parameters");
+
+            std::cout<<"Just about to assign the max_range value = " << max_range <<std::endl;
+            std::cout<<"to the variable MAX_RANGE_ = " << MAX_RANGE_ <<std::endl;
+
+            MAX_RANGE_ = max_range;
+            ROS_INFO("Max Range: %f", MAX_RANGE_);
+
+            MIN_RANGE_ = min_range;
+            ROS_INFO("Min Range: %f", MIN_RANGE_);
+            
+            TGF_RESOLUTION_ = resolution;
+            ROS_INFO("Resolution: %f", TGF_RESOLUTION_);
+            
+            NUM_ITER_ = num_iter;
+            ROS_INFO("Num of Iteration: %d", NUM_ITER_);
+            
+            NUM_LRP_ = num_lpr;
+            ROS_INFO("Num of LPR: %d", NUM_LRP_);
+            
+            NUM_MIN_POINTS_ = num_min_pts;
+            ROS_INFO("Num of min. points: %d", NUM_MIN_POINTS_);
+            
+            TH_SEEDS_ = th_seeds;
+            ROS_INFO("Seeds Threshold: %f", TH_SEEDS_);
+            
+            TH_DIST_ = th_dist;
+            ROS_INFO("Distance Threshold: %f", TH_DIST_);
+            
+            TH_OUTLIER_ = th_outlier;
+            ROS_INFO("Outlier Threshold: %f", TH_OUTLIER_);
+
+            TH_NORMAL_ = th_normal;
+            ROS_INFO("Normal Threshold: %f", TH_NORMAL_);
+
+            TH_WEIGHT_ = th_weight;
+            ROS_INFO("Node Weight Threshold: %f", TH_WEIGHT_);
+
+            TH_LCC_NORMAL_SIMILARITY_ = th_lcc_normal_similiarity;
+            ROS_INFO("LCC Normal Similarity: %f", TH_LCC_NORMAL_SIMILARITY_);
+
+            TH_LCC_PLANAR_MODEL_DIST_ = th_lcc_planar_model_dist;
+            ROS_INFO("LCC Plane Distance   : %f", TH_LCC_PLANAR_MODEL_DIST_);
+
+            TH_OBSTACLE_HEIGHT_ = th_obstacle;
+            ROS_INFO("Obstacle Max Height  : %f", TH_OBSTACLE_HEIGHT_);
+            
+            REFINE_MODE_ = refine_mode;
+            VIZ_MDOE_ = visualization;
+
+            ROS_INFO("Set TGF Parameters");
+            initTriGridField(trigrid_field_);
+            initTriGridCorners(trigrid_corners_, trigrid_centers_);
+
+            ptCloud_tgfwise_ground_.clear();
+            ptCloud_tgfwise_ground_.reserve(PTCLOUD_SIZE);
+            ptCloud_tgfwise_nonground_.clear();
+            ptCloud_tgfwise_nonground_.reserve(PTCLOUD_SIZE);
+            ptCloud_tgfwise_outliers_.clear();
+            ptCloud_tgfwise_outliers_.reserve(PTCLOUD_SIZE);
+            ptCloud_tgfwise_obstacle_.clear();
+            ptCloud_tgfwise_obstacle_.reserve(PTCLOUD_SIZE);
+    
+            ptCloud_nodewise_ground_.clear();
+            ptCloud_nodewise_ground_.reserve(NODEWISE_PTCLOUDSIZE);
+            ptCloud_nodewise_nonground_.clear();
+            ptCloud_nodewise_nonground_.reserve(NODEWISE_PTCLOUDSIZE);
+            ptCloud_nodewise_outliers_.clear();
+            ptCloud_nodewise_outliers_.reserve(NODEWISE_PTCLOUDSIZE);
+            ptCloud_nodewise_obstacle_.clear();
+            ptCloud_nodewise_obstacle_.reserve(NODEWISE_PTCLOUDSIZE);
+
+            if (VIZ_MDOE_) {
+                viz_trigrid_polygons_.polygons.clear();
+                viz_trigrid_polygons_.polygons.reserve(rows_ * cols_);
+                viz_trigrid_polygons_.likelihood.clear();
+                viz_trigrid_polygons_.likelihood.reserve(rows_ * cols_);
+                
+
+                viz_trigrid_edges_.ns = "trigrid_edges";
+                viz_trigrid_edges_.action = visualization_msgs::Marker::ADD;
+                viz_trigrid_edges_.type = visualization_msgs::Marker::LINE_LIST;
+                viz_trigrid_edges_.pose.orientation.w = 1.0;
+                viz_trigrid_edges_.scale.x = 0.5;
+                viz_trigrid_edges_.id = 0;
+
+                viz_trigrid_corners_.clear();
+                viz_trigrid_corners_.reserve(rows_*cols_ + (rows_+ 1)*(cols_+1));
+            }
+        };
+
+        void estimateGround(const pcl::PointCloud<PointType>& cloud_in,
+                            pcl::PointCloud<PointType>& cloudGround_out,
+                            pcl::PointCloud<PointType>& cloudNonground_out,
+                            double& time_taken){
+        
+            // 0. Init
+            static time_t start, end;
+            cloud_header_ = cloud_in.header;
+            pcl_conversions::fromPCL(cloud_header_, msg_header_);
+            // ROS_INFO("TriGrid Field-based Traversable Ground Segmentation...");
+            start = clock();
+            ptCloud_tgfwise_outliers_.clear();
+            ptCloud_tgfwise_outliers_.reserve(cloud_in.size());
+
+
+            // 1. Embed PointCloud to TriGridField
+            clearTriGridField(trigrid_field_);
+            clearTriGridCorners(trigrid_corners_, trigrid_centers_);
+
+            embedCloudToTriGridField(cloud_in, trigrid_field_);
+
+            // 2. Node-wise Terrain Modeling
+            modelNodeWiseTerrain(trigrid_field_);
+            
+            // 3. Breadth-first Traversable Graph Search
+            BreadthFirstTraversableGraphSearch(trigrid_field_);
+            setTGFCornersCenters(trigrid_field_, trigrid_corners_, trigrid_centers_);
+
+            // 4. TGF-wise Traversable Terrain Model Fitting
+            if (REFINE_MODE_){
+                fitTGFWiseTraversableTerrainModel(trigrid_field_, trigrid_corners_, trigrid_centers_);
+            }
+            
+            // 5. Ground Segmentation
+            segmentTGFGround(trigrid_field_, ptCloud_tgfwise_ground_, ptCloud_tgfwise_nonground_, ptCloud_tgfwise_obstacle_, ptCloud_tgfwise_outliers_);
+            cloudGround_out = ptCloud_tgfwise_ground_;
+            cloudNonground_out = ptCloud_tgfwise_nonground_;
+            cloudGround_out.header = cloudNonground_out.header = cloud_header_;
+
+            end = clock();
+            time_taken = (double)(end - start) / CLOCKS_PER_SEC;
+
+            // 6. Publish Results and Visualization
+            if (VIZ_MDOE_){
+                // publishTriGridFieldGraph();
+                // publishTriGridCorners();
+                // publishPointClouds();
+            }
+            return;
+        };
+
+        TriGridIdx getTriGridIdx(const float& x_in, const float& y_in){
+            TriGridIdx tgf_idx;
+            int r_i = (x_in - tgf_min_x)/TGF_RESOLUTION_;
+            int c_i = (y_in - tgf_min_y)/TGF_RESOLUTION_;
+            int t_i = 0;
+            double angle = atan2(y_in-(c_i*TGF_RESOLUTION_ + TGF_RESOLUTION_/2 + tgf_min_y), x_in-(r_i*TGF_RESOLUTION_ + TGF_RESOLUTION_/2 + tgf_min_x));
+
+            if (angle>=(M_PI/4) && angle <(3*M_PI/4)){
+                t_i = 1;
+            } else if (angle>=(-M_PI/4) && angle <(M_PI/4)){
+                t_i = 0;
+            } else if (angle>=(-3*M_PI/4) && angle <(-M_PI/4)){
+                t_i = 3;
+            } else{
+                t_i = 2;
+            }
+            tgf_idx.row = r_i;
+            tgf_idx.col = c_i;
+            tgf_idx.tri = t_i;
+            return tgf_idx;
+        }
+
+        TriGridNode<PointType> getTriGridNode(const float& x_in, const float& y_in){
+            TriGridNode<PointType> node;
+            TriGridIdx node_idx = getTriGridIdx(x_in, y_in);
+            node = trigrid_field_[node_idx.row][node_idx.col][node_idx.tri];
+            return node;
+        };
+
+        TriGridNode<PointType> getTriGridNode(const TriGridIdx& tgf_idx){
+            TriGridNode<PointType> node;
+            node = trigrid_field_[tgf_idx.row][tgf_idx.col][tgf_idx.tri];
+            return node;
+        };
+
+        bool is_traversable(const float& x_in, const float& y_in){
+            TriGridNode<PointType> node = getTriGridNode(x_in, y_in);
+            if (node.node_type == GROUND){
+                return true;
+            } else{
+                return false;
+            }
+        };
+
+        pcl::PointCloud<PointType> getObstaclePC(){
+            pcl::PointCloud<PointType> cloud_obstacle;
+            cloud_obstacle = ptCloud_tgfwise_obstacle_;
+            return cloud_obstacle;
+        };
+
+    private:
+        double tgf_max_x, tgf_max_y, tgf_min_x, tgf_min_y;
+        double rows_, cols_;
+
+        void initTriGridField(TriGridField<PointType>& tgf_in){
+            // ROS_INFO("Initializing TriGridField...");
+
+            tgf_max_x = MAX_RANGE_;
+            tgf_max_y = MAX_RANGE_;
+
+            tgf_min_x = -MAX_RANGE_;
+            tgf_min_y = -MAX_RANGE_;
+
+            rows_ = (int)(tgf_max_x - tgf_min_x) / TGF_RESOLUTION_;
+            cols_ = (int)(tgf_max_y - tgf_min_y) / TGF_RESOLUTION_;
+            empty_cloud_.clear();
+            empty_cloud_.reserve(PTCLOUD_SIZE);
+            
+            // Set Node structure
+            empty_trigrid_node_.node_type = UNKNOWN;
+            empty_trigrid_node_.ptCloud.clear();
+            empty_trigrid_node_.ptCloud.reserve(NODEWISE_PTCLOUDSIZE);
+
+            empty_trigrid_node_.is_curr_data = false;
+            empty_trigrid_node_.need_recheck = false;
+            empty_trigrid_node_.is_visited = false;
+            empty_trigrid_node_.is_rejection = false;
+
+            empty_trigrid_node_.check_life = 10;
+            empty_trigrid_node_.depth = -1;
+
+            empty_trigrid_node_.normal;
+            empty_trigrid_node_.mean_pt;
+            empty_trigrid_node_.d = 0;
+            
+            empty_trigrid_node_.singular_values;
+            empty_trigrid_node_.eigen_vectors;
+            empty_trigrid_node_.weight = 0;
+
+            empty_trigrid_node_.th_dist_d = 0;
+            empty_trigrid_node_.th_outlier_d = 0;
+
+            // Set TriGridField
+            tgf_in.clear();
+            std::vector<GridNode<PointType>> vec_gridnode;
+
+            for (int i = 0; i < 4 ; i ++) 
+                empty_grid_nodes_.emplace_back(empty_trigrid_node_);
+                
+            for (int i=0; i< cols_; i++){ vec_gridnode.emplace_back(empty_grid_nodes_);}
+            for (int j=0; j< rows_; j++){ tgf_in.emplace_back(vec_gridnode);}
+
+            return;
+        };
+
+        void initTriGridCorners(std::vector<std::vector<TriGridCorner>>& trigrid_corners_in,
+                                std::vector<std::vector<TriGridCorner>>& trigrid_centers_in){
+            // ROS_INFO("Initializing TriGridCorners...");
+
+            // Set TriGridCorner
+            empty_trigrid_corner_.x = empty_trigrid_corner_.y = 0.0;
+            empty_trigrid_corner_.zs.clear();
+            empty_trigrid_corner_.zs.reserve(8);
+            empty_trigrid_corner_.weights.clear();
+            empty_trigrid_corner_.weights.reserve(8);
+
+            empty_trigrid_center_.x = empty_trigrid_center_.y = 0.0;
+            empty_trigrid_center_.zs.clear();
+            empty_trigrid_center_.zs.reserve(4);
+            empty_trigrid_center_.weights.clear();
+            empty_trigrid_center_.weights.reserve(4);
+
+            trigrid_corners_in.clear();
+            trigrid_centers_in.clear();
+
+            // Set Corners and Centers
+            std::vector<TriGridCorner> col_corners;
+            std::vector<TriGridCorner> col_centers;
+            for (int i=0; i< cols_; i++){
+                col_corners.emplace_back(empty_trigrid_corner_);
+                col_centers.emplace_back(empty_trigrid_center_);
+            }
+            col_corners.emplace_back(empty_trigrid_corner_);
+
+            for (int j=0; j< rows_; j++){
+                trigrid_corners_in.emplace_back(col_corners);
+                trigrid_centers_in.emplace_back(col_centers);
+            }
+            trigrid_corners_in.emplace_back(col_corners);
+
+            return;
+        };
+
+        void clearTriGridField(TriGridField<PointType> &tgf_in){
+            // ROS_INFO("Clearing TriGridField...");
+
+            for (int r_i = 0; r_i < rows_; r_i++){
+            for (int c_i = 0; c_i < cols_; c_i++){
+                tgf_in[r_i][c_i] = empty_grid_nodes_;
+            }
+            }
+            return;
+        };
+
+        void clearTriGridCorners(std::vector<std::vector<TriGridCorner>>& trigrid_corners_in,
+                                std::vector<std::vector<TriGridCorner>>& trigrid_centers_in){
+            // ROS_INFO("Clearing TriGridCorners...");
+
+            TriGridCorner tmp_corner = empty_trigrid_corner_;
+            TriGridCorner tmp_center = empty_trigrid_center_;
+            for (int r_i = 0; r_i < rows_+1; r_i++){
+            for (int c_i = 0; c_i < cols_+1; c_i++){
+                tmp_corner.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; tmp_corner.y = (c_i)*TGF_RESOLUTION_+tgf_min_y;
+                tmp_corner.zs.clear();
+                tmp_corner.weights.clear();
+                trigrid_corners_in[r_i][c_i] = tmp_corner;
+                if (r_i < rows_ && c_i < cols_) {
+                    tmp_center.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; tmp_center.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y;
+                    tmp_center.zs.clear();
+                    tmp_center.weights.clear();
+                    trigrid_centers_in[r_i][c_i] = tmp_center;
+                }
+            }
+            }
+            return;
+        };
+        
+        double xy_2Dradius(double x, double y){
+            return sqrt(x*x + y*y);
+        };
+
+        bool filterPoint(const PointType &pt_in){
+            double xy_range = xy_2Dradius(pt_in.x, pt_in.y);
+            if (xy_range >= MAX_RANGE_ || xy_range <= MIN_RANGE_) return true;
+
+            return false;
+        }
+
+        void embedCloudToTriGridField(const pcl::PointCloud<PointType>& cloud_in, TriGridField<PointType>& tgf_out) {
+            // ROS_INFO("Embedding PointCloud to TriGridField...");
+
+            for (auto const &pt: cloud_in.points){
+                if (filterPoint(pt)){
+                    ptCloud_tgfwise_outliers_.points.push_back(pt);
+                    continue;   
+                }
+
+                int r_i = (pt.x - tgf_min_x)/TGF_RESOLUTION_;
+                int c_i = (pt.y - tgf_min_y)/TGF_RESOLUTION_;
+
+                if (r_i < 0 || r_i >= rows_ || c_i < 0 || c_i >= cols_) {
+                    ptCloud_tgfwise_outliers_.points.push_back(pt);
+                    continue;
+                }
+
+                double angle = atan2(pt.y-(c_i*TGF_RESOLUTION_ + TGF_RESOLUTION_/2 + tgf_min_y), pt.x-(r_i*TGF_RESOLUTION_ + TGF_RESOLUTION_/2 + tgf_min_x));
+                if (angle>=(M_PI/4) && angle <(3*M_PI/4)){
+                    // left side
+                    tgf_out[r_i][c_i][1].ptCloud.push_back(pt);
+                    if(!tgf_out[r_i][c_i][1].is_curr_data) {tgf_out[r_i][c_i][1].is_curr_data = true;}
+                } else if (angle>=(-M_PI/4) && angle <(M_PI/4)){
+                    // upper side
+                    tgf_out[r_i][c_i][0].ptCloud.push_back(pt);
+                    if (!tgf_out[r_i][c_i][0].is_curr_data){tgf_out[r_i][c_i][0].is_curr_data = true;}
+                    
+                } else if (angle>=(-3*M_PI/4) && angle <(-M_PI/4)){
+                    // right side
+                    tgf_out[r_i][c_i][3].ptCloud.push_back(pt);
+                    if (!tgf_out[r_i][c_i][3].is_curr_data) {tgf_out[r_i][c_i][3].is_curr_data = true;}
+                } else{
+                    // lower side
+                    tgf_out[r_i][c_i][2].ptCloud.push_back(pt);
+                    if (!tgf_out[r_i][c_i][2].is_curr_data) {tgf_out[r_i][c_i][2].is_curr_data = true;}
+                }
+            }
+
+            return;
+        };
+
+        void extractInitialSeeds(const pcl::PointCloud<PointType>& p_sorted, pcl::PointCloud<PointType>& init_seeds){
+            //function for uniform mode
+            init_seeds.points.clear();
+
+            // LPR is the mean of Low Point Representative
+            double sum = 0;
+            int cnt = 0;
+
+            // Calculate the mean height value.
+            for (int i=0; i< (int) p_sorted.points.size() && cnt<NUM_LRP_; i++){
+                sum += p_sorted.points[i].z;
+                cnt++;
+            }
+
+            double lpr_height = cnt!=0?sum/cnt:0;
+
+            for(int i=0 ; i< (int) p_sorted.points.size() ; i++){
+                if(p_sorted.points[i].z < lpr_height + TH_SEEDS_){
+                    if (p_sorted.points[i].z < lpr_height-TH_OUTLIER_) continue;
+                    init_seeds.points.push_back(p_sorted.points[i]);
+                }
+            }
+
+            return;
+        }
+
+        void estimatePlanarModel(const pcl::PointCloud<PointType>& ground_in, TriGridNode<PointType>& node_out) {
+
+            // function for uniform mode
+            Eigen::Matrix3f cov_;
+            Eigen::Vector4f pc_mean_;
+            pcl::computeMeanAndCovarianceMatrix(ground_in, cov_, pc_mean_);
+
+            // Singular Value Decomposition: SVD
+            Eigen::JacobiSVD<Eigen::MatrixXf> svd(cov_, Eigen::DecompositionOptions::ComputeFullU);
+
+            // Use the least singular vector as normal
+            node_out.eigen_vectors = svd.matrixU();
+            if (node_out.eigen_vectors.col(2)(2,0)<0) {
+                node_out.eigen_vectors.col(0)*= -1;
+                node_out.eigen_vectors.col(2)*= -1;
+            }
+            node_out.normal = node_out.eigen_vectors.col(2);
+            node_out.singular_values = svd.singularValues();
+
+            // mean ground seeds value
+            node_out.mean_pt = pc_mean_.head<3>();
+
+            // according to noraml.T*[x,y,z] = -d
+            node_out.d = -(node_out.normal.transpose()*node_out.mean_pt)(0,0);
+
+            // set distance theshold to 'th_dist - d'
+            node_out.th_dist_d = TH_DIST_ - node_out.d;
+            node_out.th_outlier_d = -node_out.d - TH_OUTLIER_;
+
+            return;
+        }    
+
+        void modelPCAbasedTerrain(TriGridNode<PointType>& node_in) {
+            // Initailization
+            if (!ptCloud_nodewise_ground_.empty()) ptCloud_nodewise_ground_.clear();
+            
+            // Tri Grid Initialization
+            // When to initialize the planar model, we don't have prior. so outlier is removed in heuristic parameter.
+            pcl::PointCloud<PointType> sort_ptCloud = node_in.ptCloud;
+
+            // sort in z-coordinate
+            sort(sort_ptCloud.points.begin(), sort_ptCloud.end(), point_z_cmp<PointType>);
+
+            // Set init seeds
+            extractInitialSeeds(sort_ptCloud, ptCloud_nodewise_ground_);
+            
+            Eigen::MatrixXf points(sort_ptCloud.points.size(),3);
+            int j = 0;
+            for (auto& p:sort_ptCloud.points){
+                points.row(j++)<<p.x, p.y, p.z;
+            }
+            // Extract Ground
+            for (int i =0; i < NUM_ITER_; i++){
+                estimatePlanarModel(ptCloud_nodewise_ground_, node_in);
+                if(ptCloud_nodewise_ground_.size() < 3){
+    
+                    node_in.node_type = NONGROUND;
+                    break;
+                }
+                ptCloud_nodewise_ground_.clear();
+                // threshold filter
+                Eigen::VectorXf result = points*node_in.normal;
+                for (int r = 0; r<result.rows(); r++){
+                    if (i < NUM_ITER_-1){
+                        if (result[r]<node_in.th_dist_d){
+                            ptCloud_nodewise_ground_.push_back(sort_ptCloud.points[r]);
+                        }
+                    } else {
+                        // Final interation
+                        if (node_in.normal(2,0) < TH_NORMAL_ ){
+                            node_in.node_type = NONGROUND;
+                        } else {
+                            node_in.node_type = GROUND;
+                        }
+                    }
+                }
+            }
+
+            return;
+        }
+
+        double calcNodeWeight(const TriGridNode<PointType>& node_in){
+            double weight = 0;
+            
+            // weight = (node_in.singular_values[0]/node_in.singular_values[2] + node_in.singular_values[1]/node_in.singular_values[2])/(node_in.singular_values[0]/node_in.singular_values[1]);
+            weight = (node_in.singular_values[0] + node_in.singular_values[1])*node_in.singular_values[1]/(node_in.singular_values[0]*node_in.singular_values[2]+0.001);
+
+            return weight;
+        }
+
+        void modelNodeWiseTerrain(TriGridField<PointType>& tgf_in) {
+            // ROS_INFO("Node-wise Terrain Modeling...");
+
+            for (int r_i = 0; r_i < rows_; r_i++){
+            for (int c_i = 0; c_i < cols_; c_i++){
+            for (int s_i = 0; s_i < 4; s_i++){
+                if (tgf_in[r_i][c_i][s_i].is_curr_data){
+                    if (tgf_in[r_i][c_i][s_i].ptCloud.size() < NUM_MIN_POINTS_){
+                        tgf_in[r_i][c_i][s_i].node_type = UNKNOWN;
+                        continue;                    
+                    } else {
+                        modelPCAbasedTerrain(tgf_in[r_i][c_i][s_i]);
+                        if (tgf_in[r_i][c_i][s_i].node_type == GROUND){ tgf_in[r_i][c_i][s_i].weight = calcNodeWeight(tgf_in[r_i][c_i][s_i]); }
+                    }
+                }
+            }
+            }
+            }
+
+            return;
+        };
+
+        void findDominantNode(const TriGridField<PointType>& tgf_in, TriGridIdx& node_idx_out) {
+            // Find the dominant node
+            ROS_INFO("Find the dominant node...");
+            TriGridIdx max_tri_idx;
+            TriGridIdx ego_idx;
+            ego_idx.row = (int)((0-tgf_min_x)/TGF_RESOLUTION_);
+            ego_idx.col = (int)((0-tgf_min_y)/TGF_RESOLUTION_);
+            ego_idx.tri = 0;
+            
+            max_tri_idx = ego_idx;
+            for (int r_i = ego_idx.row - 2; r_i < ego_idx.row + 2; r_i++){
+            for (int c_i = ego_idx.col - 2; c_i < ego_idx.col + 2; c_i++){
+            for (int s_i = 0; s_i < 4; s_i++){
+                if (tgf_in[r_i][c_i][s_i].is_curr_data){
+                    if (tgf_in[r_i][c_i][s_i].node_type == GROUND){
+                        if (tgf_in[r_i][c_i][s_i].weight > tgf_in[max_tri_idx.row][max_tri_idx.row][max_tri_idx.tri].weight){
+                            max_tri_idx.row = r_i;
+                            max_tri_idx.col = c_i;
+                            max_tri_idx.tri = s_i;
+                        }
+                    }
+                }
+            }
+            }    
+            }
+            node_idx_out = max_tri_idx;
+            return;
+        };
+
+        void searchNeighborNodes(const TriGridIdx &cur_idx, std::vector<TriGridIdx> &neighbor_idxs) {
+            neighbor_idxs.clear();
+            neighbor_idxs.reserve(14);
+            int r_i = cur_idx.row;
+            int c_i = cur_idx.col;
+            int s_i = cur_idx.tri;
+
+            std::vector<TriGridIdx> tmp_neighbors;
+            tmp_neighbors.clear();
+            tmp_neighbors.reserve(14);
+            
+            TriGridIdx neighbor_idx;
+            for (int s_i = 0; s_i < 4 ; s_i++){
+                if (s_i == cur_idx.tri) continue;
+
+                neighbor_idx = cur_idx;
+                neighbor_idx.tri = s_i;
+                tmp_neighbors.push_back(neighbor_idx);
+            }
+
+            switch (s_i) {
+                case 0:
+                    tmp_neighbors.push_back({r_i+1, c_i+1, 2});
+                    tmp_neighbors.push_back({r_i+1, c_i+1, 3});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 1});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 2});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 3});
+                    tmp_neighbors.push_back({r_i+1, c_i-1, 1});
+                    tmp_neighbors.push_back({r_i+1, c_i-1, 2});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 0});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 3});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 0});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 1});
+                    break;
+                case 1:
+                    tmp_neighbors.push_back({r_i+1, c_i+1, 2});
+                    tmp_neighbors.push_back({r_i+1, c_i+1, 3});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 1});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 2});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 0});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 2});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 3});
+                    tmp_neighbors.push_back({r_i-1, c_i+1, 0});
+                    tmp_neighbors.push_back({r_i-1, c_i+1, 3});
+                    tmp_neighbors.push_back({r_i-1, c_i+1, 0});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 1});
+                    break;
+                case 2:
+                    tmp_neighbors.push_back({r_i  , c_i+1, 2});
+                    tmp_neighbors.push_back({r_i  , c_i+1, 3});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 1});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 2});
+                    tmp_neighbors.push_back({r_i-1, c_i+1, 0});
+                    tmp_neighbors.push_back({r_i-1, c_i+1, 3});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 0});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 1});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 3});
+                    tmp_neighbors.push_back({r_i-1, c_i-1, 0});
+                    tmp_neighbors.push_back({r_i-1, c_i-1, 1});
+                    break;
+                case 3:
+                    tmp_neighbors.push_back({r_i+1, c_i  , 2});
+                    tmp_neighbors.push_back({r_i+1, c_i  , 3});
+                    tmp_neighbors.push_back({r_i+1, c_i-1, 1});
+                    tmp_neighbors.push_back({r_i+1, c_i-1, 2});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 0});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 1});
+                    tmp_neighbors.push_back({r_i  , c_i-1, 2});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 0});
+                    tmp_neighbors.push_back({r_i-1, c_i  , 3});
+                    tmp_neighbors.push_back({r_i-1, c_i-1, 0});
+                    tmp_neighbors.push_back({r_i-1, c_i-1, 1});
+                    break;
+                default:
+                    break;
+            }
+
+            for (int n_i = 0 ; n_i < (int) tmp_neighbors.size() ; n_i++) {
+
+                if (tmp_neighbors[n_i].row >= rows_ || tmp_neighbors[n_i].row < 0) {
+                    continue;
+                }
+
+                if (tmp_neighbors[n_i].col >= cols_ || tmp_neighbors[n_i].col < 0) {
+                    continue;
+                }
+
+                neighbor_idxs.push_back(tmp_neighbors[n_i]);
+            }
+        }
+
+        void searchAdjacentNodes(const TriGridIdx &cur_idx, std::vector<TriGridIdx> &adjacent_idxs) {
+            adjacent_idxs.clear();
+            adjacent_idxs.reserve(3);
+            int r_i = cur_idx.row;
+            int c_i = cur_idx.col;
+            int s_i = cur_idx.tri;
+
+            std::vector<TriGridIdx> tmp_neighbors;
+            tmp_neighbors.clear();
+            tmp_neighbors.reserve(3);
+            
+            TriGridIdx neighbor_idx;
+
+            switch (s_i) {
+                case 0:
+                    tmp_neighbors.push_back({r_i+1, c_i, 2});
+                    tmp_neighbors.push_back({r_i  , c_i, 3});
+                    tmp_neighbors.push_back({r_i  , c_i, 1});
+     
+                    break;
+                case 1:
+                    tmp_neighbors.push_back({r_i, c_i+1, 3});
+                    tmp_neighbors.push_back({r_i, c_i  , 0});
+                    tmp_neighbors.push_back({r_i, c_i  , 2});                    
+                    break;
+                case 2:
+                    tmp_neighbors.push_back({r_i-1, c_i, 0});
+                    tmp_neighbors.push_back({r_i  , c_i, 1});
+                    tmp_neighbors.push_back({r_i  , c_i, 3});
+                    break;
+                case 3:
+                    tmp_neighbors.push_back({r_i, c_i-1, 1});
+                    tmp_neighbors.push_back({r_i, c_i  , 2});
+                    tmp_neighbors.push_back({r_i, c_i  , 0});
+                    break;
+                default:
+                    break;
+            }
+
+            for (int n_i = 0 ; n_i < (int) tmp_neighbors.size() ; n_i++) {
+
+                if (tmp_neighbors[n_i].row >= rows_ || tmp_neighbors[n_i].row < 0) {
+                    continue;
+                }
+
+                if (tmp_neighbors[n_i].col >= cols_ || tmp_neighbors[n_i].col < 0) {
+                    continue;
+                }
+
+                adjacent_idxs.push_back(tmp_neighbors[n_i]);
+            }
+        }
+
+        bool LocalConvecityConcavity(const TriGridField<PointType> &tgf, const TriGridIdx &cur_node_idx, const TriGridIdx &neighbor_idx, 
+                                    double & thr_local_normal, double & thr_local_dist) {
+            TriGridNode<PointType> current_node = tgf[cur_node_idx.row][cur_node_idx.col][cur_node_idx.tri];
+            TriGridNode<PointType> neighbor_node = tgf[neighbor_idx.row][neighbor_idx.col][neighbor_idx.tri];
+
+            Eigen::Vector3f normal_src = current_node.normal; 
+            Eigen::Vector3f normal_tgt = neighbor_node.normal; 
+            Eigen::Vector3f meanPt_diff_s2t = neighbor_node.mean_pt - current_node.mean_pt;
+
+            double diff_norm = meanPt_diff_s2t.norm();
+            double dist_s2t = normal_src.dot(meanPt_diff_s2t);
+            double dist_t2s = normal_tgt.dot(-meanPt_diff_s2t);
+
+            double normal_similarity = normal_src.dot(normal_tgt);
+            double TH_NORMAL_cos_similarity = sin(diff_norm*thr_local_normal);
+            if ((normal_similarity < (1-TH_NORMAL_cos_similarity))) {
+                return false;
+            }
+
+            double TH_DIST_to_planar = diff_norm*sin(thr_local_dist);
+            if ( (abs(dist_s2t) > TH_DIST_to_planar || abs(dist_t2s) > TH_DIST_to_planar) ) {
+                return false;
+            }
+
+            return true;
+        }
+
+        void BreadthFirstTraversableGraphSearch(TriGridField<PointType>& tgf_in) {
+
+            // Find the dominant node
+            std::queue<TriGridIdx> searching_idx_queue;
+            TriGridIdx dominant_node_idx;
+            findDominantNode(tgf_in, dominant_node_idx);
+            tgf_in[dominant_node_idx.row][dominant_node_idx.col][dominant_node_idx.tri].is_visited = true;
+            tgf_in[dominant_node_idx.row][dominant_node_idx.col][dominant_node_idx.tri].depth = 0;
+            tgf_in[dominant_node_idx.row][dominant_node_idx.col][dominant_node_idx.tri].node_type = GROUND;
+
+            searching_idx_queue.push(dominant_node_idx);
+
+            double max_planar_height = 0;
+            trigrid_edges_.clear();
+            trigrid_edges_.reserve(rows_*cols_*4);
+            TriGridEdge cur_edge;
+            TriGridIdx current_node_idx;
+            while (!searching_idx_queue.empty()){
+                // set current node
+                current_node_idx = searching_idx_queue.front();
+                searching_idx_queue.pop();
+
+                // search the neighbor nodes
+                std::vector<TriGridIdx> neighbor_idxs;
+                searchNeighborNodes(current_node_idx, neighbor_idxs);
+                
+                // set the traversable edges
+                for (int i = 0; i < (int) neighbor_idxs.size(); i++){
+                    // if the neighbor node is traversable, add it to the queue
+
+                    TriGridIdx n_i = neighbor_idxs[i];
+
+
+                    if (tgf_in[n_i.row][n_i.col][n_i.tri].depth >=0){
+                        continue;
+                    }
+
+                    if (tgf_in[n_i.row][n_i.col][n_i.tri].is_visited) {
+                        if (!tgf_in[n_i.row][n_i.col][n_i.tri].need_recheck){
+                            continue;
+                        } else {
+                            if (tgf_in[n_i.row][n_i.col][n_i.tri].check_life <= 0){
+                                continue;
+                            }
+                        }
+                        continue;
+                    } else {
+                        if (tgf_in[n_i.row][n_i.col][n_i.tri].node_type != GROUND) {
+                        continue;
+                        }
+                    }
+
+                    tgf_in[n_i.row][n_i.col][n_i.tri].is_visited =true;
+                    
+                    if (!LocalConvecityConcavity(tgf_in, current_node_idx, n_i, TH_LCC_NORMAL_SIMILARITY_, TH_LCC_PLANAR_MODEL_DIST_)) {
+                        tgf_in[n_i.row][n_i.col][n_i.tri].is_rejection = true;
+                        tgf_in[n_i.row][n_i.col][n_i.tri].node_type = NONGROUND;
+
+                        if(tgf_in[n_i.row][n_i.col][n_i.tri].check_life > 0) {
+                            tgf_in[n_i.row][n_i.col][n_i.tri].check_life -=1;
+                            tgf_in[n_i.row][n_i.col][n_i.tri].need_recheck = true;
+                        } else {
+                            tgf_in[n_i.row][n_i.col][n_i.tri].need_recheck = false;
+                        }
+                        continue;
+                    }
+
+                    if (max_planar_height < tgf_in[n_i.row][n_i.col][n_i.tri].mean_pt[2]) max_planar_height = tgf_in[n_i.row][n_i.col][n_i.tri].mean_pt[2];
+
+                    tgf_in[n_i.row][n_i.col][n_i.tri].node_type = GROUND;
+                    tgf_in[n_i.row][n_i.col][n_i.tri].is_rejection = false;
+                    tgf_in[n_i.row][n_i.col][n_i.tri].depth = tgf_in[current_node_idx.row][current_node_idx.col][current_node_idx.tri].depth + 1;
+
+                    if (VIZ_MDOE_){
+                        cur_edge.Pair.first = current_node_idx;
+                        cur_edge.Pair.second = n_i;
+                        cur_edge.is_traversable = true;
+                        trigrid_edges_.push_back(cur_edge);
+                    }
+
+                    searching_idx_queue.push(n_i);
+                }
+
+                if (searching_idx_queue.empty()){
+                    // set the new dominant node
+                    for (int r_i = 0; r_i < rows_; r_i++) {
+                    for (int c_i = 0; c_i < cols_; c_i++) {
+                    for (int s_i = 0; s_i < (int) tgf_in[r_i][c_i].size() ; s_i++){
+                        if (tgf_in[r_i][c_i][s_i].is_visited) { continue; }
+
+                        if (tgf_in[r_i][c_i][s_i].node_type != GROUND ) { continue; }
+
+                        // if (tgf_in[r_i][c_i][s_i].mean_pt[2] >= max_planar_height+1) { continue; }
+
+                        if (tgf_in[r_i][c_i][s_i].depth >= 0) { continue; }
+
+                        // if (tgf_in[r_i][c_i][s_i].weight > 5*TH_WEIGHT_){
+                            tgf_in[r_i][c_i][s_i].depth = 0;
+                            tgf_in[r_i][c_i][s_i].is_visited = true;
+
+                            TriGridIdx new_dominant_idx = {r_i, c_i, s_i};
+                            searching_idx_queue.push(new_dominant_idx);
+                        // }
+                    }
+                    }
+                    }
+                }
+            }
+            return;
+        };
+
+
+        double getCornerWeight(const TriGridNode<PointType>& node_in, const pcl::PointXYZ &tgt_corner){
+            double xy_dist = sqrt( (node_in.mean_pt[0]-tgt_corner.x)*(node_in.mean_pt[0]-tgt_corner.x)+(node_in.mean_pt[1]-tgt_corner.y)*(node_in.mean_pt[1]-tgt_corner.y) );
+            return (node_in.weight/xy_dist);
+        }
+
+        void setTGFCornersCenters(const TriGridField<PointType>& tgf_in,
+                                std::vector<std::vector<TriGridCorner>>& trigrid_corners_out,
+                                std::vector<std::vector<TriGridCorner>>& trigrid_centers_out) {
+            pcl::PointXYZ corner_TL, corner_BL, corner_BR, corner_TR, corner_C;
+
+            for (int r_i = 0; r_i<rows_; r_i++){
+            for (int c_i = 0; c_i<cols_; c_i++){
+                corner_TL.x = trigrid_corners_out[r_i+1][c_i+1].x; corner_TL.y = trigrid_corners_out[r_i+1][c_i+1].y;   // LT
+                corner_BL.x = trigrid_corners_out[r_i][c_i+1].x;   corner_BL.y = trigrid_corners_out[r_i][c_i+1].y;     // LL
+                corner_BR.x = trigrid_corners_out[r_i][c_i].x;     corner_BR.y = trigrid_corners_out[r_i][c_i].y;       // RL
+                corner_TR.x = trigrid_corners_out[r_i+1][c_i].x;   corner_TR.y = trigrid_corners_out[r_i+1][c_i].y;     // RT
+                corner_C.x = trigrid_centers_out[r_i][c_i].x;    corner_C.y = trigrid_centers_out[r_i][c_i].y;       // Center
+
+                for (int s_i = 0; s_i< (int) tgf_in[r_i][c_i].size();s_i++){
+                    if (tgf_in[r_i][c_i][s_i].node_type != GROUND) { continue; }
+                    if (tgf_in[r_i][c_i][s_i].is_rejection) { continue; }
+                    if (tgf_in[r_i][c_i][s_i].depth == -1) { continue; }
+
+                    switch(s_i){
+                        case 0: // upper Tri-grid bin
+                            // RT / LT / C
+                            trigrid_corners_out[r_i+1][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_TR.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_TR.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i+1][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_TR));
+
+                            trigrid_corners_out[r_i+1][c_i+1].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_TL.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_TL.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i+1][c_i+1].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_TL));
+
+                            trigrid_centers_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_C.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_C.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_centers_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_C));
+
+                            break;
+                        case 1: // left Tri-grid bin
+                            // LT / LL / C
+                            trigrid_corners_out[r_i+1][c_i+1].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_TL.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_TL.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i+1][c_i+1].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_TL));
+
+                            trigrid_corners_out[r_i][c_i+1].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_BL.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_BL.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i][c_i+1].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_BL));
+
+                            trigrid_centers_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_C.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_C.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_centers_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_C));
+
+                            break;
+                        case 2: // lower Tri-grid bin
+                            // LL / RL / C
+                            trigrid_corners_out[r_i][c_i+1].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_BL.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_BL.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i][c_i+1].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_BL));
+
+                            trigrid_corners_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_BR.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_BR.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_BR));
+
+                            trigrid_centers_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_C.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_C.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_centers_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_C));
+                            
+                            break;
+                        case 3: // right Tri-grid bin
+                            // RL / RT / C
+                            trigrid_corners_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_BR.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_BR.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_BR));
+
+                            trigrid_corners_out[r_i+1][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_TR.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_TR.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_corners_out[r_i+1][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_TR));
+
+                            trigrid_centers_out[r_i][c_i].zs.push_back( (-tgf_in[r_i][c_i][s_i].normal(0,0)*corner_C.x - tgf_in[r_i][c_i][s_i].normal(1,0)*corner_C.y-tgf_in[r_i][c_i][s_i].d)/tgf_in[r_i][c_i][s_i].normal(2,0) );
+                            trigrid_centers_out[r_i][c_i].weights.push_back(getCornerWeight(tgf_in[r_i][c_i][s_i],corner_C));
+                            
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            }
+            }
+            return;
+        };
+        
+        TriGridCorner getMeanCorner(const TriGridCorner &corners_in){
+            // get the mean of the corners
+
+            TriGridCorner corners_out;
+            corners_out.x = corners_in.x;
+            corners_out.y = corners_in.y;
+            corners_out.zs.clear();
+            corners_out.weights.clear();
+
+            double weighted_sum_z = 0.0;
+            double sum_w = 0.0;
+            for (int i = 0; i < (int) corners_in.zs.size(); i++){
+                weighted_sum_z += corners_in.zs[i]*corners_in.weights[i];
+                sum_w += corners_in.weights[i];
+            }
+
+            corners_out.zs.push_back(weighted_sum_z/sum_w);
+            corners_out.weights.push_back(sum_w);
+
+            return corners_out;
+        }
+
+        void updateTGFCornersCenters(std::vector<std::vector<TriGridCorner>>& trigrid_corners_out,
+                                    std::vector<std::vector<TriGridCorner>>& trigrid_centers_out) {
+
+            // update corners
+            TriGridCorner updated_corner = empty_trigrid_corner_;
+            for (int r_i = 0; r_i < rows_ +1; r_i++) {
+            for (int c_i = 0; c_i < cols_ +1; c_i++) {
+                if (trigrid_corners_out[r_i][c_i].zs.size() > 0 && trigrid_corners_out[r_i][c_i].weights.size() > 0) {
+                    updated_corner = getMeanCorner(trigrid_corners_out[r_i][c_i]);
+                    trigrid_corners_out[r_i][c_i] = updated_corner;
+                } else {
+                    trigrid_corners_out[r_i][c_i].zs.clear();
+                    trigrid_corners_out[r_i][c_i].weights.clear();
+                }
+            }
+            }        
+
+            // update centers
+            TriGridCorner updated_center = empty_trigrid_center_;
+            for (int r_i = 0; r_i < rows_; r_i++) {
+            for (int c_i = 0; c_i < cols_; c_i++) {
+                if (trigrid_centers_out[r_i][c_i].zs.size() > 0 && trigrid_centers_out[r_i][c_i].weights.size() > 0) {
+                    updated_center = getMeanCorner(trigrid_centers_out[r_i][c_i]);
+                    trigrid_centers_out[r_i][c_i] = updated_center;
+                    // trigrid_centers_out[r_i][c_i].z = get_mean(trigrid_centers_out[r_i][c_i].zs,trigrid_centers_out[r_i][c_i].weights);
+                } else {
+                    trigrid_centers_out[r_i][c_i].zs.clear();
+                    trigrid_centers_out[r_i][c_i].weights.clear();
+                }
+            }
+            }
+
+            return;
+        };
+
+        Eigen::Vector3f convertCornerToEigen(TriGridCorner &corner_in) {
+            Eigen::Vector3f corner_out;
+            if (corner_in.zs.size() != corner_in.weights.size()){
+                ROS_WARN("ERROR in corners");
+            }
+            corner_out[0] = corner_in.x;
+            corner_out[1] = corner_in.y;
+            corner_out[2] = corner_in.zs[0];
+            return corner_out;
+        };
+
+        void revertTraversableNodes(std::vector<std::vector<TriGridCorner>>& trigrid_corners_in,
+                                    std::vector<std::vector<TriGridCorner>>& trigrid_centers_in, 
+                                    TriGridField<PointType>& tgf_out) {
+            Eigen::Vector3f refined_corner_1, refined_corner_2, refined_center;
+            for (int r_i = 0; r_i < rows_; r_i++) {
+            for (int c_i = 0; c_i < cols_; c_i++) {
+            for (int s_i = 0; s_i < (int) tgf_out[r_i][c_i].size(); s_i++) {
+                // set the corners for the current trigrid node
+                switch (s_i)
+                {
+                case 0:
+                    if ( trigrid_corners_in[r_i+1][c_i].zs.size()==0 || trigrid_corners_in[r_i+1][c_i+1].zs.size()==0  || trigrid_centers_in[r_i][c_i].zs.size()==0 ){
+                        if (tgf_out[r_i][c_i][s_i].node_type != NONGROUND){
+                            tgf_out[r_i][c_i][s_i].node_type = UNKNOWN;
+                        }
+                        continue;
+                    }
+                    refined_corner_1 = convertCornerToEigen(trigrid_corners_in[r_i+1][c_i]);
+                    refined_corner_2 = convertCornerToEigen(trigrid_corners_in[r_i+1][c_i+1]);
+                    refined_center = convertCornerToEigen(trigrid_centers_in[r_i][c_i]);
+                    break;
+                case 1:
+                    if ( trigrid_corners_in[r_i+1][c_i+1].zs.size()==0 || trigrid_corners_in[r_i][c_i+1].zs.size()==0  || trigrid_centers_in[r_i][c_i].zs.size()==0 ){
+                        if (tgf_out[r_i][c_i][s_i].node_type != NONGROUND){
+                            tgf_out[r_i][c_i][s_i].node_type = UNKNOWN;
+                        }
+                        continue;
+                    }
+                    refined_corner_1 = convertCornerToEigen(trigrid_corners_in[r_i+1][c_i+1]);
+                    refined_corner_2 = convertCornerToEigen(trigrid_corners_in[r_i][c_i+1]);
+                    refined_center = convertCornerToEigen(trigrid_centers_in[r_i][c_i]);    
+                    break;
+                case 2:
+                    if ( trigrid_corners_in[r_i][c_i+1].zs.size()==0 || trigrid_corners_in[r_i][c_i].zs.size()==0  || trigrid_centers_in[r_i][c_i].zs.size()==0 ){
+                        if (tgf_out[r_i][c_i][s_i].node_type != NONGROUND){
+                            tgf_out[r_i][c_i][s_i].node_type = UNKNOWN;
+                        }
+                        continue;
+                    }
+                    refined_corner_1 = convertCornerToEigen(trigrid_corners_in[r_i][c_i+1]);
+                    refined_corner_2 = convertCornerToEigen(trigrid_corners_in[r_i][c_i]);
+                    refined_center = convertCornerToEigen(trigrid_centers_in[r_i][c_i]);
+                    break;
+                case 3:
+                    if ( trigrid_corners_in[r_i][c_i].zs.size()==0 || trigrid_corners_in[r_i+1][c_i].zs.size()==0  || trigrid_centers_in[r_i][c_i].zs.size()==0 ){
+                        if (tgf_out[r_i][c_i][s_i].node_type != NONGROUND){
+                            tgf_out[r_i][c_i][s_i].node_type = UNKNOWN;
+                        }
+                        continue;
+                    }
+                    refined_corner_1 = convertCornerToEigen(trigrid_corners_in[r_i][c_i]);
+                    refined_corner_2 = convertCornerToEigen(trigrid_corners_in[r_i+1][c_i]);
+                    refined_center = convertCornerToEigen(trigrid_centers_in[r_i][c_i]);
+                    break;
+                default:
+                    ROS_ERROR("WRONG tri-grid indexing");
+                    break;
+                }
+
+                // calculate the refined planar model in the node
+                Eigen::Vector3f udpated_normal = (refined_corner_1-refined_center).cross(refined_corner_2-refined_center);
+                udpated_normal /= udpated_normal.norm();
+                // if (udpated_normal[2] < 0){
+                //     std::cout << "Origin normal: " << tgf_out[r_i][c_i][s_i].normal << std::endl;
+                //     std::cout << "Update normal: " << udpated_normal << std::endl;
+                // }
+                if (udpated_normal(2,0) < TH_NORMAL_ ){   // non-planar
+                    tgf_out[r_i][c_i][s_i].normal = udpated_normal;
+                    tgf_out[r_i][c_i][s_i].node_type = NONGROUND;
+                } else {    
+                    // planar
+                    Eigen::Vector3f updated_mean_pt;
+                    updated_mean_pt[0] = (refined_corner_1[0] + refined_corner_2[0] + refined_center[0])/3;
+                    updated_mean_pt[1] = (refined_corner_1[1] + refined_corner_2[1] + refined_center[1])/3;
+                    updated_mean_pt[2] = (refined_corner_1[2] + refined_corner_2[2] + refined_center[2])/3;
+
+                    tgf_out[r_i][c_i][s_i].normal = udpated_normal;
+                    tgf_out[r_i][c_i][s_i].mean_pt = updated_mean_pt;
+                    tgf_out[r_i][c_i][s_i].d = -(udpated_normal.dot(updated_mean_pt));
+                    tgf_out[r_i][c_i][s_i].th_dist_d = TH_DIST_ - tgf_out[r_i][c_i][s_i].d;
+                    tgf_out[r_i][c_i][s_i].th_outlier_d = -TH_OUTLIER_ - tgf_out[r_i][c_i][s_i].d;
+
+                    tgf_out[r_i][c_i][s_i].node_type = GROUND;
+                }
+            }
+            }
+            }
+
+            return;
+        };
+
+        void fitTGFWiseTraversableTerrainModel(TriGridField<PointType>& tgf,
+                                            std::vector<std::vector<TriGridCorner>>& trigrid_corners,
+                                            std::vector<std::vector<TriGridCorner>>& trigrid_centers) {
+
+            updateTGFCornersCenters(trigrid_corners, trigrid_centers);
+
+            revertTraversableNodes(trigrid_corners, trigrid_centers, tgf);
+            
+            return;
+        };
+
+        void segmentNodeGround(const TriGridNode<PointType>& node_in,
+                                pcl::PointCloud<PointType>& node_ground_out,
+                                pcl::PointCloud<PointType>& node_nonground_out,
+                                pcl::PointCloud<PointType>& node_obstacle_out,
+                                pcl::PointCloud<PointType>& node_outlier_out) {
+            node_ground_out.clear();
+            node_nonground_out.clear();
+            node_obstacle_out.clear();
+            node_outlier_out.clear();
+
+            // segment ground
+            Eigen::MatrixXf points(node_in.ptCloud.points.size(),3);
+            int j = 0; 
+            for (auto& p:node_in.ptCloud.points){
+                points.row(j++)<<p.x, p.y, p.z;
+            }
+
+            Eigen::VectorXf result = points*node_in.normal;
+            for (int r = 0; r<result.rows(); r++){
+                if (result[r]<node_in.th_dist_d){
+                    if (result[r]<node_in.th_outlier_d){
+                        node_outlier_out.push_back(node_in.ptCloud.points[r]);
+                    } else {
+                        node_ground_out.push_back(node_in.ptCloud.points[r]);
+                    }
+                } else {
+                    node_nonground_out.push_back(node_in.ptCloud.points[r]);
+                    if (result[r]<TH_OBSTACLE_HEIGHT_ - node_in.d){
+                        node_obstacle_out.push_back(node_in.ptCloud.points[r]);
+                        node_obstacle_out.points.back().intensity = result[r] + node_in.d;
+                    }
+                }
+            }
+
+            return;
+        }
+
+        void segmentTGFGround(const TriGridField<PointType>& tgf_in, 
+                        pcl::PointCloud<PointType>& ground_cloud_out, 
+                        pcl::PointCloud<PointType>& nonground_cloud_out,
+                        pcl::PointCloud<PointType>& obstacle_cloud_out,
+                        pcl::PointCloud<PointType>& outlier_cloud_out) {
+            ground_cloud_out.clear();
+            nonground_cloud_out.clear();
+            obstacle_cloud_out.clear();
+
+            for (int r_i = 0; r_i < rows_; r_i++){
+            for (int c_i = 0; c_i < cols_; c_i++){
+            for (int s_i = 0; s_i < tgf_in[r_i][c_i].size(); s_i++) {
+                if (!tgf_in[r_i][c_i][s_i].is_curr_data) {
+                    continue;
+                }
+                if (tgf_in[r_i][c_i][s_i].node_type == GROUND) {
+                    segmentNodeGround(tgf_in[r_i][c_i][s_i], ptCloud_nodewise_ground_, ptCloud_nodewise_nonground_, ptCloud_nodewise_obstacle_, ptCloud_nodewise_outliers_);
+                } else {
+                    ptCloud_nodewise_nonground_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                    ptCloud_nodewise_obstacle_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                }
+                ground_cloud_out += ptCloud_nodewise_ground_;
+                nonground_cloud_out += ptCloud_nodewise_nonground_;
+                outlier_cloud_out += ptCloud_nodewise_outliers_;
+                obstacle_cloud_out += ptCloud_nodewise_obstacle_;
+            }
+            }
+            }
+
+            return;
+        };
+
+        void segmentTGFGround_developing(const TriGridField<PointType>& tgf_in, 
+                        pcl::PointCloud<PointType>& ground_cloud_out, 
+                        pcl::PointCloud<PointType>& nonground_cloud_out,
+                        pcl::PointCloud<PointType>& obstacle_cloud_out,
+                        pcl::PointCloud<PointType>& outlier_cloud_out) {
+            ground_cloud_out.clear();
+            nonground_cloud_out.clear();
+            obstacle_cloud_out.clear();
+            TriGridIdx curr_tgf_idx;
+            std::vector<TriGridIdx> adj_idx_vec;
+            pcl::PointCloud<PointType> outlier_tmp;
+            outlier_tmp.clear();
+            outlier_tmp.reserve(NODEWISE_PTCLOUDSIZE);
+            for (int r_i = 0; r_i < rows_; r_i++){
+            for (int c_i = 0; c_i < cols_; c_i++){
+            for (int s_i = 0; s_i < (int) tgf_in[r_i][c_i].size(); s_i++) {
+                if (!tgf_in[r_i][c_i][s_i].is_curr_data) {
+                    continue;
+                }
+                if (tgf_in[r_i][c_i][s_i].node_type == UNKNOWN){
+                    continue;
+                }
+                if (tgf_in[r_i][c_i][s_i].node_type == GROUND) {
+                    segmentNodeGround(tgf_in[r_i][c_i][s_i], ptCloud_nodewise_ground_, ptCloud_nodewise_nonground_, ptCloud_nodewise_obstacle_, ptCloud_nodewise_outliers_);
+                } else {
+                    curr_tgf_idx.row = r_i;
+                    curr_tgf_idx.col = c_i;
+                    curr_tgf_idx.tri = s_i;
+                    
+                    searchAdjacentNodes(curr_tgf_idx, adj_idx_vec);
+                    if (adj_idx_vec.empty()){
+                        ptCloud_nodewise_nonground_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                        ptCloud_nodewise_obstacle_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                    } else {
+                        TriGridIdx highest_adj_tri_idx;
+                        double highest_weight = 0;
+                        bool is_adjacent = false;
+                        for (int adj_i = 0; adj_i<adj_idx_vec.size() ; adj_i++){
+                            if (getTriGridNode(adj_idx_vec[adj_i]).weight < TH_WEIGHT_) {
+                                continue;
+                            }
+                            is_adjacent = true;
+                            if (getTriGridNode(adj_idx_vec[adj_i]).weight  > highest_weight) {
+                                highest_weight = getTriGridNode(adj_idx_vec[adj_i]).weight;
+                                highest_adj_tri_idx = adj_idx_vec[adj_i];
+                            }
+                        }
+
+                        if (is_adjacent){
+                            TriGridNode<PointType> tmp_node = getTriGridNode(highest_adj_tri_idx);
+                            tmp_node.ptCloud = getTriGridNode(curr_tgf_idx).ptCloud;
+                            segmentNodeGround(tmp_node, ptCloud_nodewise_ground_, ptCloud_nodewise_nonground_, ptCloud_nodewise_obstacle_, ptCloud_nodewise_outliers_);
+                        } else {
+                            ptCloud_nodewise_nonground_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                            ptCloud_nodewise_obstacle_ = tgf_in[r_i][c_i][s_i].ptCloud;
+                        }
+                    }
+                }
+                ground_cloud_out += ptCloud_nodewise_ground_;
+                nonground_cloud_out += ptCloud_nodewise_nonground_;
+                obstacle_cloud_out += ptCloud_nodewise_obstacle_;
+                outlier_cloud_out += ptCloud_nodewise_outliers_;
+            }
+            }
+            }
+
+            return;
+        };
+
+        // functions for visualization
+
+        geometry_msgs::PolygonStamped setPlanarModel (const TriGridNode<PointType>& node_in, const TriGridIdx& node_idx) {
+            geometry_msgs::PolygonStamped polygon_out;
+            polygon_out.header = msg_header_;
+            geometry_msgs::Point32 corner_0, corner_1, corner_2;
+            int r_i = node_idx.row;
+            int c_i = node_idx.col;
+            int s_i = node_idx.tri;
+            if (node_in.node_type == GROUND){
+                switch (s_i){
+                    case 0:
+                        //topx lowy & topx topy
+                        corner_1.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = (-node_in.normal(0,0)*corner_1.x - node_in.normal(1,0)*corner_1.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_2.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = (-node_in.normal(0,0)*corner_2.x - node_in.normal(1,0)*corner_2.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = (-node_in.normal(0,0)*corner_0.x - node_in.normal(1,0)*corner_0.y-node_in.d)/node_in.normal(2,0);
+                        break;
+                    case 1:
+                        //topx topy & lowx topy
+                        corner_1.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = (-node_in.normal(0,0)*corner_1.x - node_in.normal(1,0)*corner_1.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_2.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = (-node_in.normal(0,0)*corner_2.x - node_in.normal(1,0)*corner_2.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = (-node_in.normal(0,0)*corner_0.x - node_in.normal(1,0)*corner_0.y-node_in.d)/node_in.normal(2,0);
+                        break;
+                    case 2:
+                        //lowx topy & lowx lowy
+                        corner_1.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = (-node_in.normal(0,0)*corner_1.x - node_in.normal(1,0)*corner_1.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_2.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = c_i*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = (-node_in.normal(0,0)*corner_2.x - node_in.normal(1,0)*corner_2.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = (-node_in.normal(0,0)*corner_0.x - node_in.normal(1,0)*corner_0.y-node_in.d)/node_in.normal(2,0);
+                        break;
+                    case 3:
+                        //lowx lowy & topx lowy 
+                        corner_1.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = (-node_in.normal(0,0)*corner_1.x - node_in.normal(1,0)*corner_1.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_2.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = (-node_in.normal(0,0)*corner_2.x - node_in.normal(1,0)*corner_2.y-node_in.d)/node_in.normal(2,0);
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = (-node_in.normal(0,0)*corner_0.x - node_in.normal(1,0)*corner_0.y-node_in.d)/node_in.normal(2,0);
+                        break;
+                    default:
+                        break;
+                }        
+            } else {
+                switch (s_i){
+                    case 0:
+                        //topx lowy & topx topy
+                        corner_1.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = -2.0;
+
+                        corner_2.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = -2.0;
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = -2.0;
+                        break;
+                    case 1:
+                        //topx topy & lowx topy
+                        corner_1.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = -2.0;
+
+                        corner_2.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = -2.0;
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = -2.0;
+                        break;
+                    case 2:
+                        //lowx topy & lowx lowy
+                        corner_1.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i+1)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = -2.0;
+
+                        corner_2.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = c_i*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = -2.0;
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = -2.0;
+                        break;
+                    case 3:
+                        //lowx lowy & topx lowy 
+                        corner_1.x = (r_i)*TGF_RESOLUTION_+tgf_min_x; corner_1.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_1.z = -2.0;
+
+                        corner_2.x = (r_i+1)*TGF_RESOLUTION_+tgf_min_x; corner_2.y = (c_i)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_2.z = -2.0;
+
+                        corner_0.x = (r_i+0.5)*TGF_RESOLUTION_+tgf_min_x; corner_0.y = (c_i+0.5)*TGF_RESOLUTION_+tgf_min_y; 
+                        corner_0.z = -2.0;
+                        break;
+                    default:
+                        std::cout << "the error in sub-idx" << std::endl;
+                        break;
+                }
+            }
+
+            polygon_out.polygon.points.reserve(3); 
+            polygon_out.polygon.points.push_back(corner_0);
+            polygon_out.polygon.points.push_back(corner_2);
+            polygon_out.polygon.points.push_back(corner_1);
+            return polygon_out;
+        }
+
+        void publishTriGridFieldGraph() {
+            viz_trigrid_polygons_.header = msg_header_;
+            viz_trigrid_polygons_.polygons.clear();
+            viz_trigrid_polygons_.likelihood.clear();
+
+            // visualize the graph: nodes
+            for (int r_i = 0; r_i < rows_; r_i++){
+            for (int c_i = 0; c_i < cols_; c_i++){
+            for (int s_i = 0; s_i < (int) trigrid_field_[r_i][c_i].size(); s_i++){
+                TriGridIdx curr_idx = {r_i, c_i, s_i};
+                if (trigrid_field_[r_i][c_i][s_i].node_type == GROUND) {
+                    viz_trigrid_polygons_.polygons.push_back(setPlanarModel(trigrid_field_[r_i][c_i][s_i], curr_idx));
+                    // if (trigrid_field_[r_i][c_i][s_i].normal[2] < 0.99) {
+                    //     viz_trigrid_polygons_.likelihood.push_back(0.25);
+                    // } else {
+                        viz_trigrid_polygons_.likelihood.push_back(0.0);
+                    // }
+                    
+                } else if (trigrid_field_[r_i][c_i][s_i].node_type == NONGROUND) {
+                    // continue;
+                    viz_trigrid_polygons_.polygons.push_back(setPlanarModel(trigrid_field_[r_i][c_i][s_i], curr_idx));
+                    viz_trigrid_polygons_.likelihood.push_back(1.0);
+
+                } else if (trigrid_field_[r_i][c_i][s_i].node_type == UNKNOWN) {
+                    // continue;
+                    if (trigrid_field_[r_i][c_i][s_i].is_curr_data) {
+                        viz_trigrid_polygons_.polygons.push_back(setPlanarModel(trigrid_field_[r_i][c_i][s_i], curr_idx));
+                        viz_trigrid_polygons_.likelihood.push_back(0.5);
+                    }
+                } else {
+                    ROS_WARN("Unknown Node Type");
+                }            
+            }
+            }
+            }
+
+            //visualize the graph: edges
+            viz_trigrid_edges_.header = msg_header_;
+            viz_trigrid_edges_.points.clear();
+            geometry_msgs::Point src_pt;
+            geometry_msgs::Point tgt_pt;
+            for (int e_i = 0; e_i < (int) trigrid_edges_.size(); e_i++){
+                if (trigrid_edges_[e_i].is_traversable){
+                    viz_trigrid_edges_.color.a = 1.0;
+                    viz_trigrid_edges_.color.r = 1.0;
+                    viz_trigrid_edges_.color.g = 1.0;
+                    viz_trigrid_edges_.color.b = 0.0;
+                } else {
+                    viz_trigrid_edges_.color.a = 0.1;
+                    viz_trigrid_edges_.color.r = 1.0;
+                    viz_trigrid_edges_.color.g = 1.0;
+                    viz_trigrid_edges_.color.b = 1.0;
+                }
+
+                src_pt.x = trigrid_field_[trigrid_edges_[e_i].Pair.first.row][trigrid_edges_[e_i].Pair.first.col][trigrid_edges_[e_i].Pair.first.tri].mean_pt[0];
+                src_pt.y = trigrid_field_[trigrid_edges_[e_i].Pair.first.row][trigrid_edges_[e_i].Pair.first.col][trigrid_edges_[e_i].Pair.first.tri].mean_pt[1];
+                src_pt.z = trigrid_field_[trigrid_edges_[e_i].Pair.first.row][trigrid_edges_[e_i].Pair.first.col][trigrid_edges_[e_i].Pair.first.tri].mean_pt[2];
+
+                tgt_pt.x = trigrid_field_[trigrid_edges_[e_i].Pair.second.row][trigrid_edges_[e_i].Pair.second.col][trigrid_edges_[e_i].Pair.second.tri].mean_pt[0];
+                tgt_pt.y = trigrid_field_[trigrid_edges_[e_i].Pair.second.row][trigrid_edges_[e_i].Pair.second.col][trigrid_edges_[e_i].Pair.second.tri].mean_pt[1];
+                tgt_pt.z = trigrid_field_[trigrid_edges_[e_i].Pair.second.row][trigrid_edges_[e_i].Pair.second.col][trigrid_edges_[e_i].Pair.second.tri].mean_pt[2];
+
+                viz_trigrid_edges_.points.push_back(src_pt);
+                viz_trigrid_edges_.points.push_back(tgt_pt);
+            }
+
+            // pub_trigrid_nodes_.publish(viz_trigrid_polygons_);
+            // pub_trigrid_edges_.publish(viz_trigrid_edges_);
+            return;
+        };
+
+        // void publishTriGridCorners() {
+        //     viz_trigrid_corners_.header = cloud_header_;
+        //     viz_trigrid_corners_.points.clear();
+
+        //     TriGridCorner curr_corner;
+        //     pcl::PointXYZ corner_pt;
+        //     // for corners
+        //     for (int r_i = 0; r_i < (int) trigrid_corners_.size(); r_i++){
+        //     for (int c_i = 0; c_i < (int) trigrid_corners_[0].size(); c_i++){
+        //         curr_corner = trigrid_corners_[r_i][c_i];
+        //         if (curr_corner.zs.size() != curr_corner.weights.size()){
+        //             ROS_WARN("ERROR in corners");
+        //         }
+        //         for (int i = 0; i < (int) curr_corner.zs.size(); i++){
+        //             corner_pt.x = curr_corner.x;
+        //             corner_pt.y = curr_corner.y;
+        //             corner_pt.z = curr_corner.zs[i];
+        //             viz_trigrid_corners_.points.push_back(corner_pt);
+        //         }
+        //     }
+        //     }
+
+        //     // for centers
+        //     for (int r_i = 0; r_i < (int) trigrid_centers_.size(); r_i++){
+        //     for (int c_i = 0; c_i < (int) trigrid_centers_[0].size(); c_i++){
+        //         curr_corner = trigrid_centers_[r_i][c_i];
+        //         if (curr_corner.zs.size() != curr_corner.weights.size()){
+        //             ROS_WARN("ERROR in corners");
+        //         }
+        //         for (int i = 0; i < (int) curr_corner.zs.size(); i++){
+        //             corner_pt.x = curr_corner.x;
+        //             corner_pt.y = curr_corner.y;
+        //             corner_pt.z = curr_corner.zs[i];
+        //             viz_trigrid_corners_.points.push_back(corner_pt);
+        //         }
+        //     }
+        //     }
+
+        //     pub_trigrid_corners_.publish(viz_trigrid_corners_);
+        //     return;
+        // };
+
+        // sensor_msgs::PointCloud2 convertCloudToRosMsg(pcl::PointCloud<PointType>& cloud, std::string &frame_id) {
+        //     sensor_msgs::PointCloud2 cloud_msg;
+        //     pcl::toROSMsg(cloud, cloud_msg);
+        //     cloud_msg.header.frame_id = frame_id;
+        //     return cloud_msg;
+        // };
+
+        // void publishPointClouds(){
+        //     ptCloud_tgfwise_ground_.header = cloud_header_;
+        //     pub_tgseg_ground_cloud.publish(convertCloudToRosMsg(ptCloud_tgfwise_ground_, cloud_header_.frame_id));
+            
+        //     ptCloud_tgfwise_nonground_.header = cloud_header_;
+        //     pub_tgseg_nonground_cloud.publish(convertCloudToRosMsg(ptCloud_tgfwise_nonground_, cloud_header_.frame_id));
+
+        //     ptCloud_tgfwise_outliers_.header = cloud_header_;
+        //     pub_tgseg_outliers_cloud.publish(convertCloudToRosMsg(ptCloud_tgfwise_outliers_, cloud_header_.frame_id));
+        // }
+    };
+}
+#endif
diff --git a/params/iri_ground_segmentation.yaml b/params/iri_ground_segmentation.yaml
index e8e551363078dac1077a08709a61528be33f5660..6f4b292e118fff76d3bdde19233a6e0bad3cd00a 100644
--- a/params/iri_ground_segmentation.yaml
+++ b/params/iri_ground_segmentation.yaml
@@ -6,11 +6,11 @@ iri_ground_segmentation: {
   robot_height: 2.00,       ## All obstacle points above this value (w.r.t the z ground predicted by the algorithm) are treated as overhanging obstacles 
 
   # Parameters affecting the exploration of the pointcloud
-  ROI_delta_x_and_y: 3.0,    ## This value sets the size of the ROIs: ROI_size = (2*ROI_delta_x_and_y)^2
-  ROI_shadow_area: 7.0, #5.5, #6.0,     ## This value is the same that the above, but only used in the root vertex to overcome the shadow area
+  ROI_delta_x_and_y: 3.0, #4.0,   ## This value sets the size of the ROIs: ROI_size = (2*ROI_delta_x_and_y)^2
+  ROI_shadow_area:   7.0, #6.5,   ## This value is the same that the above, but only used in the root vertex to overcome the shadow area
 
-  ground_reference_search_resolution_deg: 40.0, #6.0, #40.0, #20.0, #40 #12.00, ## It is the angular resolution when generating new ground references (graph nodes),
-                                                        ## it will affect the number of nodes in the graph: lower values generate more nodes
+  ground_reference_search_resolution_deg: 12, #40.0, #6.0, #12.00, ## It is the angular resolution when generating new ground references (graph nodes),
+                                                              ## it will affect the number of nodes in the graph: lower values generate more nodes
 
   elevation_grid_resolution: 2.1, #1.2, #1.5, #1.0 #0.5,   ## This value is used to create the "elevation point cloud" which is a reduction of the original pointcloud, where
                                           ## only the lowest z values in each cell are stored (along with a vector of indexes pointing to the remaining points
@@ -36,7 +36,7 @@ iri_ground_segmentation: {
                                     ## and a big value will increase the number of false ground points)
 
   # Neural Network related parameters
-  use_neural_network: false,
+  use_neural_network: true,
   extract_data_to_external_training_of_the_network: false,
   dataset_filename_with_global_path: '/home/idelpino/Documentos/dataset_rgb_hsv_olbp_10_frame_inc.csv',
   neural_net_filename_with_global_path: '/media/sf_virtual_box_shared/neural_networks/veg_terrain_and_obs_13_features_39_neurons.csv', #five_classes_13_features_39_neurons.csv'
@@ -45,14 +45,14 @@ iri_ground_segmentation: {
   neural_net_number_of_neurons_in_output_layer: 2,
   
   # labeling parameters
-  max_pred_std_dev_for_labelling: 0.5,                   ## ONLY IN USE TO GIVE COLOUR TO DENSE RECONSTRUCTION
+  max_pred_std_dev_for_labelling: 0.333,                 ## ONLY IN USE TO GIVE COLOUR TO DENSE RECONSTRUCTION
   score_threshold: 0.0,                                  ## for assigning ground class label: one means maha. dist. equal to zero, zero means mahalanobis dist equal to maha. thres
-  classify_not_labeled_points_as_obstacles: false,        ## when a point has no reference satisfying the max_pred_std_dev_for_labelling threshold we can leave as unknown or label it as obstacle
+  classify_not_labeled_points_as_obstacles: true,        ## when a point has no reference satisfying the max_pred_std_dev_for_labelling threshold we can leave as unknown or label it as obstacle
   ground_threshold_in_not_analyzed_areas: 0.1,           ## when it is not possible to make a local analysis of the data, we will use the lowest point (the one in elevation_cloud) as ground height, and
                                                          ## all the points above it and below this threshold will be classified as ground 
                                                          
   # visualization and debug parameters
   measure_performance: false,            ## (feature still not debugged) Flag to measure number of execution and execution times of the different functions of the algorithm 
-  show_dense_reconstruction: true,      ## To show a dense ground surface reconstruction using the predictions of the ground mode (colored using the std_dev of z coordinate)   
+  show_dense_reconstruction: false,      ## To show a dense ground surface reconstruction using the predictions of the ground mode (colored using the std_dev of z coordinate)   
                                          ## or alternatively the "elevation point cloud" (useful for parameter tunning) 
 }
diff --git a/src/ground_segmentation_alg.cpp b/src/ground_segmentation_alg.cpp
index c53ef6ae70988cfc9039fb1bcae0efe012f865f2..70c807a7abb918fbfd6e1042729e4eee0ce6ea66 100644
--- a/src/ground_segmentation_alg.cpp
+++ b/src/ground_segmentation_alg.cpp
@@ -199,9 +199,9 @@ void GroundSegmentationAlgorithm::segmentGround(
       vertex.pose.orientation.z = 0.0;
       vertex.pose.orientation.w = 1.0;
 
-      vertex.scale.x = 0.15;
-      vertex.scale.y = 0.15;
-      vertex.scale.z = 0.15;
+      vertex.scale.x = 0.25;
+      vertex.scale.y = 0.25;
+      vertex.scale.z = 0.25;
 
       vertex.color.a = 1.0; // Don't forget to set the alpha!
       if (ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT)
@@ -226,11 +226,14 @@ void GroundSegmentationAlgorithm::segmentGround(
       {
         edges_temp.edges.push_back(*e);
 
+        pcl::PointXYZRGBNormal child_ground_ref = roadmap_vertices_pcl_cloud.points[*e];
+
         visualization_msgs::Marker edge;
         edge.action = visualization_msgs::Marker::ADD;
         edge.header = common_header;
 
-        if (ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT)
+        if (ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT &&
+            child_ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT)
         {
           edge.ns = "edges";
           edge.id = edge_id++;
@@ -249,21 +252,21 @@ void GroundSegmentationAlgorithm::segmentGround(
         p.z = ground_ref.z;
         edge.points.push_back(p);
 
-        pcl::PointXYZRGBNormal child_ground_ref = roadmap_vertices_pcl_cloud.points[*e];
         p.x = child_ground_ref.x;
         p.y = child_ground_ref.y;
         p.z = child_ground_ref.z;
         edge.points.push_back(p);
 
-        edge.scale.x = 0.05;
+        edge.scale.x = 0.1;
 
         edge.pose.orientation.x = 0.0;
         edge.pose.orientation.y = 0.0;
         edge.pose.orientation.z = 0.0;
         edge.pose.orientation.w = 1.0;
 
-        edge.color.a = 0.7; // Don't forget to set the alpha!
-        if (ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT)
+        edge.color.a = 1.0; // Don't forget to set the alpha!
+        if (ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT &&
+            child_ground_ref.data_n[GRND_REF_DATA_N_2_VERTEX_CLASS] == VERTEX_CONNECTED_TO_ROOT)
         {
           edge.color.r = 0.8;
           edge.color.g = 0.53;
@@ -308,14 +311,14 @@ void GroundSegmentationAlgorithm::segmentGround(
         p.z = child_ground_ref.z;
         discarded_edge.points.push_back(p);
 
-        discarded_edge.scale.x = 0.05;
+        discarded_edge.scale.x = 0.1;
 
         discarded_edge.pose.orientation.x = 0.0;
         discarded_edge.pose.orientation.y = 0.0;
         discarded_edge.pose.orientation.z = 0.0;
         discarded_edge.pose.orientation.w = 1.0;
 
-        discarded_edge.color.a = 0.7;
+        discarded_edge.color.a = 1.0;
         switch (discarded_edge_index_and_class.edge_class)
         {
           case CLASS_EDGE_DISCARDED_TOO_MUCH_MAHALANOBIS_DISTANCE: