<commit_before>//
// file : memory_allocator.hpp
// in : file:///home/tim/projects/persistence/persistence/tools/memory_allocator.hpp
//
// created by : Timothée Feuillet on linux.site
// date: 22/09/2014 11:00:31
//
//
// Copyright (c) 2014-2016 Timothée Feuillet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#ifndef __N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__
# define __N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__

#include <cstdlib>
#include <new>
#include <cstddef>
#include <cstring>

namespace neam
{
  namespace cr
  {
    /// \brief a chunked memory pool that can produce contiguous output.
    /// \note if an allocation size is < sizeof(uint64_t), then you could assume the allocation will never fail:
    ///       if the allocation fails, the function return a pointer to a valid area (an internal field) and set the \e failed flag to true.
    ///       so allocation of very small size will alway give a valid pointer.
    class memory_allocator
    {
      public:
        /// \brief default constructor
        memory_allocator()
        {
          failed = false;
        }

        /// \brief a sub-memory_allocator: manage an allocated area of another memory_allocator
        /// \param[in,out] fallback is used when the allocated memory is greater than the \p size of the \p area
        ///                is nullptr, when the user request an allocation of size greater than the remaining area space, the allocation will fail.
        memory_allocator(void *area, size_t size, memory_allocator *fallback, bool unstack_fallbacks = true)

        {
          if (unstack_fallbacks)
          {
            while (fallback->fallback_allocator)
              fallback = fallback->fallback_allocator;
          }
          fallback_allocator = fallback;

          first = new(std::nothrow) memory_chunk;
          last = first;
          first->data = reinterpret_cast<uint8_t *>(area);
          first->size = size;
          first->do_not_destroy = true;

          use_fallback = true;
          failed = false;
        }

        ~memory_allocator()
        {
          clear();
        }

        /// \brief return whether or not an allocation has failed.
        bool has_failed() const
        {
          return failed;
        }

        /// \brief unset the failed flag
        void clear_failed()
        {
          failed = false;
        }

        /// \brief allocate \e count bytes at the end of the pool
        /// \return \b nullptr if allocation failed.
        void *allocate(size_t count)
        {
          if (!first || last->end_offset + count > last->size) // allocate a chunk (no chunk or no place in the last chunk)
          {
            if (use_fallback) // shortcut here when we have to use the fallback
            {
              last->end_offset = last->size; // make sure we won't allocate anything else.
              if (!fallback_allocator)
              {
                failed = true;
                if (count <= sizeof(fallback_small))
                  return &fallback_small;
                return nullptr;
              }

              void *ptr = fallback_allocator->allocate(count);
              if (!ptr)
              {
                failed = true;
                return nullptr;
              }
              return ptr;
            }

            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              failed = true;
              delete nchk;

              // never fail for small allocations
              if (count <= sizeof(fallback_small))
                return &fallback_small;
              return nullptr;
            }

            nchk->end_offset = count;
            if (first)
            {
              last->next = nchk;
              nchk->prev = last;
              last = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
            pool_size += count;
            return nchk->data;
          }

          // there is enough room in the last chunk
          void *data = last->data + last->end_offset;
          last->end_offset += count;
          pool_size += count;
          return data;
        }

        /// \brief allocate \e count bytes at the front of the pool
        /// \return \b nullptr if allocation failed.
        void *allocate_front(size_t count)
        {
          if (!first || first->start_offset < count) // allocate a chunk (no chunk or no place left in the first chunk)
          {
            if (use_fallback) // shortcut here when we have to use the fallback
            {
              failed = false;
              return nullptr;
            }

            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              failed = true;
              delete nchk;

              // never fail for small allocations
              if (count <= sizeof(fallback_small))
                return &fallback_small;

              return nullptr;
            }

            nchk->end_offset = nchk->size;
            nchk->start_offset = count;
            if (first)
            {
              first->prev = nchk;
              nchk->next = first;
              first = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
            pool_size += count;
            return nchk->data;
          }

          // there is enough room in the last chunk
          first->start_offset -= count;
          pool_size += count;
          return first->data + first->start_offset;
        }

        /// \brief remove \e count bytes at the end of the pool
        void pop(size_t count)
        {
          if (!last)
            return;
          if (count >= pool_size)
            return clear();

          while (count)
          {
            if (last != first && (last->end_offset - last->start_offset) < count)
            {
              pool_size -= last->end_offset - last->start_offset;
              count -= last->end_offset - last->start_offset;
              last->prev->next = nullptr;
              memory_chunk *lst = last;
              last = last->prev;
              delete lst;
            }
            else if (last == first && (last->end_offset - last->start_offset) < count)
            {
              if (use_fallback)
              {
                full_allocator = false;
                first->end_offset = 0;
              }
              else
              {
                delete first;
                first = nullptr;
                last = nullptr;
              }
              pool_size = 0;
              return;
            }
            else
            {
              last->end_offset -= count;
              pool_size -= count;
              return;
            }
          }
        }

        /// \brief remove \e count bytes at the start of the pool
        void pop_front(size_t count)
        {
          if (!first || !pool_size)
            return;
          if (count >= pool_size)
            return clear();

          while (count)
          {
            if (last != first && (first->end_offset - first->start_offset) < count)
            {
              pool_size -= first->end_offset - first->start_offset;
              count -= first->end_offset - first->start_offset;
              first->prev->next = nullptr;
              memory_chunk *lst = first;
              first = first->prev;
              delete lst;
            }
            else if (last == first && (last->end_offset - last->start_offset) < count)
            {
              delete first;
              first = nullptr;
              last = nullptr;
              pool_size = 0;
              return;
            }
            else
            {
              first->start_offset += count;
              pool_size -= count;
              return;
            }
          }
        }

        /// \brief makes sure there will be enough contiguous space.
        /// Don't mark memory as allocated, but can skip the end of a chunk if there isn't enough space.
        /// \note doesn't set the failed flag is any memory allocation failure occurs.
        bool preallocate_contiguous(size_t count)
        {
          if (use_fallback)
          {
            if (last->end_offset + count <= last->size) // already preallocated, already contiguous
              return true;
            else // use the fallback, mark the buffer as full, _here will return fallback's _here.
            {
              if (!fallback_allocator)
                return false;
              last->end_offset = last->size; // make it full (no further allocations will takes place on this allocator)
              full_allocator = true;
              return fallback_allocator->preallocate_contiguous(count);
            }
          }

          if (!first || last->end_offset + count > last->size) // allocate a chunk (no chunk or no place in the last chunk)
          {
            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              delete nchk;

              return false;
            }

            nchk->end_offset = 0;
            if (first)
            {
              last->next = nchk;
              nchk->prev = last;
              last = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
          }
          return true;
        }

        /// \brief make the data contiguous.
        /// \note don't free the memory !!!
        void *get_contiguous_data()
        {
          // easy cases:
          if (!first)
            return nullptr;
          if (!first->next && first->start_offset == 0) // already contiguous
            return first->data;

          memory_chunk *new_chk = new memory_chunk;
          new_chk->data = reinterpret_cast<uint8_t *>(operator new(pool_size, std::nothrow));
          if (!new_chk->data)
          {
            delete new_chk;
            return nullptr;
          }

          new_chk->size = pool_size;
          new_chk->start_offset = 0;
          new_chk->end_offset = pool_size;

          size_t idx = 0;
          memory_chunk *next = nullptr;
          for (memory_chunk *chr = first; chr; chr = next)
          {
            memcpy(new_chk->data + idx, chr->data + chr->start_offset, chr->end_offset - chr->start_offset);
            idx += chr->end_offset - chr->start_offset;
            next = chr->next;
            delete chr;
          }

          first = new_chk;
          last = new_chk;
          return new_chk->data;
        }

        /// \brief give the the data ownership, return the pointer to the data and clear the pool
        /// \note it's up to the user to delete this pointer.
        /// \see get_contiguous_data()
        /// \see clear()
        void *give_up_data()
        {
          void *ret = get_contiguous_data();
          if (first)
            first->data = nullptr;
          clear();
          return ret;
        }

        /// \brief empty the memory pool, delete every allocated memory.
        void clear()
        {
          memory_chunk *next = nullptr;
          for (memory_chunk *chr = first; chr; chr = next)
          {
            next = chr->next;
            delete chr;
          }
          first = nullptr;
          last = nullptr;
          pool_size = 0;
          failed = false;
        }

        /// \brief return the size of the pool
        size_t size() const
        {
          return pool_size;
        }

        /// \brief returns the current pointer.
        void *_here()
        {
          if (full_allocator)
          {
            if (fallback_allocator)
              return fallback_allocator->_here();
          }

          if (!last)
            return nullptr;
          return last->data + last->end_offset;
        }

      private:
        struct memory_chunk
        {
          uint8_t *data = nullptr;

          size_t start_offset = 0;
          size_t end_offset = 0;
          size_t size = 0;

          bool do_not_destroy = false;

          memory_chunk *next = nullptr;
          memory_chunk *prev = nullptr;

          ~memory_chunk()
          {
            if (!do_not_destroy)
              delete data;
          }
        };

      private:
        constexpr static size_t chunk_size = 8192 * 10; // 10 * 8Kio per-chunk

      private:
        memory_chunk *first = nullptr;
        memory_chunk *last = nullptr;
        size_t pool_size = 0;
        bool failed = false;
        uint64_t fallback_small; ///< \brief used when running out of memory and if the allocated size is lower than

        bool use_fallback = false;
        memory_allocator *fallback_allocator = nullptr;
        bool full_allocator = false; ///< \brief _here will return fallback_allocator->_here() or nullptr.
    };
  } // namespace cr
} // namespace neam

#endif /*__N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__*/

// kate: indent-mode cstyle; indent-width 2; replace-tabs on;

<commit_msg>[MEM/ALLOC]: Improve the memory allocator<commit_after>//
// file : memory_allocator.hpp
// in : file:///home/tim/projects/persistence/persistence/tools/memory_allocator.hpp
//
// created by : Timothée Feuillet on linux.site
// date: 22/09/2014 11:00:31
//
//
// Copyright (c) 2014-2016 Timothée Feuillet
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//

#ifndef __N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__
# define __N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__

#include <cstdlib>
#include <new>
#include <cstddef>
#include <cstdint>
#include <cstring>

namespace neam
{
  namespace cr
  {
    /// \brief a chunked memory pool that can produce contiguous output.
    /// \note if an allocation size is < sizeof(uint64_t), then you could assume the allocation will never fail:
    ///       if the allocation fails, the function return a pointer to a valid area (an internal field) and set the \e failed flag to true.
    ///       so allocation of very small size will alway give a valid pointer.
    class memory_allocator
    {
      public:
        /// \brief default constructor
        memory_allocator()
        {
          failed = false;
        }

        /// \brief a sub-memory_allocator: manage an allocated area of another memory_allocator
        /// \param[in,out] fallback is used when the allocated memory is greater than the \p size of the \p area
        ///                is nullptr, when the user request an allocation of size greater than the remaining area space, the allocation will fail.
        memory_allocator(void *area, size_t size, memory_allocator *fallback, bool unstack_fallbacks = true)

        {
          if (unstack_fallbacks)
          {
            while (fallback->fallback_allocator)
              fallback = fallback->fallback_allocator;
          }
          fallback_allocator = fallback;

          first = new(std::nothrow) memory_chunk;
          last = first;
          first->data = reinterpret_cast<uint8_t *>(area);
          first->size = size;
          first->do_not_destroy = true;

          use_fallback = true;
          failed = false;
        }

        /// \brief Move constructor
        memory_allocator(memory_allocator &&o)
         : first(o.first), last(o.last), pool_size(o.pool_size), failed(o.failed),
           use_fallback(o.use_fallback), fallback_allocator(o.fallback_allocator),
           full_allocator(o.full_allocator)
        {
          o.first = nullptr;
          o.last = nullptr;
          o.pool_size = 0;
          o.failed = true; // Set the failed flag. Just in case.
          o.fallback_allocator = nullptr;
        }

        /// \brief Move/affectation operator
        memory_allocator &operator = (memory_allocator &&o)
        {
          if (&o == this)
            return *this;

          clear();
          first = o.first;
          last = o.last;
          pool_size = o.pool_size;
          failed = o.failed;
          use_fallback = o.use_fallback;
          fallback_allocator = o.fallback_allocator;
          full_allocator = o.full_allocator;

          o.first = nullptr;
          o.last = nullptr;
          o.pool_size = 0;
          o.failed = true; // Set the failed flag. Just in case.
          o.fallback_allocator = nullptr;
          return *this;
        }

        ~memory_allocator()
        {
          clear();
        }

        /// \brief return whether or not an allocation has failed.
        bool has_failed() const
        {
          return failed;
        }

        /// \brief unset the failed flag
        void clear_failed()
        {
          failed = false;
        }

        /// \brief allocate \e count bytes at the end of the pool
        /// \return \b nullptr if allocation failed.
        void *allocate(size_t count)
        {
          if (!first || last->end_offset + count > last->size) // allocate a chunk (no chunk or no place in the last chunk)
          {
            if (use_fallback) // shortcut here when we have to use the fallback
            {
              last->end_offset = last->size; // make sure we won't allocate anything else.
              if (!fallback_allocator)
              {
                failed = true;
                if (count <= sizeof(fallback_small))
                  return &fallback_small;
                return nullptr;
              }

              void *ptr = fallback_allocator->allocate(count);
              if (!ptr)
              {
                failed = true;
                return nullptr;
              }
              return ptr;
            }

            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              failed = true;
              delete nchk;

              // never fail for small allocations
              if (count <= sizeof(fallback_small))
                return &fallback_small;
              return nullptr;
            }

            nchk->end_offset = count;
            if (first)
            {
              last->next = nchk;
              nchk->prev = last;
              last = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
            pool_size += count;
            return nchk->data;
          }

          // there is enough room in the last chunk
          void *data = last->data + last->end_offset;
          last->end_offset += count;
          pool_size += count;
          return data;
        }

        /// \brief allocate \e count bytes at the front of the pool
        /// \return \b nullptr if allocation failed.
        void *allocate_front(size_t count)
        {
          if (!first || first->start_offset < count) // allocate a chunk (no chunk or no place left in the first chunk)
          {
            if (use_fallback) // shortcut here when we have to use the fallback
            {
              failed = false;
              return nullptr;
            }

            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              failed = true;
              delete nchk;

              // never fail for small allocations
              if (count <= sizeof(fallback_small))
                return &fallback_small;

              return nullptr;
            }

            nchk->end_offset = nchk->size;
            nchk->start_offset = count;
            if (first)
            {
              first->prev = nchk;
              nchk->next = first;
              first = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
            pool_size += count;
            return nchk->data;
          }

          // there is enough room in the last chunk
          first->start_offset -= count;
          pool_size += count;
          return first->data + first->start_offset;
        }

        /// \brief remove \e count bytes at the end of the pool
        void pop(size_t count)
        {
          if (!last)
            return;
          if (count >= pool_size)
            return clear();

          while (count)
          {
            if (last != first && (last->end_offset - last->start_offset) < count)
            {
              pool_size -= last->end_offset - last->start_offset;
              count -= last->end_offset - last->start_offset;
              last->prev->next = nullptr;
              memory_chunk *lst = last;
              last = last->prev;
              delete lst;
            }
            else if (last == first && (last->end_offset - last->start_offset) < count)
            {
              if (use_fallback)
              {
                full_allocator = false;
                first->end_offset = 0;
              }
              else
              {
                delete first;
                first = nullptr;
                last = nullptr;
              }
              pool_size = 0;
              return;
            }
            else
            {
              last->end_offset -= count;
              pool_size -= count;
              return;
            }
          }
        }

        /// \brief remove \e count bytes at the start of the pool
        void pop_front(size_t count)
        {
          if (!first || !pool_size)
            return;
          if (count >= pool_size)
            return clear();

          while (count)
          {
            if (last != first && (first->end_offset - first->start_offset) < count)
            {
              pool_size -= first->end_offset - first->start_offset;
              count -= first->end_offset - first->start_offset;
              first->prev->next = nullptr;
              memory_chunk *lst = first;
              first = first->prev;
              delete lst;
            }
            else if (last == first && (last->end_offset - last->start_offset) < count)
            {
              delete first;
              first = nullptr;
              last = nullptr;
              pool_size = 0;
              return;
            }
            else
            {
              first->start_offset += count;
              pool_size -= count;
              return;
            }
          }
        }

        /// \brief makes sure there will be enough contiguous space.
        /// Don't mark memory as allocated, but can skip the end of a chunk if there isn't enough space.
        /// \note doesn't set the failed flag is any memory allocation failure occurs.
        bool preallocate_contiguous(size_t count)
        {
          if (use_fallback)
          {
            if (last->end_offset + count <= last->size) // already preallocated, already contiguous
              return true;
            else // use the fallback, mark the buffer as full, _here will return fallback's _here.
            {
              if (!fallback_allocator)
                return false;
              last->end_offset = last->size; // make it full (no further allocations will takes place on this allocator)
              full_allocator = true;
              return fallback_allocator->preallocate_contiguous(count);
            }
          }

          if (!first || last->end_offset + count > last->size) // allocate a chunk (no chunk or no place in the last chunk)
          {
            memory_chunk *nchk = new(std::nothrow) memory_chunk;
            if (nchk)
            {
              nchk->size = count > chunk_size ? count : chunk_size;
              nchk->data = reinterpret_cast<uint8_t *>(operator new(nchk->size, std::nothrow));
            }
            if (!nchk || !nchk->data)
            {
              delete nchk;

              return false;
            }

            nchk->end_offset = 0;
            if (first)
            {
              last->next = nchk;
              nchk->prev = last;
              last = nchk;
            }
            else
            {
              first = nchk;
              last = nchk;
            }
          }
          return true;
        }

        /// \brief Check if the data is already contiguous
        bool is_data_contiguous() const
        {
          if (!first)
            return true;
          if (!first->next && first->start_offset == 0) // already contiguous
            return true;
          return false;
        }

        /// \brief make the data contiguous.
        /// \note don't free the memory !!!
        void *get_contiguous_data()
        {
          // easy cases:
          if (!first)
            return nullptr;
          if (!first->next && first->start_offset == 0) // already contiguous
            return first->data;

          memory_chunk *new_chk = new memory_chunk;
          new_chk->data = reinterpret_cast<uint8_t *>(operator new(pool_size, std::nothrow));
          if (!new_chk->data)
          {
            delete new_chk;
            return nullptr;
          }

          new_chk->size = pool_size;
          new_chk->start_offset = 0;
          new_chk->end_offset = pool_size;

          size_t idx = 0;
          memory_chunk *next = nullptr;
          for (memory_chunk *chr = first; chr; chr = next)
          {
            memcpy(new_chk->data + idx, chr->data + chr->start_offset, chr->end_offset - chr->start_offset);
            idx += chr->end_offset - chr->start_offset;
            next = chr->next;
            delete chr;
          }

          first = new_chk;
          last = new_chk;
          return new_chk->data;
        }

        /// \brief Like get_contiguous_data() but does not clear the memory_allocator
        /// \note As you have the ownership of the memory, it's your duty to free it
        void *get_contiguous_data_copy() const
        {
          uint8_t *data = reinterpret_cast<uint8_t *>(operator new(pool_size, std::nothrow));
          if (!data)
            return nullptr;

          size_t idx = 0;
          memory_chunk *next = nullptr;
          for (memory_chunk *chr = first; chr; chr = next)
          {
            memcpy(data + idx, chr->data + chr->start_offset, chr->end_offset - chr->start_offset);
            idx += chr->end_offset - chr->start_offset;
            next = chr->next;
            delete chr;
          }

          return data;
        }

        /// \brief give the the data ownership, return the pointer to the data and clear the pool
        /// \note it's up to the user to delete this pointer.
        /// \see get_contiguous_data()
        /// \see clear()
        void *give_up_data()
        {
          void *ret = get_contiguous_data();
          if (first)
            first->data = nullptr;
          clear();
          return ret;
        }

        /// \brief empty the memory pool, delete every allocated memory.
        void clear()
        {
          memory_chunk *next = nullptr;
          for (memory_chunk *chr = first; chr; chr = next)
          {
            next = chr->next;
            delete chr;
          }
          first = nullptr;
          last = nullptr;
          pool_size = 0;
          failed = false;
        }

        /// \brief return the size of the pool
        size_t size() const
        {
          return pool_size;
        }

        /// \brief returns the current pointer.
        void *_here()
        {
          if (full_allocator)
          {
            if (fallback_allocator)
              return fallback_allocator->_here();
          }

          if (!last)
            return nullptr;
          return last->data + last->end_offset;
        }

      private:
        struct memory_chunk
        {
          uint8_t *data = nullptr;

          size_t start_offset = 0;
          size_t end_offset = 0;
          size_t size = 0;

          bool do_not_destroy = false;

          memory_chunk *next = nullptr;
          memory_chunk *prev = nullptr;

          ~memory_chunk()
          {
            if (!do_not_destroy)
              delete data;
          }
        };

      private:
        constexpr static size_t chunk_size = 8192 * 10; // 10 * 8Kio per-chunk

      private:
        memory_chunk *first = nullptr;
        memory_chunk *last = nullptr;
        size_t pool_size = 0;
        bool failed = false;
        uint64_t fallback_small; ///< \brief used when running out of memory and if the allocated size is lower than

        bool use_fallback = false;
        memory_allocator *fallback_allocator = nullptr;
        bool full_allocator = false; ///< \brief _here will return fallback_allocator->_here() or nullptr.
    };
  } // namespace cr
} // namespace neam

#endif /*__N_2080522430493415542_1720253011__MEMORY_ALLOCATOR_HPP__*/

// kate: indent-mode cstyle; indent-width 2; replace-tabs on;

<|endoftext|>


<commit_before>#include "main_window.moc.hpp"

#include <string.h>
#include <stdlib.h>
#include <iostream>

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QCheckBox>

ThreadWindowImpl::ThreadWindowImpl(std::shared_ptr<Plugin> *plugs, QWidget *parent)
   : QWidget(parent), plugins(plugs)
{
   setAttribute(Qt::WA_QuitOnClose, false);
   QVBoxLayout *vbox = new QVBoxLayout;
   vbox->addWidget(new QLabel("DSP plugin config:", this));

   tab = new QTabWidget(this);
   for (unsigned i = 0; i < MetaDSP::max_plugs; i++)
      tab->addTab(new PluginSettings(plugins[i], tab), QString::fromStdString(plugins[i]->ident()));

   vbox->addWidget(tab);

   setWindowTitle("SSNES Meta DSP");
   setLayout(vbox);
}

void MetaApplication::show()
{
   impl->show();
}

MetaApplication::MetaApplication(std::shared_ptr<Plugin> *plugs)
   : plugins(plugs)
{
   static int argc = 1;
   static const char *appname = "ssnes-dsp";
   static char *argv[] = { const_cast<char*>(appname), NULL };

   app = new QApplication(argc, argv);
   impl = new ThreadWindowImpl(plugins);
}

MetaApplication::~MetaApplication()
{
   delete impl;
   delete app;
}

PluginSettings::PluginSettings(std::shared_ptr<Plugin> &plug, QWidget *parent)
   : QWidget(parent), plugin(plug)
{
   QVBoxLayout *vbox = new QVBoxLayout;

   QCheckBox *box = new QCheckBox("Enable", this);
   box->setCheckState(Qt::Checked);
   connect(box, SIGNAL(stateChanged(int)), this, SLOT(enable(int)));
   vbox->addWidget(box);

   auto list = plugin->options();
   for (auto itr = list.begin(); itr != list.end(); ++itr)
      vbox->addWidget(new PluginSetting(plugin, *itr, this));

   setLayout(vbox);
}

void PluginSettings::enable(int val)
{
   Global::lock();
   if (val == Qt::Checked)
      plugin->enabled(true);
   else if (val == Qt::Unchecked)
      plugin->enabled(false);
   Global::unlock();
}

PluginSetting::PluginSetting(std::shared_ptr<Plugin> &plug,
      const PluginOption &opt, QWidget *parent)
   : QWidget(parent), plugin(plug)
{
   id = opt.id;
   min = opt.min;
   max = opt.max;
   current = opt.current;

   QHBoxLayout *hbox = new QHBoxLayout;
   hbox->addWidget(new QLabel(QString::fromStdString(opt.description), this));
   slider = new QSlider(Qt::Horizontal, this);
   slider->setMinimum(0);
   slider->setMaximum(Intervals);
   connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updated()));
   hbox->addWidget(slider);

   value = new QLabel(QString::number(current, 'f', 1));
   hbox->addWidget(value);

   val2slide(current);
   setLayout(hbox);
}

void PluginSetting::updated()
{
   current = slide2val();
   value->setText(QString::number(current, 'f', 1));

   Global::lock();
   plugin->set_option(id, current);
   Global::unlock();
}

double PluginSetting::slide2val() const
{
   int pos = slider->value();
   return min + (max - min) * pos / Intervals;
}

void PluginSetting::val2slide(double val)
{
   int value = (val - min) * Intervals / (max - min);
   slider->setValue(value);
}

namespace Global
{
   static QMutex g_lock;

   void lock()
   {
      g_lock.lock();
   }

   void unlock()
   {
      g_lock.unlock();
   }
}
<commit_msg>Do not lock needlessly.<commit_after>#include "main_window.moc.hpp"

#include <string.h>
#include <stdlib.h>
#include <iostream>

#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QCheckBox>

ThreadWindowImpl::ThreadWindowImpl(std::shared_ptr<Plugin> *plugs, QWidget *parent)
   : QWidget(parent), plugins(plugs)
{
   setAttribute(Qt::WA_QuitOnClose, false);
   QVBoxLayout *vbox = new QVBoxLayout;
   vbox->addWidget(new QLabel("DSP plugin config:", this));

   tab = new QTabWidget(this);
   for (unsigned i = 0; i < MetaDSP::max_plugs; i++)
      tab->addTab(new PluginSettings(plugins[i], tab), QString::fromStdString(plugins[i]->ident()));

   vbox->addWidget(tab);

   setWindowTitle("SSNES Meta DSP");
   setLayout(vbox);
}

void MetaApplication::show()
{
   impl->show();
}

MetaApplication::MetaApplication(std::shared_ptr<Plugin> *plugs)
   : plugins(plugs)
{
   static int argc = 1;
   static const char *appname = "ssnes-dsp";
   static char *argv[] = { const_cast<char*>(appname), NULL };

   app = new QApplication(argc, argv);
   impl = new ThreadWindowImpl(plugins);
}

MetaApplication::~MetaApplication()
{
   delete impl;
   delete app;
}

PluginSettings::PluginSettings(std::shared_ptr<Plugin> &plug, QWidget *parent)
   : QWidget(parent), plugin(plug)
{
   QVBoxLayout *vbox = new QVBoxLayout;

   QCheckBox *box = new QCheckBox("Enable", this);
   box->setCheckState(Qt::Checked);
   connect(box, SIGNAL(stateChanged(int)), this, SLOT(enable(int)));
   vbox->addWidget(box);

   auto list = plugin->options();
   for (auto itr = list.begin(); itr != list.end(); ++itr)
      vbox->addWidget(new PluginSetting(plugin, *itr, this));

   setLayout(vbox);
}

void PluginSettings::enable(int val)
{
   Global::lock();
   if (val == Qt::Checked)
      plugin->enabled(true);
   else if (val == Qt::Unchecked)
      plugin->enabled(false);
   Global::unlock();
}

PluginSetting::PluginSetting(std::shared_ptr<Plugin> &plug,
      const PluginOption &opt, QWidget *parent)
   : QWidget(parent), plugin(plug)
{
   id = opt.id;
   min = opt.min;
   max = opt.max;
   current = opt.current;

   QHBoxLayout *hbox = new QHBoxLayout;
   hbox->addWidget(new QLabel(QString::fromStdString(opt.description), this));
   slider = new QSlider(Qt::Horizontal, this);
   slider->setMinimum(0);
   slider->setMaximum(Intervals);
   connect(slider, SIGNAL(valueChanged(int)), this, SLOT(updated()));
   hbox->addWidget(slider);

   value = new QLabel(QString::number(current, 'f', 1));
   hbox->addWidget(value);

   val2slide(current);
   setLayout(hbox);
}

void PluginSetting::updated()
{
   current = slide2val();
   value->setText(QString::number(current, 'f', 1));

   Global::lock();
   plugin->set_option(id, current);
   Global::unlock();
}

double PluginSetting::slide2val() const
{
   int pos = slider->value();
   return min + (max - min) * pos / Intervals;
}

void PluginSetting::val2slide(double val)
{
   int value = (val - min) * Intervals / (max - min);
   slider->setValue(value);
}

namespace Global
{
#ifdef META_THREADED
   static QMutex g_lock;

   void lock()
   {
      g_lock.lock();
   }

   void unlock()
   {
      g_lock.unlock();
   }
#else
   void lock() {}
   void unlock() {}
#endif
}
<|endoftext|>


<commit_before>/***********************************************************************
 *    filename:   Element.cpp
 *    created:    28/10/2011
 *    author:     Martin Preisler
 *************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2011 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include "CEGUI/Element.h"

#include <boost/test/unit_test.hpp>
#include <boost/timer.hpp>

BOOST_AUTO_TEST_SUITE(Element)

BOOST_AUTO_TEST_CASE(RelativeSizing)
{
    // in this test, everything is top left aligned

    CEGUI::Element* root = new CEGUI::Element();
    root->setSize(CEGUI::USize(100.0f * CEGUI::UDim::px(), 100 * CEGUI::UDim::px()));
    CEGUI::Element* child = new CEGUI::Element();
    child->setSize(CEGUI::USize(50.0f * CEGUI::UDim::percent(), 25.0f * CEGUI::UDim::percent()));
    root->addChild(child);

    BOOST_CHECK_EQUAL(root->getPixelSize(), CEGUI::Sizef(100.0f, 100.0f));
    BOOST_CHECK_EQUAL(child->getPixelSize(), CEGUI::Sizef(50.0f, 25.0f));

    CEGUI::Element* innerChild = new CEGUI::Element();
    child->addChild(innerChild);
    innerChild->setSize(CEGUI::USize(200.0f * CEGUI::UDim::percent(), 100.0f * CEGUI::UDim::percent()));
    BOOST_CHECK_EQUAL(innerChild->getPixelSize(), CEGUI::Sizef(100.0f, 25.0f));

    delete innerChild;
    delete child;
    delete root;
}

BOOST_AUTO_TEST_CASE(TopLeftAlignment)
{
    // in this test, everything is top left aligned

    CEGUI::Element* root = new CEGUI::Element();
    root->setSize(CEGUI::USize(100.0f * CEGUI::UDim::px(), 100 * CEGUI::UDim::px()));
    CEGUI::Element* child = new CEGUI::Element();
    child->setSize(CEGUI::USize(50.0f * CEGUI::UDim::px(), 25.0f * CEGUI::UDim::px()));
    root->addChild(child);

    // even though it is the default at the point of writing the test, we have to make sure this fact doesn't change!
    BOOST_CHECK_EQUAL(root->getHorizontalAlignment(), CEGUI::HA_LEFT);
    BOOST_CHECK_EQUAL(root->getVerticalAlignment(), CEGUI::VA_TOP);
    BOOST_CHECK_EQUAL(child->getHorizontalAlignment(), CEGUI::HA_LEFT);
    BOOST_CHECK_EQUAL(child->getVerticalAlignment(), CEGUI::VA_TOP);

    BOOST_CHECK_EQUAL(root->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 100, 100));
    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 50, 25));

    child->setPosition(CEGUI::UVector2(10.0f * CEGUI::UDim::px(), 5.0f * CEGUI::UDim::px()));

    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(10, 5, 60, 30));
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Split Element top left alignment test case into 2<commit_after>/***********************************************************************
 *    filename:   Element.cpp
 *    created:    28/10/2011
 *    author:     Martin Preisler
 *************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2011 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include "CEGUI/Element.h"

#include <boost/test/unit_test.hpp>
#include <boost/timer.hpp>

BOOST_AUTO_TEST_SUITE(Element)

BOOST_AUTO_TEST_CASE(RelativeSizing)
{
    // in this test, everything is top left aligned

    CEGUI::Element* root = new CEGUI::Element();
    root->setSize(CEGUI::USize(100.0f * CEGUI::UDim::px(), 100 * CEGUI::UDim::px()));
    CEGUI::Element* child = new CEGUI::Element();
    child->setSize(CEGUI::USize(50.0f * CEGUI::UDim::percent(), 25.0f * CEGUI::UDim::percent()));
    root->addChild(child);

    BOOST_CHECK_EQUAL(root->getPixelSize(), CEGUI::Sizef(100.0f, 100.0f));
    BOOST_CHECK_EQUAL(child->getPixelSize(), CEGUI::Sizef(50.0f, 25.0f));

    CEGUI::Element* innerChild = new CEGUI::Element();
    child->addChild(innerChild);
    innerChild->setSize(CEGUI::USize(200.0f * CEGUI::UDim::percent(), 100.0f * CEGUI::UDim::percent()));
    BOOST_CHECK_EQUAL(innerChild->getPixelSize(), CEGUI::Sizef(100.0f, 25.0f));

    delete innerChild;
    delete child;
    delete root;
}

BOOST_AUTO_TEST_CASE(LeftAlignment)
{
    // in this test, everything is top left aligned

    CEGUI::Element* root = new CEGUI::Element();
    root->setSize(CEGUI::USize(100.0f * CEGUI::UDim::px(), 0.0f * CEGUI::UDim::px()));
    CEGUI::Element* child = new CEGUI::Element();
    child->setSize(CEGUI::USize(50.0f * CEGUI::UDim::px(), 0.0f * CEGUI::UDim::px()));
    root->addChild(child);

    // even though it is the default at the point of writing the test, we have to make sure this fact doesn't change!
    BOOST_CHECK_EQUAL(root->getHorizontalAlignment(), CEGUI::HA_LEFT);
    BOOST_CHECK_EQUAL(child->getHorizontalAlignment(), CEGUI::HA_LEFT);

    BOOST_CHECK_EQUAL(root->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 100, 0));
    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 50, 0));

    child->setPosition(CEGUI::UVector2(10.0f * CEGUI::UDim::px(), 0.0f * CEGUI::UDim::px()));

    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(10, 0, 60, 0));
}

BOOST_AUTO_TEST_CASE(TopAlignment)
{
    // in this test, everything is top left aligned

    CEGUI::Element* root = new CEGUI::Element();
    root->setSize(CEGUI::USize(0.0f * CEGUI::UDim::px(), 100 * CEGUI::UDim::px()));
    CEGUI::Element* child = new CEGUI::Element();
    child->setSize(CEGUI::USize(0.0f * CEGUI::UDim::px(), 25.0f * CEGUI::UDim::px()));
    root->addChild(child);

    // even though it is the default at the point of writing the test, we have to make sure this fact doesn't change!
    BOOST_CHECK_EQUAL(root->getVerticalAlignment(), CEGUI::VA_TOP);
    BOOST_CHECK_EQUAL(child->getVerticalAlignment(), CEGUI::VA_TOP);

    BOOST_CHECK_EQUAL(root->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 0, 100));
    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 0, 0, 25));

    child->setPosition(CEGUI::UVector2(0.0f * CEGUI::UDim::px(), 5.0f * CEGUI::UDim::px()));

    BOOST_CHECK_EQUAL(child->getUnclippedOuterRect().get(), CEGUI::Rectf(0, 5, 0, 30));
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>#include "g_json.h"

// TILE PARSING
static unordered_map<const char*, jsonParseFunc> tileParseFields;

void ParseSubtile(cJSON* json, void* ptTile, int tileNum) {
	Tile* t = (Tile*)ptTile;
	if(!t) {
		return;
	}
	cJSON* jsonNode = cJSON_GetObjectItem(json, "low");
	if(jsonNode) {
		t->lowmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "high");
	if(jsonNode) {
		t->highmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "light");
	if(jsonNode) {
		t->lightmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "vis");
	if(jsonNode) {
		t->vismask |= (1 << tileNum);
	}
}

void InitTileParseFields() {
#define SUBTILE_PARSER(x) [](cJSON* j, void* p) -> void { ParseSubtile(j, p, x); }
#define INT_PARSER(x) [](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; t->##x## = cJSON_ToInteger(j); }
#define NAME_PARSER [](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; strcpy(t->name, cJSON_ToString(j)); }
#define MAT_PARSER \
	[](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; t->materialHandle = trap->RegisterMaterial(cJSON_ToString(j)); }

	tileParseFields["subtile0"] = SUBTILE_PARSER(0);
	tileParseFields["subtile1"] = SUBTILE_PARSER(1);
	tileParseFields["subtile2"] = SUBTILE_PARSER(2);
	tileParseFields["subtile3"] = SUBTILE_PARSER(3);
	tileParseFields["subtile4"] = SUBTILE_PARSER(4);
	tileParseFields["subtile5"] = SUBTILE_PARSER(5);
	tileParseFields["subtile6"] = SUBTILE_PARSER(6);
	tileParseFields["subtile7"] = SUBTILE_PARSER(7);
	tileParseFields["subtile8"] = SUBTILE_PARSER(8);
	tileParseFields["subtile9"] = SUBTILE_PARSER(9);
	tileParseFields["subtile10"] = SUBTILE_PARSER(10);
	tileParseFields["subtile11"] = SUBTILE_PARSER(11);
	tileParseFields["subtile12"] = SUBTILE_PARSER(12);
	tileParseFields["subtile13"] = SUBTILE_PARSER(13);
	tileParseFields["subtile14"] = SUBTILE_PARSER(14);
	tileParseFields["subtile15"] = SUBTILE_PARSER(15);
	tileParseFields["lowmask"] = INT_PARSER(lowmask);
	tileParseFields["highmask"] = INT_PARSER(highmask);
	tileParseFields["lightmask"] = INT_PARSER(lightmask);
	tileParseFields["vismask"] = INT_PARSER(vismask);
	tileParseFields["name"] = NAME_PARSER;
	tileParseFields["material"] = MAT_PARSER;
}

// LEVEL PARSING
static unordered_map<const char*, jsonParseFunc> levelParseFields;
void InitLevelParseFields() {
#define NAME_PARSER [](cJSON* j, void* p) -> void { MapDatum* t = (MapDatum*)p; strcpy(t->name, cJSON_ToString(j)); }
#define LEVEL_PARSER [](cJSON* j, void* p) -> void { MapDatum* t = (MapDatum*)p; t->nDungeonLevel = cJSON_ToInteger(j); }
#define VIS_PARSER(x) [](cJSON* j, void*p) -> void { MapDatum* t = (MapDatum*)p; t->iLink[x] = cJSON_ToInteger(j); }
	levelParseFields["name"] = NAME_PARSER;
	levelParseFields["level"] = LEVEL_PARSER;
	levelParseFields["vis0"] = VIS_PARSER(0);
	levelParseFields["vis1"] = VIS_PARSER(1);
	levelParseFields["vis2"] = VIS_PARSER(2);
	levelParseFields["vis3"] = VIS_PARSER(3);
	levelParseFields["vis4"] = VIS_PARSER(4);
	levelParseFields["vis5"] = VIS_PARSER(5);
	levelParseFields["vis6"] = VIS_PARSER(6);
	levelParseFields["vis7"] = VIS_PARSER(7);
	levelParseFields["vis8"] = VIS_PARSER(8);
	levelParseFields["vis9"] = VIS_PARSER(9);
}

Worldspace world;
vector<MapDatum> vMapData;

MapLoader::MapLoader(const string& presetsPath, const string& tilePath) {
	// Load the tiles.
	InitTileParseFields();
	vector<string> paths;
	int numFiles = trap->ListFilesInDir(tilePath.c_str(), paths, ".json");
	if(numFiles <= 0) {
		R_Error("MapLoader could not load tiles (missing?)");
	}
	for(auto it = paths.begin(); it != paths.end(); ++it) {
		Tile t;
		JSON_ParseFile((char*)it->c_str(), tileParseFields, &t);
		vTiles.push_back(t);
		mTileResolver.insert(make_pair(t.name, vTiles.end()-1));
	}

	// Load the Levels.json
	JSON_ParseMultifile<MapDatum>("levels/Levels.json", levelParseFields, vMapData);

	// Finally, start loading the maps which are important.
}

MapLoader::~MapLoader() {
	mTileResolver.clear();
	vTiles.clear();
	tileParseFields.clear();
}

MapLoader* maps;
void InitLevels() {
	maps = new MapLoader("levels/preset", "levels/tiles");
}

void ShutdownLevels() {
	delete maps;
}<commit_msg>Fixed silly mistake with Levels.json parsing<commit_after>#include "g_json.h"

// TILE PARSING
static unordered_map<const char*, jsonParseFunc> tileParseFields;

void ParseSubtile(cJSON* json, void* ptTile, int tileNum) {
	Tile* t = (Tile*)ptTile;
	if(!t) {
		return;
	}
	cJSON* jsonNode = cJSON_GetObjectItem(json, "low");
	if(jsonNode) {
		t->lowmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "high");
	if(jsonNode) {
		t->highmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "light");
	if(jsonNode) {
		t->lightmask |= (1 << tileNum);
	}

	jsonNode = cJSON_GetObjectItem(json, "vis");
	if(jsonNode) {
		t->vismask |= (1 << tileNum);
	}
}

void InitTileParseFields() {
#define SUBTILE_PARSER(x) [](cJSON* j, void* p) -> void { ParseSubtile(j, p, x); }
#define INT_PARSER(x) [](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; t->##x## = cJSON_ToInteger(j); }
#define NAME_PARSER [](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; strcpy(t->name, cJSON_ToString(j)); }
#define MAT_PARSER \
	[](cJSON* j, void* p) -> void { Tile* t = (Tile*)p; t->materialHandle = trap->RegisterMaterial(cJSON_ToString(j)); }

	tileParseFields["subtile0"] = SUBTILE_PARSER(0);
	tileParseFields["subtile1"] = SUBTILE_PARSER(1);
	tileParseFields["subtile2"] = SUBTILE_PARSER(2);
	tileParseFields["subtile3"] = SUBTILE_PARSER(3);
	tileParseFields["subtile4"] = SUBTILE_PARSER(4);
	tileParseFields["subtile5"] = SUBTILE_PARSER(5);
	tileParseFields["subtile6"] = SUBTILE_PARSER(6);
	tileParseFields["subtile7"] = SUBTILE_PARSER(7);
	tileParseFields["subtile8"] = SUBTILE_PARSER(8);
	tileParseFields["subtile9"] = SUBTILE_PARSER(9);
	tileParseFields["subtile10"] = SUBTILE_PARSER(10);
	tileParseFields["subtile11"] = SUBTILE_PARSER(11);
	tileParseFields["subtile12"] = SUBTILE_PARSER(12);
	tileParseFields["subtile13"] = SUBTILE_PARSER(13);
	tileParseFields["subtile14"] = SUBTILE_PARSER(14);
	tileParseFields["subtile15"] = SUBTILE_PARSER(15);
	tileParseFields["lowmask"] = INT_PARSER(lowmask);
	tileParseFields["highmask"] = INT_PARSER(highmask);
	tileParseFields["lightmask"] = INT_PARSER(lightmask);
	tileParseFields["vismask"] = INT_PARSER(vismask);
	tileParseFields["name"] = NAME_PARSER;
	tileParseFields["material"] = MAT_PARSER;
}

// LEVEL PARSING
static unordered_map<const char*, jsonParseFunc> levelParseFields;
void InitLevelParseFields() {
#define NAME_PARSER [](cJSON* j, void* p) -> void { MapDatum* t = (MapDatum*)p; strcpy(t->name, cJSON_ToString(j)); }
#define LEVEL_PARSER [](cJSON* j, void* p) -> void { MapDatum* t = (MapDatum*)p; t->nDungeonLevel = cJSON_ToInteger(j); }
#define VIS_PARSER(x) [](cJSON* j, void*p) -> void { MapDatum* t = (MapDatum*)p; t->iLink[x] = cJSON_ToInteger(j); }
	levelParseFields["name"] = NAME_PARSER;
	levelParseFields["level"] = LEVEL_PARSER;
	levelParseFields["vis0"] = VIS_PARSER(0);
	levelParseFields["vis1"] = VIS_PARSER(1);
	levelParseFields["vis2"] = VIS_PARSER(2);
	levelParseFields["vis3"] = VIS_PARSER(3);
	levelParseFields["vis4"] = VIS_PARSER(4);
	levelParseFields["vis5"] = VIS_PARSER(5);
	levelParseFields["vis6"] = VIS_PARSER(6);
	levelParseFields["vis7"] = VIS_PARSER(7);
	levelParseFields["vis8"] = VIS_PARSER(8);
	levelParseFields["vis9"] = VIS_PARSER(9);
}

Worldspace world;
vector<MapDatum> vMapData;

MapLoader::MapLoader(const string& presetsPath, const string& tilePath) {
	// Load the tiles.
	InitTileParseFields();
	vector<string> paths;
	int numFiles = trap->ListFilesInDir(tilePath.c_str(), paths, ".json");
	if(numFiles <= 0) {
		R_Error("MapLoader could not load tiles (missing?)");
	}
	for(auto it = paths.begin(); it != paths.end(); ++it) {
		Tile t;
		JSON_ParseFile((char*)it->c_str(), tileParseFields, &t);
		vTiles.push_back(t);
		mTileResolver.insert(make_pair(t.name, vTiles.end()-1));
	}

	// Load the Levels.json
	InitLevelParseFields();
	JSON_ParseMultifile<MapDatum>("levels/Levels.json", levelParseFields, vMapData);

	// Finally, start loading the maps which are important.
}

MapLoader::~MapLoader() {
	mTileResolver.clear();
	vTiles.clear();
	tileParseFields.clear();
}

MapLoader* maps;
void InitLevels() {
	maps = new MapLoader("levels/preset", "levels/tiles");
}

void ShutdownLevels() {
	delete maps;
}<|endoftext|>


<commit_before>#include <exception>
#include <QAction>
#include <QClipboard>
#include <QCursor>
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QMap>
#include <QMapIterator>
#include <QMenu>
#include <QMessageBox>
#include <QModelIndex>
#include <QString>
#include <QStringList>
#include <QTableWidgetItem>
#include <QTableWidgetSelectionRange>
#include <QTextStream>
#include "columnmappingdialog.hpp"
#include "mainwindow.hpp"
#include "sfcsv/sfcsv.h"
#include "ui_mainwindow.h"

static QStringList parseCsv(const QString& in) {
    QStringList result;
    std::vector<std::string> parsed;
    const std::string line = in.toStdString();
    sfcsv::parse_line(line, std::back_inserter(parsed), ',');
    for(auto&& res : parsed) {
        result.append(QString::fromStdString(res));
    }

    return result;
}

QStringList parseFile(const QString& in) {
    QStringList results;
    QFile inFile(in);
    if(!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        throw std::runtime_error("Unable to open file");
    }

    QTextStream stream(&inFile);
    while(!stream.atEnd()) {
        const QString line = stream.readLine().remove("\n");
        results << line;
    }

    return results;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    config("config.ini", QSettings::IniFormat)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;

    config.remove("has_header");
}

void MainWindow::on_actionOpen_triggered()
{
    const QString openedFile =
            QFileDialog::getOpenFileName(this, tr("Open..."),
                                         config.value("last_opened").toString());
    if(openedFile.isEmpty()) {
        return;
    }

    config.setValue("last_opened", openedFile);

    // Read first row from CSV file
    QString firstRow;
    QFile inFile(config.value("last_opened").toString());
    if(!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        throw std::runtime_error("Unable to open file");
    }

    firstRow = inFile.readLine();
    firstRow.remove('\n');

    const QStringList source = parseCsv(firstRow);
    config.setValue("source_keys", source);

    try {
        ColumnMappingDialog dlg;
        const auto ok = dlg.exec();
        if(!ok) {
            return;
        }

        // Get all rows
        QStringList lines = parseFile(openedFile);
        if(lines.isEmpty()) {
            QMessageBox::critical(this, tr("Empty CSV"),
                                  tr("CSV file does not contain any data"));
            return;
        }

        // Set header
        ui->tableCsv->clear();
        ui->tableCsv->setRowCount(lines.size());
        ui->tableCsv->setColumnCount(lines.size());
        const bool hasHeader = config.value("has_header", false).toBool();
        if(hasHeader) {
            const QStringList labels = parseCsv(lines.at(0));
            ui->tableCsv->setColumnCount(labels.size());
            ui->tableCsv->setHorizontalHeaderLabels(labels);
            lines.removeAt(0);
            if(lines.isEmpty()) {
                QMessageBox::critical(this, tr("Empty CSV"),
                                      tr("CSV file contains only a header"));
                return;
            }
        }

        // Display data
        for(int i = 0, size = lines.size(); i < size; ++i) {
            const QStringList parsed = parseCsv(lines.at(i));
            for(int j = 0, jsize = parsed.size(); j < jsize; ++j) {
                auto item = new QTableWidgetItem(parsed.at(j));
                ui->tableCsv->setItem(i, j, item);
            }
        }

    }
    catch(std::exception &ex) {
        QMessageBox::critical(this, tr("Error"), QString(ex.what()));
    }
}

void MainWindow::on_tableCsv_customContextMenuRequested(const QPoint &pos)
{
    Q_UNUSED(pos);

    QMenu menu;
    auto cbAction = new QAction(tr("Copy to clipboard"), this);
    cbAction->setData("clipboard");
    menu.addAction(cbAction);
    auto selected = menu.exec(QCursor::pos());
    if(selected && selected->data().toString() == "clipboard") {
        const auto ranges = ui->tableCsv->selectedRanges();
        QString parsedTpl;
        for(const auto& range : ranges) {
            const QStringList list = config.value("mapped_keys").toStringList();
            QMap<QString, QString> mappings;
            for(int rIdx = range.topRow(), rEnd = range.bottomRow(); rIdx <= rEnd; ++rIdx) {
                for(int i = 0, end = ui->tableCsv->columnCount(); i != end; ++i) {
                    mappings.insert(list.at(i), ui->tableCsv->item(rIdx, i)->text());
                }

                QString tpl = ui->templateEdit->toPlainText();
                QMapIterator<QString, QString> iter(mappings);
                while(iter.hasNext()) {
                    iter.next();
                    const QString var = tr("$%1").arg(iter.key());
                    tpl.replace(var, iter.value());
                }

                parsedTpl.append(tpl);
            }
        }

        auto clipboard = QApplication::clipboard();
        clipboard->setText(parsedTpl);
    }
}

void MainWindow::on_actionMappings_triggered()
{

}
<commit_msg>Remove keys after closing app<commit_after>#include <exception>
#include <QAction>
#include <QClipboard>
#include <QCursor>
#include <QDebug>
#include <QFile>
#include <QFileDialog>
#include <QMap>
#include <QMapIterator>
#include <QMenu>
#include <QMessageBox>
#include <QModelIndex>
#include <QString>
#include <QStringList>
#include <QTableWidgetItem>
#include <QTableWidgetSelectionRange>
#include <QTextStream>
#include "columnmappingdialog.hpp"
#include "mainwindow.hpp"
#include "sfcsv/sfcsv.h"
#include "ui_mainwindow.h"

static QStringList parseCsv(const QString& in) {
    QStringList result;
    std::vector<std::string> parsed;
    const std::string line = in.toStdString();
    sfcsv::parse_line(line, std::back_inserter(parsed), ',');
    for(auto&& res : parsed) {
        result.append(QString::fromStdString(res));
    }

    return result;
}

QStringList parseFile(const QString& in) {
    QStringList results;
    QFile inFile(in);
    if(!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        throw std::runtime_error("Unable to open file");
    }

    QTextStream stream(&inFile);
    while(!stream.atEnd()) {
        const QString line = stream.readLine().remove("\n");
        results << line;
    }

    return results;
}

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    config("config.ini", QSettings::IniFormat)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;

    config.remove("has_header");
    config.remove("source_keys");
    config.remove("mapped_keys");
}

void MainWindow::on_actionOpen_triggered()
{
    const QString openedFile =
            QFileDialog::getOpenFileName(this, tr("Open..."),
                                         config.value("last_opened").toString());
    if(openedFile.isEmpty()) {
        return;
    }

    config.setValue("last_opened", openedFile);

    // Read first row from CSV file
    QString firstRow;
    QFile inFile(config.value("last_opened").toString());
    if(!inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        throw std::runtime_error("Unable to open file");
    }

    firstRow = inFile.readLine();
    firstRow.remove('\n');

    const QStringList source = parseCsv(firstRow);
    config.setValue("source_keys", source);

    try {
        ColumnMappingDialog dlg;
        const auto ok = dlg.exec();
        if(!ok) {
            return;
        }

        // Get all rows
        QStringList lines = parseFile(openedFile);
        if(lines.isEmpty()) {
            QMessageBox::critical(this, tr("Empty CSV"),
                                  tr("CSV file does not contain any data"));
            return;
        }

        // Set header
        ui->tableCsv->clear();
        ui->tableCsv->setRowCount(lines.size());
        ui->tableCsv->setColumnCount(lines.size());
        const bool hasHeader = config.value("has_header", false).toBool();
        if(hasHeader) {
            const QStringList labels = parseCsv(lines.at(0));
            ui->tableCsv->setColumnCount(labels.size());
            ui->tableCsv->setHorizontalHeaderLabels(labels);
            lines.removeAt(0);
            if(lines.isEmpty()) {
                QMessageBox::critical(this, tr("Empty CSV"),
                                      tr("CSV file contains only a header"));
                return;
            }
        }

        // Display data
        for(int i = 0, size = lines.size(); i < size; ++i) {
            const QStringList parsed = parseCsv(lines.at(i));
            for(int j = 0, jsize = parsed.size(); j < jsize; ++j) {
                auto item = new QTableWidgetItem(parsed.at(j));
                ui->tableCsv->setItem(i, j, item);
            }
        }

    }
    catch(std::exception &ex) {
        QMessageBox::critical(this, tr("Error"), QString(ex.what()));
    }
}

void MainWindow::on_tableCsv_customContextMenuRequested(const QPoint &pos)
{
    Q_UNUSED(pos);

    QMenu menu;
    auto cbAction = new QAction(tr("Copy to clipboard"), this);
    cbAction->setData("clipboard");
    menu.addAction(cbAction);
    auto selected = menu.exec(QCursor::pos());
    if(selected && selected->data().toString() == "clipboard") {
        const auto ranges = ui->tableCsv->selectedRanges();
        QString parsedTpl;
        for(const auto& range : ranges) {
            const QStringList list = config.value("mapped_keys").toStringList();
            QMap<QString, QString> mappings;
            for(int rIdx = range.topRow(), rEnd = range.bottomRow(); rIdx <= rEnd; ++rIdx) {
                for(int i = 0, end = ui->tableCsv->columnCount(); i != end; ++i) {
                    mappings.insert(list.at(i), ui->tableCsv->item(rIdx, i)->text());
                }

                QString tpl = ui->templateEdit->toPlainText();
                QMapIterator<QString, QString> iter(mappings);
                while(iter.hasNext()) {
                    iter.next();
                    const QString var = tr("$%1").arg(iter.key());
                    tpl.replace(var, iter.value());
                }

                parsedTpl.append(tpl);
            }
        }

        auto clipboard = QApplication::clipboard();
        clipboard->setText(parsedTpl);
    }
}

void MainWindow::on_actionMappings_triggered()
{

}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <stdio.h>
#include <QScrollBar>
#include <QFileDialog>
#include <opencv2/imgproc/imgproc.hpp>
#include "hardware/camera/qopencvcamera.h"
#include "hardware/projector/seconddisplayprojector.h"
#include "geom/triangulator.h"
#include "hardware/projector/gridpattern.h"
#include "hardware/projector/flatcolorpattern.h"
#include "hardware/standards/dotmatrixstandard.h"
#include "hardware/standards/chessboardstandard.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    calib(new CalibrationDialog),
    debugWin(new QPlainTextEdit),
    graycode(new GrayCodePattern(1,false)),
    dbgIm(new ImageViewWidget),
    singleCal(new SingleCalibrationDialog),
    bg(0),
    singleCal2(new SingleCalibrationDialog),
    sinusoidPower(6),
    sinusoidShifts(8)
{
    ui->setupUi(this);

    debugWin->setPlainText(QString("ScanStudio started."));
    debugWin->setWindowTitle(QString("Debugging info"));
    debugWin->setWindowIcon(
                QIcon(tr(":/icons/oxygen/camera-web-64.png")));
    debugWin->setWindowFlags(Qt::WindowStaysOnTopHint);
    debugWin->setReadOnly(true);

    connect(&stdSettings,SIGNAL(accept()),this,SLOT(adjustCalStd()));
    connect(&camSettings,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(&camSettings,
            SIGNAL(acceptedCameras(QCamera*,QCamera*)),
            this,
            SLOT(cameraSettingsChanged(QCamera*,QCamera*)));

    ui->modelView->zoomFit();
    dbgIm->setWindowTitle("Debugging -- camera view");

    QOpenCVCamera *capCam = new QOpenCVCamera(2);
    connect(capCam,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    capCam->setResolution(1600,1200);
    capCam->setPrincipalPoint(800,600);
    capCam->setFocalLength(1300);
    capCam->setPosition(cv::Vec3d(-0.25,-0.01,0));
    capCam->setOrientation(cv::Vec3d(0,M_PI/12,0));

    camera = capCam;

    QOpenCVCamera *capCam2 = new QOpenCVCamera(1);
    connect(capCam2,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    capCam2->setResolution(1600,1200);
    capCam2->setPrincipalPoint(800,600);
    capCam2->setFocalLength(1300);
    capCam2->setPosition(cv::Vec3d(0.2,0,0));
    capCam2->setOrientation(cv::Vec3d(0,-0.1,0));

    camera2 = capCam2;

    SecondDisplayProjector *pj = new SecondDisplayProjector();
    pj->setScreen(1);
    pj->setResolution(848,480);
    pj->setPrincipalPoint(848/2,480/2);
    pj->setPosition(cv::Vec3d(0.0,0,0));
    pj->setOrientation(cv::Vec3d(-0*M_PI/180,0.0,0.0));
    pj->setFocalLength(3600);

    GridPattern *grid = new GridPattern();
    pj->queue(grid);
//    FlatColorPattern *flat = new FlatColorPattern();
//    pj->queue(flat);

    projector = pj;
    capCam->connectProjector(pj);
    capCam2->connectProjector(pj);

    compiler = new BinaryCompiler(capCam);
    compiler->setProjector(pj);

    fringer = new PhaseCompiler(capCam);
    fringer->setProjector(pj);

    singleCal->setCamera(capCam);
    singleCal2->setCamera(capCam2);

    // debug our components

    connect(pj,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
//    connect(compiler,
//            SIGNAL(visualBinaryFrame(cv::Mat)),
//            this,
//            SLOT(debugImage(cv::Mat)));
    connect(compiler,
            SIGNAL(visualBinaryFrame(cv::Mat)),
            this,
            SLOT(writeDebugImg1(cv::Mat)));
    connect(compiler,
            SIGNAL(binaryFrameCaptured(cv::Mat,bool)),
            this,
            SLOT(binaryImageCaptured(cv::Mat,bool)));
    connect(compiler,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(calib,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(fringer, SIGNAL(phaseMapCaptured(cv::Mat,bool)),
            this,    SLOT(phaseMapCaptured(cv::Mat,bool)));

    connect(ui->analysis, SIGNAL(plotCrossSection(QString,cv::Mat)),
            ui->plot,     SLOT(plotLine(QString,cv::Mat)));
    connect(ui->analysis, SIGNAL(cropXChanged(int,int)),
            ui->plot,     SLOT(setXRange(int,int)));

//    standard = new DotMatrixStandard(cv::Size(6,7),
//                                     0.91*20e-3, 0.91*17e-3,
//                                     0.91*10e-3);
    standard = new ChessboardStandard(cv::Size(9,12),20e-3);
    singleCal->setStandard(standard);

}

MainWindow::~MainWindow()
{
    delete ui;
    QCoreApplication::exit(0);

}

void MainWindow::debug(QString str)
{
    debugWin->setPlainText(QString("%1\n%2")
                            .arg(debugWin->toPlainText())
                            .arg(str));
    QScrollBar *sb = debugWin->verticalScrollBar();
    sb->setValue(sb->maximum());
}

void MainWindow::debug(const char *str)
{
    debug(QString(str));
}

void MainWindow::showDebug()
{
    debugWin->show();
}

void MainWindow::cameraSettingsChanged(QCamera *first, QCamera *)
{
    QOpenCVCamera *cv = dynamic_cast<QOpenCVCamera*>(first);
    if(!cv) return;
    connect(first,
            SIGNAL(frameCaptured(cv::Mat,QCamera::FrameType,QCamera*)),
            this,
            SLOT(debugImage(cv::Mat,QCamera::FrameType,QCamera*)));
    cv->startStream();
}

void MainWindow::debugImage(cv::Mat im,
                            QCamera*,
                            QProjector::Pattern*)
{
    debug("User interface displays image.");
    if(im.channels()==3){
        cv::Mat3b img;
        im.convertTo(img,CV_8UC3);
        dbgIm->displayImage(img,true);
        dbgIm->show();
        return;
    }
    cv::Mat_<double> m;
    im.convertTo(m,CV_64F);
    dbgIm->displayImage(m,true);
    dbgIm->show();
    dbgIm->raise();
}

void MainWindow::debugImage(cv::Mat im)
{
    debugImage(im,0,0);
}

void MainWindow::binaryImageCaptured(cv::Mat binary, bool)
{
    // do nothing if the calibration dialog is open
    // hacky; fix later
    if(calib->isVisible()) return;
    binary.convertTo(lastBinaryFrame,CV_64F);
    ui->analysis->setBinary(lastBinaryFrame);
    computeCombinedGeometry();

    geom = Triangulator::computeSheet(
                lastBinaryFrame,
                camera,
                projector,
                1);
//    geom->removeNonManifold();
//    std::cout << "foreground: " << geom << '\n';
//    if(bg) geom->removeBackground(bg,0.005);
    ui->modelView->setData(geom);
}

void MainWindow::writeDebugImg1(cv::Mat im)
{
    cv::imwrite("/home/ryan/Documents/mqp-data/debug/cam1.png",im);
}

void MainWindow::writeDebugImg2(cv::Mat im)
{
    cv::imwrite("/home/ryan/Documents/mqp-data/debug/cam2.png",im);
}

void MainWindow::saveSTL()
{
    QString fnm;
    fnm = QFileDialog::getSaveFileName(
                    this,
                    tr("Save STL"),
                    QDir::homePath(),
                    tr("Standard Tesselation Language(*.stl)"));
    if(fnm.length()>0){
        if(!fnm.endsWith(".stl")){
            fnm = fnm.append(".stl");
        }
        geom->saveSTL(fnm.toLocal8Bit().data());
    }
}

void MainWindow::setAsBackground()
{
    std::cout << "background: " << geom << '\n';
    bg = geom;
    ui->modelView->setBackground(geom);
    ui->modelView->setData(0);
}

void MainWindow::setShifts(int shifts)
{
    sinusoidShifts = shifts;
}

void MainWindow::setBits(int bits)
{
    sinusoidPower = bits;
}

void MainWindow::showAbout()
{
    about.show();
}

void MainWindow::setFullScreen(bool fs)
{
    if(fs){
        showFullScreen();
    }else{
        showNormal();
    }
}

void MainWindow::showCameraSettings()
{
    debug("Showing camera settings.");
    singleCal->open();
}

void MainWindow::showCamera2Settings()
{
    debug("Showing camera 2 settings.");
    singleCal2->open();
}

void MainWindow::showProjectorSettings()
{
    projSettings.show();
}

void MainWindow::showCalStdSettings()
{
    debug("Show calibration standard settings.");
    stdSettings.show();
}

void MainWindow::showCalibrationDialog()
{
    calib->setLeft(camera);
    calib->setRight(camera2);
    calib->setProjector(projector);
    calib->setBinary(compiler);
    calib->show();
}

void MainWindow::quitProgram()
{
    QCoreApplication::exit(0);
}

void MainWindow::adjustCalStd()
{
    enableCalibrate();
}

void MainWindow::takeFrame()
{
    compiler->requestFrame(10);
    fringer->requestFrame(1<<(sinusoidPower-1),sinusoidShifts);
}

void MainWindow::phaseMapCaptured(cv::Mat ph, bool)
{
    lastPhaseMap = ph;
    ui->analysis->setPhaseMap(ph);
    computeCombinedGeometry();
}

void MainWindow::closeEvent(QCloseEvent *)
{
    quitProgram();
}

void MainWindow::enableCalibrate()
{
//    ui->actionCalibrate->setEnabled(true);
}

void MainWindow::computeCombinedGeometry()
{
    return;
    lastCombined = Triangulator::combineBinaryAndPhase(
                lastBinaryFrame,
                lastPhaseMap,
                sinusoidPower);
    geom = Triangulator::computeSheet(
                lastPhaseMap,
                camera,
                projector,
                1);
    ui->modelView->setData(geom);
}
<commit_msg>stash<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <stdio.h>
#include <QScrollBar>
#include <QFileDialog>
#include <opencv2/imgproc/imgproc.hpp>
#include "hardware/camera/qopencvcamera.h"
#include "hardware/projector/seconddisplayprojector.h"
#include "geom/triangulator.h"
#include "hardware/projector/gridpattern.h"
#include "hardware/projector/flatcolorpattern.h"
#include "hardware/standards/dotmatrixstandard.h"
#include "hardware/standards/chessboardstandard.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    calib(new CalibrationDialog),
    debugWin(new QPlainTextEdit),
    graycode(new GrayCodePattern(1,false)),
    dbgIm(new ImageViewWidget),
    singleCal(new SingleCalibrationDialog),
    bg(0),
    singleCal2(new SingleCalibrationDialog),
    sinusoidPower(6),
    sinusoidShifts(8)
{
    ui->setupUi(this);

    debugWin->setPlainText(QString("ScanStudio started."));
    debugWin->setWindowTitle(QString("Debugging info"));
    debugWin->setWindowIcon(
                QIcon(tr(":/icons/oxygen/camera-web-64.png")));
    debugWin->setWindowFlags(Qt::WindowStaysOnTopHint);
    debugWin->setReadOnly(true);

    connect(&stdSettings,SIGNAL(accept()),this,SLOT(adjustCalStd()));
    connect(&camSettings,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(&camSettings,
            SIGNAL(acceptedCameras(QCamera*,QCamera*)),
            this,
            SLOT(cameraSettingsChanged(QCamera*,QCamera*)));

    ui->modelView->zoomFit();
    dbgIm->setWindowTitle("Debugging -- camera view");

    QOpenCVCamera *capCam = new QOpenCVCamera(2);
    connect(capCam,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    capCam->setResolution(1600,1200);
    capCam->setPrincipalPoint(800,600);
    capCam->setFocalLength(1300);
    capCam->setPosition(cv::Vec3d(-0.25,-0.01,0));
    capCam->setOrientation(cv::Vec3d(0,M_PI/12,0));

    camera = capCam;

    QOpenCVCamera *capCam2 = new QOpenCVCamera(1);
    connect(capCam2,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    capCam2->setResolution(1600,1200);
    capCam2->setPrincipalPoint(800,600);
    capCam2->setFocalLength(1300);
    capCam2->setPosition(cv::Vec3d(0.2,0,0));
    capCam2->setOrientation(cv::Vec3d(0,-0.1,0));

    camera2 = capCam2;

    SecondDisplayProjector *pj = new SecondDisplayProjector();
    pj->setScreen(1);
    pj->setResolution(848,480);
    pj->setPrincipalPoint(848/2,480/2);
    pj->setPosition(cv::Vec3d(0.0,0,0));
    pj->setOrientation(cv::Vec3d(-0*M_PI/180,0.0,0.0));
    pj->setFocalLength(3600);

    GridPattern *grid = new GridPattern();
    pj->queue(grid);
//    FlatColorPattern *flat = new FlatColorPattern();
//    pj->queue(flat);

    projector = pj;
    capCam->connectProjector(pj);
    capCam2->connectProjector(pj);

    compiler = new BinaryCompiler(capCam);
    compiler->setProjector(pj);

    fringer = new PhaseCompiler(capCam);
    fringer->setProjector(pj);

    singleCal->setCamera(capCam);
    singleCal2->setCamera(capCam2);

    // debug our components

    connect(pj,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
//    connect(compiler,
//            SIGNAL(visualBinaryFrame(cv::Mat)),
//            this,
//            SLOT(debugImage(cv::Mat)));
    connect(compiler,
            SIGNAL(visualBinaryFrame(cv::Mat)),
            this,
            SLOT(writeDebugImg1(cv::Mat)));
    connect(compiler,
            SIGNAL(binaryFrameCaptured(cv::Mat,bool)),
            this,
            SLOT(binaryImageCaptured(cv::Mat,bool)));
    connect(compiler,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(calib,
            SIGNAL(debug(QString)),
            this,
            SLOT(debug(QString)));
    connect(fringer, SIGNAL(phaseMapCaptured(cv::Mat,bool)),
            this,    SLOT(phaseMapCaptured(cv::Mat,bool)));

    connect(ui->analysis, SIGNAL(plotCrossSection(QString,cv::Mat)),
            ui->plot,     SLOT(plotLine(QString,cv::Mat)));
    connect(ui->analysis, SIGNAL(cropXChanged(int,int)),
            ui->plot,     SLOT(setXRange(int,int)));

//    standard = new DotMatrixStandard(cv::Size(6,7),
//                                     0.91*20e-3, 0.91*17e-3,
//                                     0.91*10e-3);
    standard = new ChessboardStandard(cv::Size(9,12),20e-3);
    singleCal->setStandard(standard);

}

MainWindow::~MainWindow()
{
    delete ui;
    QCoreApplication::exit(0);

}

void MainWindow::debug(QString str)
{
    debugWin->setPlainText(QString("%1\n%2")
                            .arg(debugWin->toPlainText())
                            .arg(str));
    QScrollBar *sb = debugWin->verticalScrollBar();
    sb->setValue(sb->maximum());
}

void MainWindow::debug(const char *str)
{
    debug(QString(str));
}

void MainWindow::showDebug()
{
    debugWin->show();
}

void MainWindow::cameraSettingsChanged(QCamera *first, QCamera *)
{
    QOpenCVCamera *cv = dynamic_cast<QOpenCVCamera*>(first);
    if(!cv) return;
    connect(first,
            SIGNAL(frameCaptured(cv::Mat,QCamera::FrameType,QCamera*)),
            this,
            SLOT(debugImage(cv::Mat,QCamera::FrameType,QCamera*)));
    cv->startStream();
}

void MainWindow::debugImage(cv::Mat im,
                            QCamera*,
                            QProjector::Pattern*)
{
    debug("User interface displays image.");
    if(im.channels()==3){
        cv::Mat3b img;
        im.convertTo(img,CV_8UC3);
        dbgIm->displayImage(img,true);
        dbgIm->show();
        return;
    }
    cv::Mat_<double> m;
    im.convertTo(m,CV_64F);
    dbgIm->displayImage(m,true);
    dbgIm->show();
    dbgIm->raise();
}

void MainWindow::debugImage(cv::Mat im)
{
    debugImage(im,0,0);
}

void MainWindow::binaryImageCaptured(cv::Mat binary, bool)
{
    // do nothing if the calibration dialog is open
    // hacky; fix later
    if(calib->isVisible()) return;
    binary.convertTo(lastBinaryFrame,CV_64F);
    ui->analysis->setBinary(lastBinaryFrame);
    computeCombinedGeometry();

    geom = Triangulator::computeSheet(
                lastBinaryFrame,
                camera,
                projector,
                1);
//    geom->removeNonManifold();
//    std::cout << "foreground: " << geom << '\n';
//    if(bg) geom->removeBackground(bg,0.005);
    ui->modelView->setData(geom);
}

void MainWindow::writeDebugImg1(cv::Mat im)
{
    cv::imwrite("/home/ryan/Documents/mqp-data/debug/cam1.png",im);
}

void MainWindow::writeDebugImg2(cv::Mat im)
{
    cv::imwrite("/home/ryan/Documents/mqp-data/debug/cam2.png",im);
}

void MainWindow::saveSTL()
{
    QString fnm;
    fnm = QFileDialog::getSaveFileName(
                    this,
                    tr("Save STL"),
                    QDir::homePath(),
                    tr("Standard Tesselation Language(*.stl)"));
    if(fnm.length()>0){
        if(!fnm.endsWith(".stl")){
            fnm = fnm.append(".stl");
        }
        geom->saveSTL(fnm.toLocal8Bit().data());
    }
}

void MainWindow::setAsBackground()
{
    std::cout << "background: " << geom << '\n';
    bg = geom;
    ui->modelView->setBackground(geom);
    ui->modelView->setData(0);
}

void MainWindow::setShifts(int shifts)
{
    sinusoidShifts = shifts;
}

void MainWindow::setBits(int bits)
{
    sinusoidPower = bits;
}

void MainWindow::showAbout()
{
    about.show();
}

void MainWindow::setFullScreen(bool fs)
{
    if(fs){
        showFullScreen();
    }else{
        showNormal();
    }
}

void MainWindow::showCameraSettings()
{
    debug("Showing camera settings.");
    singleCal->open();
}

void MainWindow::showCamera2Settings()
{
    debug("Showing camera 2 settings.");
    singleCal2->open();
}

void MainWindow::showProjectorSettings()
{
    projSettings.show();
}

void MainWindow::showCalStdSettings()
{
    debug("Show calibration standard settings.");
    stdSettings.show();
}

void MainWindow::showCalibrationDialog()
{
    calib->setLeft(camera);
    calib->setRight(camera2);
    calib->setProjector(projector);
    calib->setBinary(compiler);
    calib->show();
}

void MainWindow::quitProgram()
{
    QCoreApplication::exit(0);
}

void MainWindow::adjustCalStd()
{
    enableCalibrate();
}

void MainWindow::takeFrame()
{
    compiler->requestFrame(10);
//    fringer->requestFrame(1<<(sinusoidPower-1),sinusoidShifts);
}

void MainWindow::phaseMapCaptured(cv::Mat ph, bool)
{
    lastPhaseMap = ph;
    ui->analysis->setPhaseMap(ph);
    computeCombinedGeometry();
}

void MainWindow::closeEvent(QCloseEvent *)
{
    quitProgram();
}

void MainWindow::enableCalibrate()
{
//    ui->actionCalibrate->setEnabled(true);
}

void MainWindow::computeCombinedGeometry()
{
    return;
    lastCombined = Triangulator::combineBinaryAndPhase(
                lastBinaryFrame,
                lastPhaseMap,
                sinusoidPower);
    geom = Triangulator::computeSheet(
                lastPhaseMap,
                camera,
                projector,
                1);
    ui->modelView->setData(geom);
}
<|endoftext|>