mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			448 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef MARCHINGSQUARES_HPP
 | 
						|
#define MARCHINGSQUARES_HPP
 | 
						|
 | 
						|
#include <type_traits>
 | 
						|
#include <cstdint>
 | 
						|
#include <vector>
 | 
						|
#include <algorithm>
 | 
						|
#include <cassert>
 | 
						|
 | 
						|
namespace marchsq {
 | 
						|
 | 
						|
// Marks a square in the grid
 | 
						|
struct Coord {
 | 
						|
    long r = 0, c = 0;
 | 
						|
    
 | 
						|
    Coord() = default;
 | 
						|
    explicit Coord(long s) : r(s), c(s) {}
 | 
						|
    Coord(long _r, long _c): r(_r), c(_c) {}
 | 
						|
    
 | 
						|
    size_t seq(const Coord &res) const { return r * res.c + c; }
 | 
						|
    Coord& operator+=(const Coord& b) { r += b.r; c += b.c; return *this; }
 | 
						|
    Coord operator+(const Coord& b) const { Coord a = *this; a += b; return a; }
 | 
						|
};
 | 
						|
 | 
						|
// Closed ring of cell coordinates
 | 
						|
using Ring = std::vector<Coord>;
 | 
						|
 | 
						|
// Specialize this struct to register a raster type for the Marching squares alg
 | 
						|
template<class T, class Enable = void> struct _RasterTraits {
 | 
						|
    
 | 
						|
    // The type of pixel cell in the raster
 | 
						|
    using ValueType = typename T::ValueType;
 | 
						|
    
 | 
						|
    // Value at a given position
 | 
						|
    static ValueType get(const T &raster, size_t row, size_t col);
 | 
						|
    
 | 
						|
    // Number of rows and cols of the raster
 | 
						|
    static size_t rows(const T &raster);
 | 
						|
    static size_t cols(const T &raster);
 | 
						|
};
 | 
						|
 | 
						|
// Specialize this to use parellel loops within the algorithm
 | 
						|
template<class ExecutionPolicy, class Enable = void> struct _Loop {
 | 
						|
    template<class It, class Fn> static void for_each(It from, It to, Fn &&fn)
 | 
						|
    {
 | 
						|
        for (auto it = from; it < to; ++it) fn(*it, size_t(it - from));
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
namespace __impl {
 | 
						|
 | 
						|
template<class T> using RasterTraits = _RasterTraits<std::decay_t<T>>;
 | 
						|
template<class T> using TRasterValue = typename RasterTraits<T>::ValueType;
 | 
						|
 | 
						|
template<class T> size_t rows(const T &raster)
 | 
						|
{
 | 
						|
    return RasterTraits<T>::rows(raster);
 | 
						|
}
 | 
						|
 | 
						|
template<class T> size_t cols(const T &raster)
 | 
						|
{
 | 
						|
    return RasterTraits<T>::cols(raster);
 | 
						|
}
 | 
						|
 | 
						|
template<class T> TRasterValue<T> isoval(const T &rst, const Coord &crd)
 | 
						|
{
 | 
						|
    return RasterTraits<T>::get(rst, crd.r, crd.c);
 | 
						|
}
 | 
						|
 | 
						|
template<class ExecutionPolicy, class It, class Fn>
 | 
						|
void for_each(ExecutionPolicy&& policy, It from, It to, Fn &&fn)
 | 
						|
{
 | 
						|
    _Loop<ExecutionPolicy>::for_each(from, to, fn);
 | 
						|
}
 | 
						|
 | 
						|
// Type of squares (tiles) depending on which vertices are inside an ROI
 | 
						|
// The vertices would be marked a, b, c, d in counter clockwise order from the 
 | 
						|
// bottom left vertex of a square.
 | 
						|
// d --- c
 | 
						|
// |     |
 | 
						|
// |     |
 | 
						|
// a --- b
 | 
						|
enum class SquareTag : uint8_t {
 | 
						|
//     0, 1, 2,  3, 4,  5,  6,   7, 8,  9, 10,  11, 12,  13,  14,  15
 | 
						|
    none, a, b, ab, c, ac, bc, abc, d, ad, bd, abd, cd, acd, bcd, full
 | 
						|
};
 | 
						|
 | 
						|
template<class E> constexpr std::underlying_type_t<E> _t(E e) noexcept
 | 
						|
{
 | 
						|
    return static_cast<std::underlying_type_t<E>>(e);
 | 
						|
}
 | 
						|
 | 
						|
enum class Dir: uint8_t { left, down, right, up, none};
 | 
						|
 | 
						|
static const constexpr Dir NEXT_CCW[] = {
 | 
						|
    /* 00 */ Dir::none,      // SquareTag::none (empty square, nowhere to go)
 | 
						|
    /* 01 */ Dir::left,      // SquareTag::a
 | 
						|
    /* 02 */ Dir::down,      // SquareTag::b
 | 
						|
    /* 03 */ Dir::left,      // SquareTag::ab
 | 
						|
    /* 04 */ Dir::right,     // SquareTag::c
 | 
						|
    /* 05 */ Dir::none,      // SquareTag::ac   (ambiguous case)
 | 
						|
    /* 06 */ Dir::down,      // SquareTag::bc
 | 
						|
    /* 07 */ Dir::left,      // SquareTag::abc
 | 
						|
    /* 08 */ Dir::up,        // SquareTag::d
 | 
						|
    /* 09 */ Dir::up,        // SquareTag::ad
 | 
						|
    /* 10 */ Dir::none,      // SquareTag::bd   (ambiguous case)
 | 
						|
    /* 11 */ Dir::up,        // SquareTag::abd
 | 
						|
    /* 12 */ Dir::right,     // SquareTag::cd
 | 
						|
    /* 13 */ Dir::right,     // SquareTag::acd
 | 
						|
    /* 14 */ Dir::down,      // SquareTag::bcd
 | 
						|
    /* 15 */ Dir::none       // SquareTag::full (full covered, nowhere to go)
 | 
						|
};
 | 
						|
 | 
						|
static const constexpr uint8_t PREV_CCW[] = {
 | 
						|
    /* 00 */ 1 << _t(Dir::none),
 | 
						|
    /* 01 */ 1 << _t(Dir::up),      
 | 
						|
    /* 02 */ 1 << _t(Dir::left),
 | 
						|
    /* 03 */ 1 << _t(Dir::left),     
 | 
						|
    /* 04 */ 1 << _t(Dir::down),     
 | 
						|
    /* 05 */ 1 << _t(Dir::up) | 1 << _t(Dir::down),      
 | 
						|
    /* 06 */ 1 << _t(Dir::down),
 | 
						|
    /* 07 */ 1 << _t(Dir::down),
 | 
						|
    /* 08 */ 1 << _t(Dir::right),
 | 
						|
    /* 09 */ 1 << _t(Dir::up),
 | 
						|
    /* 10 */ 1 << _t(Dir::left) | 1 << _t(Dir::right), 
 | 
						|
    /* 11 */ 1 << _t(Dir::left),   
 | 
						|
    /* 12 */ 1 << _t(Dir::right),
 | 
						|
    /* 13 */ 1 << _t(Dir::up),
 | 
						|
    /* 14 */ 1 << _t(Dir::right), 
 | 
						|
    /* 15 */ 1 << _t(Dir::none)  
 | 
						|
};
 | 
						|
 | 
						|
const constexpr uint8_t DIRMASKS[] = {
 | 
						|
    /*left: */ 0x01, /*down*/ 0x12, /*right */0x21, /*up*/ 0x10, /*none*/ 0x00
 | 
						|
};
 | 
						|
 | 
						|
inline Coord step(const Coord &crd, Dir d)
 | 
						|
{
 | 
						|
    uint8_t dd = DIRMASKS[uint8_t(d)];
 | 
						|
    return {crd.r - 1 + (dd & 0x0f), crd.c - 1 + (dd >> 4)};
 | 
						|
}
 | 
						|
 | 
						|
template<class Rst> class Grid {
 | 
						|
    const Rst *            m_rst = nullptr;
 | 
						|
    Coord                  m_cellsize, m_res_1, m_window, m_gridsize, m_grid_1;
 | 
						|
    std::vector<uint8_t>   m_tags;     // Assign tags to each square
 | 
						|
 | 
						|
    Coord rastercoord(const Coord &crd) const
 | 
						|
    {
 | 
						|
        return {(crd.r - 1) * m_window.r, (crd.c - 1) * m_window.c};
 | 
						|
    }
 | 
						|
 | 
						|
    Coord bl(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, 0}; }
 | 
						|
    Coord br(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, m_res_1.c}; }
 | 
						|
    Coord tr(const Coord &crd) const { return tl(crd) + Coord{0, m_res_1.c}; }
 | 
						|
    Coord tl(const Coord &crd) const { return rastercoord(crd); }
 | 
						|
    
 | 
						|
    bool is_within(const Coord &crd)
 | 
						|
    {
 | 
						|
        long R = rows(*m_rst), C = cols(*m_rst);
 | 
						|
        return crd.r >= 0 && crd.r < R && crd.c >= 0 && crd.c < C;
 | 
						|
    };
 | 
						|
 | 
						|
    // Calculate the tag for a cell (or square). The cell coordinates mark the
 | 
						|
    // top left vertex of a square in the raster. v is the isovalue
 | 
						|
    uint8_t get_tag_for_cell(const Coord &cell, TRasterValue<Rst> v)
 | 
						|
    {        
 | 
						|
        Coord sqr[] = {bl(cell), br(cell), tr(cell), tl(cell)};
 | 
						|
        
 | 
						|
        uint8_t t = ((is_within(sqr[0]) && isoval(*m_rst, sqr[0]) >= v)) +
 | 
						|
                    ((is_within(sqr[1]) && isoval(*m_rst, sqr[1]) >= v) << 1) +
 | 
						|
                    ((is_within(sqr[2]) && isoval(*m_rst, sqr[2]) >= v) << 2) +
 | 
						|
                    ((is_within(sqr[3]) && isoval(*m_rst, sqr[3]) >= v) << 3);
 | 
						|
        
 | 
						|
        assert(t < 16);
 | 
						|
        return t;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Get a cell coordinate from a sequential index
 | 
						|
    Coord coord(size_t i) const
 | 
						|
    {
 | 
						|
        return {long(i) / m_gridsize.c, long(i) % m_gridsize.c};
 | 
						|
    }
 | 
						|
 | 
						|
    size_t seq(const Coord &crd) const { return crd.seq(m_gridsize); }
 | 
						|
    
 | 
						|
    bool is_visited(size_t idx, Dir d = Dir::none) const
 | 
						|
    {
 | 
						|
        SquareTag t = get_tag(idx);
 | 
						|
        uint8_t ref = d == Dir::none ? PREV_CCW[_t(t)] : uint8_t(1 << _t(d));
 | 
						|
        return t == SquareTag::full || t == SquareTag::none ||
 | 
						|
               ((m_tags[idx] & 0xf0) >> 4) == ref;
 | 
						|
    }
 | 
						|
    
 | 
						|
    void set_visited(size_t idx, Dir d = Dir::none)
 | 
						|
    {
 | 
						|
        m_tags[idx] |= (1 << (_t(d)) << 4);
 | 
						|
    }
 | 
						|
    
 | 
						|
    bool is_ambiguous(size_t idx) const
 | 
						|
    {
 | 
						|
        SquareTag t = get_tag(idx);
 | 
						|
        return t == SquareTag::ac || t == SquareTag::bd;
 | 
						|
    }
 | 
						|
 | 
						|
    // Search for a new starting square
 | 
						|
    size_t search_start_cell(size_t i = 0) const
 | 
						|
    {
 | 
						|
        // Skip ambiguous tags as starting tags due to unknown previous
 | 
						|
        // direction.
 | 
						|
        while ((i < m_tags.size()) && (is_visited(i) || is_ambiguous(i))) ++i;
 | 
						|
        
 | 
						|
        return i;
 | 
						|
    }
 | 
						|
    
 | 
						|
    SquareTag get_tag(size_t idx) const { return SquareTag(m_tags[idx] & 0x0f); }
 | 
						|
        
 | 
						|
    Dir next_dir(Dir prev, SquareTag tag) const
 | 
						|
    {
 | 
						|
        // Treat ambiguous cases as two separate regions in one square.
 | 
						|
        switch (tag) {
 | 
						|
        case SquareTag::ac:
 | 
						|
            switch (prev) {
 | 
						|
            case Dir::down: return Dir::right;
 | 
						|
            case Dir::up:   return Dir::left;
 | 
						|
            default:        assert(false); return Dir::none;
 | 
						|
            }
 | 
						|
        case SquareTag::bd:
 | 
						|
            switch (prev) {
 | 
						|
            case Dir::right: return Dir::up;
 | 
						|
            case Dir::left:  return Dir::down;
 | 
						|
            default:         assert(false); return Dir::none;
 | 
						|
            }
 | 
						|
        default:
 | 
						|
            return NEXT_CCW[uint8_t(tag)];
 | 
						|
        }
 | 
						|
        
 | 
						|
        return Dir::none;
 | 
						|
    }
 | 
						|
    
 | 
						|
    struct CellIt {
 | 
						|
        Coord crd; Dir dir= Dir::none; const Rst *grid = nullptr;
 | 
						|
        
 | 
						|
        TRasterValue<Rst> operator*() const { return isoval(*grid, crd); }
 | 
						|
        CellIt& operator++() { crd = step(crd, dir); return *this; }
 | 
						|
        CellIt operator++(int) { CellIt it = *this; ++(*this); return it; }
 | 
						|
        bool operator!=(const CellIt &it) { return crd.r != it.crd.r || crd.c != it.crd.c; }
 | 
						|
        
 | 
						|
        using value_type        = TRasterValue<Rst>;
 | 
						|
        using pointer           = TRasterValue<Rst> *;
 | 
						|
        using reference         = TRasterValue<Rst> &;
 | 
						|
        using difference_type   = long;
 | 
						|
        using iterator_category = std::forward_iterator_tag;
 | 
						|
    };
 | 
						|
    
 | 
						|
    // Two cell iterators representing an edge of a square. This is then
 | 
						|
    // used for binary search for the first active pixel on the edge.
 | 
						|
    struct Edge { CellIt from, to; };
 | 
						|
    
 | 
						|
    Edge _edge(const Coord &ringvertex) const
 | 
						|
    {
 | 
						|
        size_t idx = ringvertex.r;
 | 
						|
        Coord cell = coord(idx);
 | 
						|
        uint8_t tg = m_tags[ringvertex.r];
 | 
						|
        SquareTag t = SquareTag(tg & 0x0f);
 | 
						|
        
 | 
						|
        switch (t) {
 | 
						|
        case SquareTag::a:
 | 
						|
        case SquareTag::ab:
 | 
						|
        case SquareTag::abc:
 | 
						|
            return {{tl(cell), Dir::down,  m_rst}, {bl(cell)}};
 | 
						|
        case SquareTag::b:
 | 
						|
        case SquareTag::bc:
 | 
						|
        case SquareTag::bcd:
 | 
						|
            return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
 | 
						|
        case SquareTag::c:
 | 
						|
            return {{br(cell), Dir::up,    m_rst}, {tr(cell)}};
 | 
						|
        case SquareTag::ac:
 | 
						|
            switch (Dir(ringvertex.c)) {
 | 
						|
            case Dir::left:  return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
 | 
						|
            case Dir::right: return {{br(cell), Dir::up,   m_rst}, {tr(cell)}};
 | 
						|
            default: assert(false);
 | 
						|
            }
 | 
						|
        case SquareTag::d:
 | 
						|
        case SquareTag::ad:
 | 
						|
        case SquareTag::abd:
 | 
						|
            return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
 | 
						|
        case SquareTag::bd:
 | 
						|
            switch (Dir(ringvertex.c)) {
 | 
						|
            case Dir::down: return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
 | 
						|
            case Dir::up:   return {{tr(cell), Dir::left,  m_rst}, {tl(cell)}};
 | 
						|
            default: assert(false);
 | 
						|
            }
 | 
						|
        case SquareTag::cd:
 | 
						|
        case SquareTag::acd:
 | 
						|
            return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
 | 
						|
        case SquareTag::full:
 | 
						|
        case SquareTag::none: {
 | 
						|
            Coord crd{tl(cell) + Coord{m_cellsize.r / 2, m_cellsize.c / 2}};
 | 
						|
            return {{crd, Dir::none, m_rst}, {crd}};
 | 
						|
        }
 | 
						|
        }
 | 
						|
        
 | 
						|
        return {}; 
 | 
						|
    }
 | 
						|
    
 | 
						|
    Edge edge(const Coord &ringvertex) const
 | 
						|
    {
 | 
						|
        const long R = rows(*m_rst), C = cols(*m_rst);
 | 
						|
        const long R_1 = R - 1, C_1 = C - 1;
 | 
						|
        
 | 
						|
        Edge e = _edge(ringvertex);
 | 
						|
        e.to.dir = e.from.dir;
 | 
						|
        ++e.to;
 | 
						|
        
 | 
						|
        e.from.crd.r = std::min(e.from.crd.r, R_1);
 | 
						|
        e.from.crd.r = std::max(e.from.crd.r, 0l);
 | 
						|
        e.from.crd.c = std::min(e.from.crd.c, C_1);
 | 
						|
        e.from.crd.c = std::max(e.from.crd.c, 0l);
 | 
						|
        
 | 
						|
        e.to.crd.r = std::min(e.to.crd.r, R);
 | 
						|
        e.to.crd.r = std::max(e.to.crd.r, 0l);
 | 
						|
        e.to.crd.c = std::min(e.to.crd.c, C);
 | 
						|
        e.to.crd.c = std::max(e.to.crd.c, 0l);
 | 
						|
        
 | 
						|
        return e;
 | 
						|
    }
 | 
						|
    
 | 
						|
public:
 | 
						|
    explicit Grid(const Rst &rst, const Coord &cellsz, const Coord &overlap)
 | 
						|
        : m_rst{&rst}
 | 
						|
        , m_cellsize{cellsz}
 | 
						|
        , m_res_1{m_cellsize.r - 1, m_cellsize.c - 1}
 | 
						|
        , m_window{overlap.r < cellsz.r ? cellsz.r - overlap.r : cellsz.r,
 | 
						|
                   overlap.c < cellsz.c ? cellsz.c - overlap.c : cellsz.c}
 | 
						|
        , m_gridsize{2 + (long(rows(rst)) - overlap.r) / m_window.r,
 | 
						|
                     2 + (long(cols(rst)) - overlap.c) / m_window.c}
 | 
						|
        , m_tags(m_gridsize.r * m_gridsize.c, 0)
 | 
						|
    {}
 | 
						|
    
 | 
						|
    // Go through the cells and mark them with the appropriate tag.
 | 
						|
    template<class ExecutionPolicy>
 | 
						|
    void tag_grid(ExecutionPolicy &&policy, TRasterValue<Rst> isoval)
 | 
						|
    {        
 | 
						|
        // parallel for r
 | 
						|
        for_each (std::forward<ExecutionPolicy>(policy),
 | 
						|
                 m_tags.begin(), m_tags.end(),
 | 
						|
                 [this, isoval](uint8_t& tag, size_t idx) {
 | 
						|
            tag = get_tag_for_cell(coord(idx), isoval);
 | 
						|
        });
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Scan for the rings on the tagged grid. Each ring vertex stores the
 | 
						|
    // sequential index of the cell and the next direction (Dir).
 | 
						|
    // This info can be used later to calculate the exact raster coordinate.
 | 
						|
    std::vector<Ring> scan_rings()
 | 
						|
    {
 | 
						|
        std::vector<Ring> rings;
 | 
						|
        size_t startidx = 0;
 | 
						|
        while ((startidx = search_start_cell(startidx)) < m_tags.size()) {
 | 
						|
            Ring ring;
 | 
						|
            
 | 
						|
            size_t idx = startidx;
 | 
						|
            Dir prev = Dir::none, next = next_dir(prev, get_tag(idx));
 | 
						|
            
 | 
						|
            while (next != Dir::none && !is_visited(idx, prev)) {
 | 
						|
                Coord ringvertex{long(idx), long(next)};
 | 
						|
                ring.emplace_back(ringvertex);
 | 
						|
                set_visited(idx, prev);
 | 
						|
                
 | 
						|
                idx  = seq(step(coord(idx), next));
 | 
						|
                prev = next;
 | 
						|
                next = next_dir(next, get_tag(idx));
 | 
						|
            }
 | 
						|
            
 | 
						|
            // To prevent infinite loops in case of degenerate input
 | 
						|
            if (next == Dir::none) m_tags[startidx] = _t(SquareTag::none);
 | 
						|
            
 | 
						|
            if (ring.size() > 1) {
 | 
						|
                ring.pop_back();
 | 
						|
                rings.emplace_back(ring);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        
 | 
						|
        return rings;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Calculate the exact raster position from the cells which store the
 | 
						|
    // sequantial index of the square and the next direction
 | 
						|
    template<class ExecutionPolicy>
 | 
						|
    void interpolate_rings(ExecutionPolicy && policy,
 | 
						|
                           std::vector<Ring> &rings,
 | 
						|
                           TRasterValue<Rst>  isov)
 | 
						|
    {
 | 
						|
        for_each(std::forward<ExecutionPolicy>(policy),
 | 
						|
                 rings.begin(), rings.end(), [this, isov] (Ring &ring, size_t)
 | 
						|
        {
 | 
						|
            for (Coord &ringvertex : ring) {
 | 
						|
                Edge e = edge(ringvertex);
 | 
						|
                
 | 
						|
                CellIt found = std::lower_bound(e.from, e.to, isov);
 | 
						|
                ringvertex = found.crd;
 | 
						|
            }
 | 
						|
        });
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template<class Raster, class ExecutionPolicy>
 | 
						|
std::vector<marchsq::Ring> execute_with_policy(ExecutionPolicy &&   policy,
 | 
						|
                                               const Raster &       raster,
 | 
						|
                                               TRasterValue<Raster> isoval,
 | 
						|
                                               Coord windowsize = {})
 | 
						|
{
 | 
						|
    if (!rows(raster) || !cols(raster)) return {};
 | 
						|
    
 | 
						|
    size_t ratio = cols(raster) / rows(raster);
 | 
						|
    
 | 
						|
    if (!windowsize.r) windowsize.r = 2;
 | 
						|
    if (!windowsize.c)
 | 
						|
        windowsize.c = std::max(2l, long(windowsize.r * ratio));
 | 
						|
    
 | 
						|
    Coord overlap{1};
 | 
						|
    
 | 
						|
    Grid<Raster> grid{raster, windowsize, overlap};
 | 
						|
    
 | 
						|
    grid.tag_grid(std::forward<ExecutionPolicy>(policy), isoval);
 | 
						|
    std::vector<marchsq::Ring> rings = grid.scan_rings();
 | 
						|
    grid.interpolate_rings(std::forward<ExecutionPolicy>(policy), rings, isoval);
 | 
						|
    
 | 
						|
    return rings;
 | 
						|
}
 | 
						|
 | 
						|
template<class Raster>
 | 
						|
std::vector<marchsq::Ring> execute(const Raster &raster,
 | 
						|
                                   TRasterValue<Raster> isoval,
 | 
						|
                                   Coord                windowsize = {})
 | 
						|
{
 | 
						|
    return execute_with_policy(nullptr, raster, isoval, windowsize);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace __impl
 | 
						|
 | 
						|
using __impl::execute_with_policy;
 | 
						|
using __impl::execute;
 | 
						|
 | 
						|
} // namespace marchsq
 | 
						|
 | 
						|
#endif // MARCHINGSQUARES_HPP
 |