<commit_before>/******************************************************************************

  nomlib - C++11 cross-platform game engine

Copyright (c) 2013, Jeffrey Carpenter <jeffrey.carp@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

******************************************************************************/
#include "nomlib/graphics/fonts/TrueTypeFont.hpp"

namespace nom {

TrueTypeFont::TrueTypeFont ( void ) :
  type_ ( IFont::FontType::TrueTypeFont ),
  sheet_width_ ( 16 ),  // Arbitrary; based on nom::BitmapFont
  sheet_height_ ( 16 ), // Arbitrary; based on nom::BitmapFont
  font_size_ ( 14 ), // Terrible Eyesight (TM)
  use_cache_ ( false )
{
  NOM_LOG_TRACE ( NOM );
}

TrueTypeFont::~TrueTypeFont ( void )
{
  NOM_LOG_TRACE ( NOM );
}

TrueTypeFont::TrueTypeFont ( const TrueTypeFont& copy ) :
  type_ { copy.type() },
  sheet_width_ { copy.sheet_width() },
  sheet_height_ { copy.sheet_height() },
  font_ { copy.font_ },
  pages_ { copy.pages() },
  font_size_ { copy.font_size() },
  filename_ { copy.filename_ },
  use_cache_ { copy.use_cache_ },
  metrics_ { copy.metrics() }
{
  //
}

IFont::SharedPtr TrueTypeFont::clone ( void ) const
{
  return TrueTypeFont::SharedPtr ( new TrueTypeFont ( *this ) );
}

bool TrueTypeFont::valid ( void ) const
{
  if ( this->font() != nullptr ) return true;

  return false;
}

enum IFont::FontType TrueTypeFont::type ( void ) const
{
  return this->type_;
}

TTF_Font* TrueTypeFont::font ( void ) const
{
  return this->font_.get();
}

SDL_SURFACE::RawPtr TrueTypeFont::image ( uint32 character_size ) const
{
  return this->pages_[character_size].texture->image();
}

int TrueTypeFont::spacing ( uint32 character_size ) const
{
  return this->pages_[character_size].glyphs[32].advance;
}

sint TrueTypeFont::font_size ( void ) const
{
  return this->font_size_;
}

int TrueTypeFont::newline ( uint32 character_size ) const
{
  return this->metrics_.newline;
}

sint TrueTypeFont::kerning ( uint32 first_char, uint32 second_char, uint32 character_size ) const
{
  return -1; // TODO
}

/*
void TrueTypeFont::set_font_size ( sint point_size )
{
  sint original_font_size = this->font_size_;

  this->font_size_ = point_size;

  if ( this->rebuild() == false )
  {
NOM_LOG_ERR ( NOM, "Could not set new font size." );
    this->font_size_ = original_font_size;
    return;
  }
}
*/

/*
void TrueTypeFont::setFontStyle ( int32 style, uint8 options )
{
  switch ( style )
  {
    default: break;
    case FontStyle::Regular:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_NORMAL );
    }
    break;

    case FontStyle::Bold:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_BOLD );
    }
    break;

    case FontStyle::Italic:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_ITALIC );
    }
    break;

    case FontStyle::Underlined:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_UNDERLINE );
    }
    break;

    /// Text effect utilizing alpha channels for the appearance of gray text
    case FontStyle::Faded:
    {
      this->text_style = FontStyle::Faded;
      this->style_options = options;
    break;
    }
  }
}
*/

/*
int32 TrueTypeFont::getFontOutline ( void ) const
{
  return TTF_GetFontOutline ( this->font.get() );
}

void TrueTypeFont::setFontOutline ( int32 depth )
{
  TTF_SetFontOutline ( this->font.get(), depth );
}
*/

const Glyph& TrueTypeFont::glyph ( uint32 codepoint, uint32 character_size ) const
{
  GlyphAtlas& glyphs = this->pages_[character_size].glyphs;

  return glyphs[codepoint];
/* TODO
  GlyphAtlas::const_iterator it = glyphs.find(codepoint);

  if ( it != glyphs.end() )
  {
    return it->second;
  }
  else
  {
    // FIXME: implement support for handling this condition
    return Glyph();
  }
*/
}

bool TrueTypeFont::load ( const std::string& filename, const Color4u& colorkey,
                          bool use_cache
                        )
{
  this->font_ = std::shared_ptr<TTF_Font> ( TTF_OpenFont ( filename.c_str(), this->font_size() ), priv::TTF_FreeFont );

  if ( this->valid() == false )
  {
NOM_LOG_ERR ( NOM, "Could not load TTF file: " + filename );
    return false;
  }

  // Store the new filename & caching choice for future reference; primarily
  // used when rebuilding font metrics, such as when we change the font point
  // size or load a new font.
  this->filename_ = filename;
  this->use_cache_ = use_cache;

  // Attempt to build font metrics
  if ( this->build() == false )
  {
    NOM_LOG_ERR ( NOM, "Could not build TrueType font metrics" );
    return false;
  }

  return true;
}

bool TrueTypeFont::build ( uint32 character_size )
{
  int ret = 0; // Error code
  uint16 ascii_char; // Integer type expected by SDL2_ttf
  const uint32 starting_glyph = 32; // space character
  const uint32 ending_glyph = 127; // tilde character

  // Glyph metrics
  int advance = 0; // spacing in between characters separated by a space char
  int glyph_width = 0; // glyph's width in pixels
  int glyph_height = 0; // glyph's height in pixels

  // Texture sheet calculations
  int padding = 1;
  int spacing = 2;

  Image glyph_image; // Raster bitmap of a glyph
  IntRect blit; // rendering bounding coordinates
  //FontPage& page = this->pages_[0]; // our font's glyph page

  if ( this->valid() == false )
  {
    NOM_LOG_ERR ( NOM, "TTF pointer is invalid" );
    return false;
  }

  const Point2i sheet_size =  Point2i ( sheet_width() * sheet_height(), // 256
                                        sheet_width() * sheet_height()  // 256
                                      );

  this->pages_[0].texture->initialize ( sheet_size );

  // ASCII 32..127 is the standard glyph set -- 94 printable characters;
  //
  // 32 = ' ' (space)
  // 65 = 'A'
  // 122 = 'z'
  // 126 = '~' (tilde)
  //
  // glyph < sheet_width() * sheet_height() = 256 glyphs
  for ( uint32 glyph = starting_glyph; glyph < ending_glyph; ++glyph )
  {
    ascii_char = static_cast<uint16> ( glyph );

    if ( TTF_GlyphIsProvided ( this->font(), ascii_char ) )
    {
      // We obtain width & height of a glyph from its rendered form
      glyph_image.initialize ( TTF_RenderGlyph_Solid ( this->font(), ascii_char, SDL_COLOR(NOM_COLOR4U_WHITE) ) );

      if ( glyph_image.valid() == false )
      {
        NOM_LOG_ERR(NOM, TTF_GetError() );
        return false;
      }

      glyph_width = glyph_image.width();
      glyph_height = glyph_image.height();

      // -_-
      // Disappointedly, the only metric that we can use here is the advance
      ret = TTF_GlyphMetrics  ( this->font(),
                                ascii_char,
                                nullptr, // Left (X) origin
                                nullptr, // Width
                                nullptr, // Top (Y) origin
                                nullptr, // Height
                                &advance
                              );

      if ( ret != 0 ) // Likely to be a missing glyph
      {
        NOM_LOG_ERR ( NOM, TTF_GetError() );
        return false;
      }
      this->pages_[0].glyphs[glyph].advance = advance;

      // Calculate the best packing of the glyph, so that we are able to fit
      // everything on the sheet without overlap, etc.
      this->pages_[0].glyphs[glyph].bounds = this->glyph_rect ( this->pages_[0], glyph_width + spacing * padding, glyph_height + spacing * padding );

      #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS)
        NOM_DUMP_VAR(glyph); // integer position
        NOM_DUMP_VAR(static_cast<uchar>(glyph)); // printable
        NOM_DUMP_VAR(this->glyph(glyph).bounds); // bounding box
        NOM_DUMP_VAR(advance); // spacing
      #endif

      // Prepare the rendering coordinates for writing the glyph our texture map
      // Prepare the coordinates for rendering a glyph onto our texture sheet
      // we are creating.
      blit.x = this->pages_[0].glyphs[glyph].bounds.x;
      blit.y = this->pages_[0].glyphs[glyph].bounds.y;
      blit.w = -1; // Why -1 ???
      blit.h = -1; // Why -1 ???
      glyph_image.draw( this->pages_[0].texture->image(), blit );

      //this->pages_[0].texture->set_alpha(255);
      //this->pages_[0].texture->set_blend_mode(SDL_BLENDMODE_BLEND);

      // Turn color key transparency on so we are not left with a black,
      // AKA non-transparent background.
      this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, true );

      // Dump all of the rendered glyphs as a series of image files -- the
      // filenames will be the ASCII numeric values. The output should consist
      // of individual glyph files, at whatever font scale previously loaded.
      //
      // You could easily create a new sprite sheet out of these individual
      // frames!
      #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS_PNG)
        std::string ascii_filename = std::to_string(ascii_char);
        ascii_filename.append(".png");
        glyph_image.save_png(ascii_filename);
      #endif
    } // end if glyph is provided
  } // end for glyphs loop

  // Export the destination texture -- this should be a texture sheet that we
  // can render from within the nom::Label class.
  #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS)
    // We intentionally flip color keying off here temporarily so that our eyes
    // have an easier time.
    //
    // You'll may want to leave color keying on if you are interested in using
    // these bitmap glyphs for anything else!
    this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, false );
    this->pages_[0].texture->save_png("ttf_dest.png");
    this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, true );
  #endif

  // Build up our font-wide metrics
  if (this->valid() == false )
  {
    NOM_LOG_ERR(NOM, "Could not build font metrics; our font is invalid" );
    return false;
  }

  this->metrics_.height = TTF_FontHeight ( this->font() );
  this->metrics_.newline = TTF_FontLineSkip ( this->font() );
  this->metrics_.ascent = TTF_FontAscent ( this->font() );
  this->metrics_.descent = TTF_FontDescent ( this->font() );
  this->metrics_.family = TTF_FontFaceFamilyName ( this->font() );

  return true;
}

const GlyphPage& TrueTypeFont::pages ( void ) const
{
  return this->pages_;
}

sint TrueTypeFont::sheet_width ( void ) const
{
  return this->sheet_width_;
}

sint TrueTypeFont::sheet_height ( void ) const
{
  return this->sheet_height_;
}

const IntRect TrueTypeFont::glyph_rect ( FontPage& page, int width, int height ) const
{
  //std::unique_ptr<FontRow> row;
  FontRow* row = nullptr;
  float best_ratio = 0;

  // Find an optimal layout within our texture's sheet for a specified glyph
  for ( std::vector<FontRow>::iterator it = page.rows.begin(); it != page.rows.end() && ! row; ++it )
  {
    float ratio = static_cast<float> ( height ) / it->height;

    // Ignore rows that are either too small or too high
    if ( ( ratio < 0.7f ) || ( ratio > 1.f ) )
    {
      continue;
    }

    // Check if there's enough horizontal space left in the row
    if ( width > page.texture->width() - it->width )
    {
      continue;
    }

    // Make sure that this new row is the best found so far
    if ( ratio < best_ratio )
    {
      continue;
    }

    // The current row passed all the tests: we can select it
    row = &*it;
    best_ratio = ratio;
  }

  // If we didn't find a matching row, create a new one (10% taller than the glyph)
  if ( ! row )
  {
    int row_height = height + height / 10;
    while ( page.next_row + row_height >= page.texture->height() )
    {
      // Not enough space: resize the texture if possible
      uint texture_width  = page.texture->width();
      uint texture_height = page.texture->height();
      if ( ( texture_width * 2 <= Texture::maximum_size().x )
           && ( texture_height * 2 <= Texture::maximum_size().y ) )
      {
        // Make the texture 2 times bigger
        NOM_LOG_ERR(NOM,"WATCH OUT -- texture rescaling is 50/50 chance of working");
        Image sheet;
        sheet.initialize ( Point2i( texture_width * 2, texture_height * 2 ) );
        sheet.draw ( page.texture->image(), IntRect(0,0,-1,-1) );
      }
      else
      {
        // Oops, we've reached the maximum texture size...
        NOM_LOG_ERR( NOM, "Failed to add new character to sheet: sheet >= maximum texture size" );
        return IntRect ( 0, 0, 2, 2 );
      }
    }

    // We can now create the new row
    page.rows.push_back ( FontRow ( page.next_row, row_height ) );
    page.next_row += row_height;
    row = &page.rows.back();
  }

  // Find the glyph's rectangle on the selected row
  IntRect rect ( row->width, row->top, width, height );

  // Update the row informations
  row->width += width;

  return rect;
}

struct FontMetrics TrueTypeFont::metrics ( void ) const
{
  return this->metrics_;
}

} // namespace nom
<commit_msg>TrueTypeFont: Preliminary support for font size above default sheet size<commit_after>/******************************************************************************

  nomlib - C++11 cross-platform game engine

Copyright (c) 2013, Jeffrey Carpenter <jeffrey.carp@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

******************************************************************************/
#include "nomlib/graphics/fonts/TrueTypeFont.hpp"

namespace nom {

TrueTypeFont::TrueTypeFont ( void ) :
  type_ ( IFont::FontType::TrueTypeFont ),
  sheet_width_ ( 16 ),  // Arbitrary; based on nom::BitmapFont
  sheet_height_ ( 16 ), // Arbitrary; based on nom::BitmapFont
  font_size_ ( 14 ), // Terrible Eyesight (TM)
  use_cache_ ( false )
{
  NOM_LOG_TRACE ( NOM );
}

TrueTypeFont::~TrueTypeFont ( void )
{
  NOM_LOG_TRACE ( NOM );
}

TrueTypeFont::TrueTypeFont ( const TrueTypeFont& copy ) :
  type_ { copy.type() },
  sheet_width_ { copy.sheet_width() },
  sheet_height_ { copy.sheet_height() },
  font_ { copy.font_ },
  pages_ { copy.pages() },
  font_size_ { copy.font_size() },
  filename_ { copy.filename_ },
  use_cache_ { copy.use_cache_ },
  metrics_ { copy.metrics() }
{
  //
}

IFont::SharedPtr TrueTypeFont::clone ( void ) const
{
  return TrueTypeFont::SharedPtr ( new TrueTypeFont ( *this ) );
}

bool TrueTypeFont::valid ( void ) const
{
  if ( this->font() != nullptr ) return true;

  return false;
}

enum IFont::FontType TrueTypeFont::type ( void ) const
{
  return this->type_;
}

TTF_Font* TrueTypeFont::font ( void ) const
{
  return this->font_.get();
}

SDL_SURFACE::RawPtr TrueTypeFont::image ( uint32 character_size ) const
{
  return this->pages_[character_size].texture->image();
}

int TrueTypeFont::spacing ( uint32 character_size ) const
{
  return this->pages_[character_size].glyphs[32].advance;
}

sint TrueTypeFont::font_size ( void ) const
{
  return this->font_size_;
}

int TrueTypeFont::newline ( uint32 character_size ) const
{
  return this->metrics_.newline;
}

sint TrueTypeFont::kerning ( uint32 first_char, uint32 second_char, uint32 character_size ) const
{
  return -1; // TODO
}

/*
void TrueTypeFont::set_font_size ( sint point_size )
{
  sint original_font_size = this->font_size_;

  this->font_size_ = point_size;

  if ( this->rebuild() == false )
  {
NOM_LOG_ERR ( NOM, "Could not set new font size." );
    this->font_size_ = original_font_size;
    return;
  }
}
*/

/*
void TrueTypeFont::setFontStyle ( int32 style, uint8 options )
{
  switch ( style )
  {
    default: break;
    case FontStyle::Regular:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_NORMAL );
    }
    break;

    case FontStyle::Bold:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_BOLD );
    }
    break;

    case FontStyle::Italic:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_ITALIC );
    }
    break;

    case FontStyle::Underlined:
    {
      TTF_SetFontStyle ( this->font.get(), TTF_STYLE_UNDERLINE );
    }
    break;

    /// Text effect utilizing alpha channels for the appearance of gray text
    case FontStyle::Faded:
    {
      this->text_style = FontStyle::Faded;
      this->style_options = options;
    break;
    }
  }
}
*/

/*
int32 TrueTypeFont::getFontOutline ( void ) const
{
  return TTF_GetFontOutline ( this->font.get() );
}

void TrueTypeFont::setFontOutline ( int32 depth )
{
  TTF_SetFontOutline ( this->font.get(), depth );
}
*/

const Glyph& TrueTypeFont::glyph ( uint32 codepoint, uint32 character_size ) const
{
  GlyphAtlas& glyphs = this->pages_[character_size].glyphs;

  return glyphs[codepoint];
/* TODO
  GlyphAtlas::const_iterator it = glyphs.find(codepoint);

  if ( it != glyphs.end() )
  {
    return it->second;
  }
  else
  {
    // FIXME: implement support for handling this condition
    return Glyph();
  }
*/
}

bool TrueTypeFont::load ( const std::string& filename, const Color4u& colorkey,
                          bool use_cache
                        )
{
  this->font_ = std::shared_ptr<TTF_Font> ( TTF_OpenFont ( filename.c_str(), this->font_size() ), priv::TTF_FreeFont );

  if ( this->valid() == false )
  {
NOM_LOG_ERR ( NOM, "Could not load TTF file: " + filename );
    return false;
  }

  // Store the new filename & caching choice for future reference; primarily
  // used when rebuilding font metrics, such as when we change the font point
  // size or load a new font.
  this->filename_ = filename;
  this->use_cache_ = use_cache;

  // Attempt to build font metrics
  if ( this->build() == false )
  {
    NOM_LOG_ERR ( NOM, "Could not build TrueType font metrics" );
    return false;
  }

  return true;
}

bool TrueTypeFont::build ( uint32 character_size )
{
  int ret = 0; // Error code
  uint16 ascii_char; // Integer type expected by SDL2_ttf
  const uint32 starting_glyph = 32; // space character
  const uint32 ending_glyph = 127; // tilde character

  // Glyph metrics
  int advance = 0; // spacing in between characters separated by a space char
  int glyph_width = 0; // glyph's width in pixels
  int glyph_height = 0; // glyph's height in pixels

  // Texture sheet calculations
  int padding = 1;
  int spacing = 2;

  Image glyph_image; // Raster bitmap of a glyph
  IntRect blit; // rendering bounding coordinates
  //FontPage& page = this->pages_[0]; // our font's glyph page

  if ( this->valid() == false )
  {
    NOM_LOG_ERR ( NOM, "TTF pointer is invalid" );
    return false;
  }

  const Point2i sheet_size =  Point2i ( sheet_width() * sheet_height(), // 256
                                        sheet_width() * sheet_height()  // 256
                                      );

  this->pages_[0].texture->initialize ( sheet_size );

  // ASCII 32..127 is the standard glyph set -- 94 printable characters;
  //
  // 32 = ' ' (space)
  // 65 = 'A'
  // 122 = 'z'
  // 126 = '~' (tilde)
  //
  // glyph < sheet_width() * sheet_height() = 256 glyphs
  for ( uint32 glyph = starting_glyph; glyph < ending_glyph; ++glyph )
  {
    ascii_char = static_cast<uint16> ( glyph );

    if ( TTF_GlyphIsProvided ( this->font(), ascii_char ) )
    {
      // We obtain width & height of a glyph from its rendered form
      glyph_image.initialize ( TTF_RenderGlyph_Solid ( this->font(), ascii_char, SDL_COLOR(NOM_COLOR4U_WHITE) ) );

      if ( glyph_image.valid() == false )
      {
        NOM_LOG_ERR(NOM, TTF_GetError() );
        return false;
      }

      glyph_width = glyph_image.width();
      glyph_height = glyph_image.height();

      // -_-
      // Disappointedly, the only metric that we can use here is the advance
      ret = TTF_GlyphMetrics  ( this->font(),
                                ascii_char,
                                nullptr, // Left (X) origin
                                nullptr, // Width
                                nullptr, // Top (Y) origin
                                nullptr, // Height
                                &advance
                              );

      if ( ret != 0 ) // Likely to be a missing glyph
      {
        NOM_LOG_ERR ( NOM, TTF_GetError() );
        return false;
      }
      this->pages_[0].glyphs[glyph].advance = advance;

      // Calculate the best packing of the glyph, so that we are able to fit
      // everything on the sheet without overlap, etc.
      this->pages_[0].glyphs[glyph].bounds = this->glyph_rect ( this->pages_[0], glyph_width + spacing * padding, glyph_height + spacing * padding );

      #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS)
        NOM_DUMP_VAR(glyph); // integer position
        NOM_DUMP_VAR(static_cast<uchar>(glyph)); // printable
        NOM_DUMP_VAR(this->glyph(glyph).bounds); // bounding box
        NOM_DUMP_VAR(advance); // spacing
      #endif

      // Prepare the rendering coordinates for writing the glyph our texture map
      // Prepare the coordinates for rendering a glyph onto our texture sheet
      // we are creating.
      blit.x = this->pages_[0].glyphs[glyph].bounds.x;
      blit.y = this->pages_[0].glyphs[glyph].bounds.y;
      blit.w = -1; // Why -1 ???
      blit.h = -1; // Why -1 ???
      glyph_image.draw( this->pages_[0].texture->image(), blit );

      // Dump all of the rendered glyphs as a series of image files -- the
      // filenames will be the ASCII numeric values. The output should consist
      // of individual glyph files, at whatever font scale previously loaded.
      //
      // You could easily create a new sprite sheet out of these individual
      // frames!
      #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS_PNG)
        std::string ascii_filename = std::to_string(ascii_char);
        ascii_filename.append(".png");
        glyph_image.save_png(ascii_filename);
      #endif
    } // end if glyph is provided
  } // end for glyphs loop

  // Turn color key transparency on so we are not left with a black,
  // AKA non-transparent background.
  this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, true );

  // Export the destination texture -- this should be a texture sheet that we
  // can render from within the nom::Label class.
  #if defined(NOM_DEBUG_SDL2_TRUE_TYPE_FONT_GLYPHS)
    // We intentionally flip color keying off here temporarily so that our eyes
    // have an easier time.
    //
    // You'll may want to leave color keying on if you are interested in using
    // these bitmap glyphs for anything else!
    this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, false );
    this->pages_[0].texture->save_png("ttf_dest.png");
    this->pages_[0].texture->set_colorkey ( NOM_COLOR4U_BLACK, true );
  #endif

  // Build up our font-wide metrics
  if (this->valid() == false )
  {
    NOM_LOG_ERR(NOM, "Could not build font metrics; our font is invalid" );
    return false;
  }

  this->metrics_.height = TTF_FontHeight ( this->font() );
  this->metrics_.newline = TTF_FontLineSkip ( this->font() );
  this->metrics_.ascent = TTF_FontAscent ( this->font() );
  this->metrics_.descent = TTF_FontDescent ( this->font() );
  this->metrics_.family = TTF_FontFaceFamilyName ( this->font() );

  return true;
}

const GlyphPage& TrueTypeFont::pages ( void ) const
{
  return this->pages_;
}

sint TrueTypeFont::sheet_width ( void ) const
{
  return this->sheet_width_;
}

sint TrueTypeFont::sheet_height ( void ) const
{
  return this->sheet_height_;
}

const IntRect TrueTypeFont::glyph_rect ( FontPage& page, int width, int height ) const
{
  //std::unique_ptr<FontRow> row;
  FontRow* row = nullptr;
  float best_ratio = 0;

  // Find an optimal layout within our texture's sheet for a specified glyph
  for ( std::vector<FontRow>::iterator it = page.rows.begin(); it != page.rows.end() && ! row; ++it )
  {
    float ratio = static_cast<float> ( height ) / it->height;

    // Ignore rows that are either too small or too high
    if ( ( ratio < 0.7f ) || ( ratio > 1.f ) )
    {
      continue;
    }

    // Check if there's enough horizontal space left in the row
    if ( width > page.texture->width() - it->width )
    {
      continue;
    }

    // Make sure that this new row is the best found so far
    if ( ratio < best_ratio )
    {
      continue;
    }

    // The current row passed all the tests: we can select it
    row = &*it;
    best_ratio = ratio;
  }

  // If we didn't find a matching row, create a new one
  // (10% taller than the glyph)
  if ( ! row )
  {
    int row_height = height + height / 10;
    if ( page.next_row + row_height >= page.texture->height() )
    {
      // Not enough space: resize the texture if possible
      uint texture_width  = page.texture->width();
      uint texture_height = page.texture->height();
      if  (
            ( texture_width * 2 <= Texture::maximum_size().x )
            &&
            ( texture_height * 2 <= Texture::maximum_size().y )
          )
      {
        // Make the texture 2 times bigger
        Image sheet;
        sheet.initialize ( Point2i( texture_width * 2, texture_height * 2 ) );
        page.texture->draw ( sheet.image(), IntRect(0, 0, -1, -1) );
        //sheet.save_png("src.png");
        page.texture->initialize ( sheet.image() );
      }
      else
      {
        // Oops, we've reached the maximum texture size...
        NOM_LOG_ERR( NOM, "Failed to add new character to sheet: sheet >= maximum texture size" );
        return IntRect ( 0, 0, 2, 2 );
      }
    }

    // We can now create the new row
    page.rows.push_back ( FontRow ( page.next_row, row_height ) );
    page.next_row += row_height;
    row = &page.rows.back();
  }

  // Find the glyph's rectangle on the selected row
  IntRect rect ( row->width, row->top, width, height );

  // Update the row informations
  row->width += width;

  return rect;
}

struct FontMetrics TrueTypeFont::metrics ( void ) const
{
  return this->metrics_;
}

} // namespace nom
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - a low Mach number Navier-Stokes Finite-Element Solver
//
// Copyright (C) 2010,2011 The PECOS Development Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the Version 2 GNU General
// Public License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA
//
//-----------------------------------------------------------------------el-
//
// $Id$
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

#include "grins_solver.h"

// For instantiating systems
#include "multiphysics_sys.h"

// libMesh I/O classes
#include "gmv_io.h"
#include "tecplot_io.h"
#include "exodusII_io.h"
#include "vtk_io.h"

#include <iostream>

template< class T >
GRINS::Solver<T>::Solver( )
  : _system_initialized(false)
{
  return;
}

template< class T >
GRINS::Solver<T>::~Solver()
{
  // If we initialized the system, we need to destroy the
  // EquationSystems object.
  if(this->_system_initialized)
    {
      delete this->_equation_systems;
    }
  return;
}

template< class T >
void GRINS::Solver<T>::read_input_options( const GetPot& input )
{
  // Linear/Nonlinear solver options
  this->_max_nonlinear_iterations    = input("linear-nonlinear-solver/max_nonlinear_iterations", 10 );
  this->_relative_step_tolerance     = input("linear-nonlinear-solver/relative_step_tolerance", 1.e-6 );
  this->_absolute_step_tolerance     = input("linear-nonlinear-solver/absolute_step_tolerance", 0.0 );
  this->_relative_residual_tolerance = input("linear-nonlinear-solver/relative_residual_tolerance", 1.e-16 );
  this->_absolute_residual_tolerance = input("linear-nonlinear-solver/absolute_residual_tolerance", 0.0 );
  this->_max_linear_iterations       = input("linear-nonlinear-solver/max_linear_iterations", 500 );
  this->_initial_linear_tolerance    = input("linear-nonlinear-solver/initial_linear_tolerance", 1.e-3 );

  // Unsteady solver options
  this->_transient   = input("unsteady-solver/transient", false );
  this->_theta       = input("unsteady-solver/theta", 0.5 );
  this->_n_timesteps = input("unsteady-solver/n_timesteps", 1 );
  this->_deltat      = input("unsteady-solver/deltat", 0.0 ); //TODO: Better default here?

  // Visualization options
  this->_vis_output_file_prefix = input("vis-options/vis_output_file_prefix", "unknown" );
  this->_output_vis_time_series = input("vis-options/output_vis_time_series", false);

  unsigned int num_formats = input.vector_variable_size("vis-options/output_format");

  // If no format specified, default to ExodusII only
  if( num_formats == 0 )
    {
      _output_format.push_back("ExodusII");
    }

  for( unsigned int i = 0; i < num_formats; i++ )
    {
      _output_format.push_back( input("vis-options/output_format", "DIE", i ) );
    }

  // Screen display options
  this->_print_mesh_info            = input("screen-options/print_mesh_info", false );
  this->_print_log_info             = input("screen-options/print_log_info", false );
  this->_solver_quiet               = input("screen-options/solver_quiet", false );
  this->_solver_verbose             = input("screen-options/solver_verbose", false );
  this->_print_equation_system_info = input("screen-options/print_equation_system_info", false );

  return;
}

template< class T >
libMesh::Mesh* GRINS::Solver<T>::get_mesh()
{
  return this->_mesh;
}

template< class T >
void GRINS::Solver<T>::set_mesh( libMesh::Mesh *mesh )
{
  this->_mesh = mesh;
  return;
}

template< class T >
void GRINS::Solver<T>::set_solver_options( libMesh::DiffSolver& solver  )
{
  solver.quiet                       = this->_solver_quiet;
  solver.verbose                     = this->_solver_verbose;
  solver.max_nonlinear_iterations    = this->_max_nonlinear_iterations;
  solver.relative_step_tolerance     = this->_relative_step_tolerance;
  solver.absolute_step_tolerance     = this->_absolute_step_tolerance;
  solver.relative_residual_tolerance = this->_relative_residual_tolerance;
  solver.absolute_residual_tolerance = this->_absolute_residual_tolerance;
  solver.max_linear_iterations       = this->_max_linear_iterations;
  solver.initial_linear_tolerance    = this->_initial_linear_tolerance;

  return;
}

template< class T >
void GRINS::Solver<T>::initialize_system( std::string system_name, GetPot& input )
{
  // Print mesh info if the user wants it
  if( this->_print_mesh_info ) this->_mesh->print_info();

  // Only print log info if the user requests it
  libMesh::perflog.disable_logging();
  if( this->_print_log_info ) libMesh::perflog.enable_logging();

  // Create an equation systems object.
  this->_equation_systems = new libMesh::EquationSystems(*_mesh);

  // Declare the system and its variables.
  libMesh::EquationSystems *es = this->_equation_systems;
  this->_system = &es->add_system<T> (system_name);

  this->_system->read_input_options( input );

  // Solve this as a time-dependent or steady system
  if (this->_transient)
    {
      // Setup time solver
      libMesh::EulerSolver* time_solver = new libMesh::EulerSolver( *(this->_system) );

      this->_system->time_solver = AutoPtr<TimeSolver>(time_solver);

      // Set theta parameter for time-stepping scheme
      time_solver->theta = this->_theta;
    }
  else
    {
      libMesh::SteadySolver* time_solver = new libMesh::SteadySolver( *(this->_system) );
      this->_system->time_solver = AutoPtr<TimeSolver>(time_solver);
      libmesh_assert( this->_n_timesteps == 1 );
    }

  // Initialize the system
  this->_equation_systems->init();

  // Print info if requested
  if( this->_print_equation_system_info ) this->_equation_systems->print_info();

  // Get diff solver to set options
  libMesh::DiffSolver &solver = *(this->_system->time_solver->diff_solver().get());

  // Set linear/nonlinear solver options
  this->set_solver_options( solver );

  this->_system_initialized = true;

  return;
}

template< class T >
void GRINS::Solver<T>::solve()
{
  this->_system->deltat = this->_deltat;

  // Now we begin the timestep loop to compute the time-accurate
  // solution of the equations.
  for (unsigned int t_step=0; t_step < this->_n_timesteps; t_step++)
    {
      // GRVY timers contained in here (if enabled)
      this->_system->solve();

      // Dump out time series visualization if user wants it.
      if( this->_output_vis_time_series )
	{
	  this->output_visualization( t_step );
	}

      // Advance to the next timestep in a transient problem
      this->_system->time_solver->advance_timestep();
    } // End time loop.

  return;
}

template< class T >
void GRINS::Solver<T>::output_visualization()
{
  this->dump_visualization( this->_vis_output_file_prefix, 0 );
  return;
}

template< class T >
void GRINS::Solver<T>::output_visualization( unsigned int time_step )
{
  std::stringstream suffix;

  suffix << time_step;

  std::string filename = this->_vis_output_file_prefix;
  filename+="."+suffix.str();

  this->dump_visualization( filename, time_step );

  return;
}

template< class T >
void GRINS::Solver<T>::dump_visualization( const std::string filename_prefix, const int time_step )
{
  if( this->_vis_output_file_prefix == "unknown" )
    {
      // TODO: Need consisent way to print warning messages.
      std::cout << " WARNING in GRINS::Solver::dump_visualization :" <<
	" using 'unknown' as file prefix since it was not set " <<
	std::endl;
    }

  for( std::vector<std::string>::const_iterator format = _output_format.begin();
       format != _output_format.end();
       format ++ )
    {
      // The following is a modifed copy from the FIN-S code.
      if ((*format) == "tecplot" ||
	  (*format) == "dat")
	{
	  std::string filename = filename_prefix+".dat";
	  libMesh::TecplotIO(*(this->_mesh),false).write_equation_systems(
									  filename,
									  *(this->_equation_systems) );

	  // Left this here as an example from FIN-S if we need to
	  // handle boundary meshes separately in the future.
	  /*
	    if (have_boundary_data)
	    {
	    sprintf( filechar, "%s-surf-%05d.dat",
	    output_name.c_str(),
	    write_soln_number );

	    libMesh::TecplotIO(*boundary_mesh,false).write_equation_systems(
	    std::string(filechar),
	    *_boundary_equation_systems );
	    }
	  */
	}
      else if ((*format) == "tecplot_binary" ||
	       (*format) == "plt")
	{
	  std::string filename = filename_prefix+".plt";
	  libMesh::TecplotIO(*(this->_mesh),true).write_equation_systems(
									 filename,
									 *(this->_equation_systems) );
	}
      else if ((*format) == "gmv")
	{
	  std::string filename = filename_prefix+".gmv";
	  GMVIO(*(this->_mesh)).write_equation_systems(
						       filename,
						       *(this->_equation_systems) );
	}
      else if ((*format) == "vtu")
	{
	  std::string filename = filename_prefix+".vtu";
	  VTKIO(*(this->_mesh)).write_equation_systems(
						       filename,
						       *(this->_equation_systems) );
	}
      else if ((*format) == "ExodusII")
	{
	  std::string filename = filename_prefix+".exo";
	  std::cout << "time_step = " << time_step << ", time = " << this->_system->time << std::endl;
	  ExodusII_IO(*(this->_mesh)).write_timestep(
						     filename,
						     *(this->_equation_systems),
						     time_step+1, // ExodusII wants 1-based counting
						     this->_system->time );
	}
      else if ((*format).find("xda") != std::string::npos ||
	       (*format).find("xdr") != std::string::npos)
	{
	  std::string filename = filename_prefix+"."+(*format);
	  const bool binary = ((*format).find("xdr") != std::string::npos);
	  (this->_equation_systems)->write( filename,
					    binary ? libMeshEnums::ENCODE : libMeshEnums::WRITE,
					    EquationSystems::WRITE_DATA | EquationSystems::WRITE_ADDITIONAL_DATA );
	}
      else
	{
	  // TODO: Do we want to use this to error throughout the code?
	  // TODO: (at least need to pass/print some message/string) - sahni
	  libmesh_error();
	}
    } // End loop over formats

  return;
}

template< class T >
T* GRINS::Solver<T>::get_system()
{
  return this->_system;
}

#ifdef USE_GRVY_TIMERS
template< class T >
void GRINS::Solver<T>::attach_grvy_timer( GRVY::GRVY_Timer_Class* grvy_timer )
{
  _timer = grvy_timer;

  // Attach timer to system
  this->_system->attach_grvy_timer( grvy_timer );

  return;
}
#endif

// Instantiate class
template class GRINS::Solver<GRINS::MultiphysicsSystem>;
<commit_msg>[GRINS]: Using the write API for ExodusII for writing multiple time steps.<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - a low Mach number Navier-Stokes Finite-Element Solver
//
// Copyright (C) 2010,2011 The PECOS Development Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the Version 2 GNU General
// Public License as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA
//
//-----------------------------------------------------------------------el-
//
// $Id$
//
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------

#include "grins_solver.h"

// For instantiating systems
#include "multiphysics_sys.h"

// libMesh I/O classes
#include "gmv_io.h"
#include "tecplot_io.h"
#include "exodusII_io.h"
#include "vtk_io.h"

#include <iostream>

template< class T >
GRINS::Solver<T>::Solver( )
  : _system_initialized(false)
{
  return;
}

template< class T >
GRINS::Solver<T>::~Solver()
{
  // If we initialized the system, we need to destroy the
  // EquationSystems object.
  if(this->_system_initialized)
    {
      delete this->_equation_systems;
    }
  return;
}

template< class T >
void GRINS::Solver<T>::read_input_options( const GetPot& input )
{
  // Linear/Nonlinear solver options
  this->_max_nonlinear_iterations    = input("linear-nonlinear-solver/max_nonlinear_iterations", 10 );
  this->_relative_step_tolerance     = input("linear-nonlinear-solver/relative_step_tolerance", 1.e-6 );
  this->_absolute_step_tolerance     = input("linear-nonlinear-solver/absolute_step_tolerance", 0.0 );
  this->_relative_residual_tolerance = input("linear-nonlinear-solver/relative_residual_tolerance", 1.e-16 );
  this->_absolute_residual_tolerance = input("linear-nonlinear-solver/absolute_residual_tolerance", 0.0 );
  this->_max_linear_iterations       = input("linear-nonlinear-solver/max_linear_iterations", 500 );
  this->_initial_linear_tolerance    = input("linear-nonlinear-solver/initial_linear_tolerance", 1.e-3 );

  // Unsteady solver options
  this->_transient   = input("unsteady-solver/transient", false );
  this->_theta       = input("unsteady-solver/theta", 0.5 );
  this->_n_timesteps = input("unsteady-solver/n_timesteps", 1 );
  this->_deltat      = input("unsteady-solver/deltat", 0.0 ); //TODO: Better default here?

  // Visualization options
  this->_vis_output_file_prefix = input("vis-options/vis_output_file_prefix", "unknown" );
  this->_output_vis_time_series = input("vis-options/output_vis_time_series", false);

  unsigned int num_formats = input.vector_variable_size("vis-options/output_format");

  // If no format specified, default to ExodusII only
  if( num_formats == 0 )
    {
      _output_format.push_back("ExodusII");
    }

  for( unsigned int i = 0; i < num_formats; i++ )
    {
      _output_format.push_back( input("vis-options/output_format", "DIE", i ) );
    }

  // Screen display options
  this->_print_mesh_info            = input("screen-options/print_mesh_info", false );
  this->_print_log_info             = input("screen-options/print_log_info", false );
  this->_solver_quiet               = input("screen-options/solver_quiet", false );
  this->_solver_verbose             = input("screen-options/solver_verbose", false );
  this->_print_equation_system_info = input("screen-options/print_equation_system_info", false );

  return;
}

template< class T >
libMesh::Mesh* GRINS::Solver<T>::get_mesh()
{
  return this->_mesh;
}

template< class T >
void GRINS::Solver<T>::set_mesh( libMesh::Mesh *mesh )
{
  this->_mesh = mesh;
  return;
}

template< class T >
void GRINS::Solver<T>::set_solver_options( libMesh::DiffSolver& solver  )
{
  solver.quiet                       = this->_solver_quiet;
  solver.verbose                     = this->_solver_verbose;
  solver.max_nonlinear_iterations    = this->_max_nonlinear_iterations;
  solver.relative_step_tolerance     = this->_relative_step_tolerance;
  solver.absolute_step_tolerance     = this->_absolute_step_tolerance;
  solver.relative_residual_tolerance = this->_relative_residual_tolerance;
  solver.absolute_residual_tolerance = this->_absolute_residual_tolerance;
  solver.max_linear_iterations       = this->_max_linear_iterations;
  solver.initial_linear_tolerance    = this->_initial_linear_tolerance;

  return;
}

template< class T >
void GRINS::Solver<T>::initialize_system( std::string system_name, GetPot& input )
{
  // Print mesh info if the user wants it
  if( this->_print_mesh_info ) this->_mesh->print_info();

  // Only print log info if the user requests it
  libMesh::perflog.disable_logging();
  if( this->_print_log_info ) libMesh::perflog.enable_logging();

  // Create an equation systems object.
  this->_equation_systems = new libMesh::EquationSystems(*_mesh);

  // Declare the system and its variables.
  libMesh::EquationSystems *es = this->_equation_systems;
  this->_system = &es->add_system<T> (system_name);

  this->_system->read_input_options( input );

  // Solve this as a time-dependent or steady system
  if (this->_transient)
    {
      // Setup time solver
      libMesh::EulerSolver* time_solver = new libMesh::EulerSolver( *(this->_system) );

      this->_system->time_solver = AutoPtr<TimeSolver>(time_solver);

      // Set theta parameter for time-stepping scheme
      time_solver->theta = this->_theta;
    }
  else
    {
      libMesh::SteadySolver* time_solver = new libMesh::SteadySolver( *(this->_system) );
      this->_system->time_solver = AutoPtr<TimeSolver>(time_solver);
      libmesh_assert( this->_n_timesteps == 1 );
    }

  // Initialize the system
  this->_equation_systems->init();

  // Print info if requested
  if( this->_print_equation_system_info ) this->_equation_systems->print_info();

  // Get diff solver to set options
  libMesh::DiffSolver &solver = *(this->_system->time_solver->diff_solver().get());

  // Set linear/nonlinear solver options
  this->set_solver_options( solver );

  this->_system_initialized = true;

  return;
}

template< class T >
void GRINS::Solver<T>::solve()
{
  this->_system->deltat = this->_deltat;

  // Now we begin the timestep loop to compute the time-accurate
  // solution of the equations.
  for (unsigned int t_step=0; t_step < this->_n_timesteps; t_step++)
    {
      // GRVY timers contained in here (if enabled)
      this->_system->solve();

      // Dump out time series visualization if user wants it.
      if( this->_output_vis_time_series )
	{
	  this->output_visualization( t_step );
	}

      // Advance to the next timestep in a transient problem
      this->_system->time_solver->advance_timestep();
    } // End time loop.

  return;
}

template< class T >
void GRINS::Solver<T>::output_visualization()
{
  this->dump_visualization( this->_vis_output_file_prefix, 0 );
  return;
}

template< class T >
void GRINS::Solver<T>::output_visualization( unsigned int time_step )
{
  std::stringstream suffix;

  suffix << time_step;

  std::string filename = this->_vis_output_file_prefix;
  filename+="."+suffix.str();

  this->dump_visualization( filename, time_step );

  return;
}

template< class T >
void GRINS::Solver<T>::dump_visualization( const std::string filename_prefix, const int time_step )
{
  if( this->_vis_output_file_prefix == "unknown" )
    {
      // TODO: Need consisent way to print warning messages.
      std::cout << " WARNING in GRINS::Solver::dump_visualization :" <<
	" using 'unknown' as file prefix since it was not set " <<
	std::endl;
    }

  for( std::vector<std::string>::const_iterator format = _output_format.begin();
       format != _output_format.end();
       format ++ )
    {
      // The following is a modifed copy from the FIN-S code.
      if ((*format) == "tecplot" ||
	  (*format) == "dat")
	{
	  std::string filename = filename_prefix+".dat";
	  libMesh::TecplotIO(*(this->_mesh),false).write_equation_systems(
									  filename,
									  *(this->_equation_systems) );

	  // Left this here as an example from FIN-S if we need to
	  // handle boundary meshes separately in the future.
	  /*
	    if (have_boundary_data)
	    {
	    sprintf( filechar, "%s-surf-%05d.dat",
	    output_name.c_str(),
	    write_soln_number );

	    libMesh::TecplotIO(*boundary_mesh,false).write_equation_systems(
	    std::string(filechar),
	    *_boundary_equation_systems );
	    }
	  */
	}
      else if ((*format) == "tecplot_binary" ||
	       (*format) == "plt")
	{
	  std::string filename = filename_prefix+".plt";
	  libMesh::TecplotIO(*(this->_mesh),true).write_equation_systems(
									 filename,
									 *(this->_equation_systems) );
	}
      else if ((*format) == "gmv")
	{
	  std::string filename = filename_prefix+".gmv";
	  GMVIO(*(this->_mesh)).write_equation_systems(
						       filename,
						       *(this->_equation_systems) );
	}
      else if ((*format) == "vtu")
	{
	  std::string filename = filename_prefix+".vtu";
	  VTKIO(*(this->_mesh)).write_equation_systems(
						       filename,
						       *(this->_equation_systems) );
	}
      else if ((*format) == "ExodusII")
	{
	  std::string filename = filename_prefix+".exo";

	  // The "1" is hardcoded for the number of time steps because the ExodusII manual states that
	  // it should be the number of timesteps within the file. Here, we are explicitly only doing
	  // one timestep per file.
	  ExodusII_IO(*(this->_mesh)).write_timestep(
						     filename,
						     *(this->_equation_systems),
						     1,
						     this->_system->time );
	}
      else if ((*format).find("xda") != std::string::npos ||
	       (*format).find("xdr") != std::string::npos)
	{
	  std::string filename = filename_prefix+"."+(*format);
	  const bool binary = ((*format).find("xdr") != std::string::npos);
	  (this->_equation_systems)->write( filename,
					    binary ? libMeshEnums::ENCODE : libMeshEnums::WRITE,
					    EquationSystems::WRITE_DATA | EquationSystems::WRITE_ADDITIONAL_DATA );
	}
      else
	{
	  // TODO: Do we want to use this to error throughout the code?
	  // TODO: (at least need to pass/print some message/string) - sahni
	  libmesh_error();
	}
    } // End loop over formats

  return;
}

template< class T >
T* GRINS::Solver<T>::get_system()
{
  return this->_system;
}

#ifdef USE_GRVY_TIMERS
template< class T >
void GRINS::Solver<T>::attach_grvy_timer( GRVY::GRVY_Timer_Class* grvy_timer )
{
  _timer = grvy_timer;

  // Attach timer to system
  this->_system->attach_grvy_timer( grvy_timer );

  return;
}
#endif

// Instantiate class
template class GRINS::Solver<GRINS::MultiphysicsSystem>;
<|endoftext|>


<commit_before>/*!
    \file pipe.cpp
    \brief Pipe implementation
    \author Ivan Shynkarenka
    \date 30.11.2016
    \copyright MIT License
*/

#include "system/pipe.h"

#include "errors/fatal.h"

#include <cassert>

#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif

namespace CppCommon {

//! @cond INTERNALS

class Pipe::Impl
{
public:
    Impl()
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = pipe(_pipe);
        if (result != 0)
            throwex SystemException("Failed to create a new pipe!");
#elif defined(_WIN32) || defined(_WIN64)
        if (!CreatePipe(&_pipe[0], &_pipe[1], nullptr, 0))
            throwex SystemException("Failed to create a new pipe!");
#endif
    }

    ~Impl()
    {
        try
        {
            if (IsPipeOpened())
                Close();
        }
        catch (const SystemException& ex)
        {
            fatality(SystemException(ex.string()));
        }
    }

    void* reader() const noexcept
    {
        return (void*)(size_t)_pipe[0];
    }

    void* writer() const noexcept
    {
        return (void*)(size_t)_pipe[1];
    }

    bool IsPipeOpened() const noexcept
    {
        return IsPipeReadOpened() || IsPipeWriteOpened();
    }

    bool IsPipeReadOpened() const noexcept
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        return (_pipe[0] >= 0);
#elif defined(_WIN32) || defined(_WIN64)
        return (_pipe[0] != INVALID_HANDLE_VALUE);
#endif
    }

    bool IsPipeWriteOpened() const noexcept
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        return (_pipe[1] >= 0);
#elif defined(_WIN32) || defined(_WIN64)
        return (_pipe[1] != INVALID_HANDLE_VALUE);
#endif
    }

    size_t Read(void* buffer, size_t size)
    {
        if ((buffer == nullptr) || (size == 0))
            return 0;

        assert(IsPipeReadOpened() && "Pipe is not opened for reading!");
        if (!IsPipeReadOpened())
            throwex SystemException("Cannot read from the closed pipe!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        ssize_t result = read(_pipe[0], buffer, size);
        if (result < 0)
            throwex SystemException("Cannot read from the pipe!");
        return (size_t)result;
#elif defined(_WIN32) || defined(_WIN64)
        DWORD result;
        if (!ReadFile(_pipe[0], buffer, (DWORD)size, &result, nullptr))
            throwex SystemException("Cannot read from the pipe!");
        return (size_t)result;
#endif
    }

    size_t Write(const void* buffer, size_t size)
    {
        if ((buffer == nullptr) || (size == 0))
            return 0;

        assert(IsPipeWriteOpened() && "Pipe is not opened for writing!");
        if (!IsPipeWriteOpened())
            throwex SystemException("Cannot write into the closed pipe!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        ssize_t result = write(_pipe[1], buffer, size);
        if (result < 0)
            throwex SystemException("Cannot write into the pipe!");
        return (size_t)result;
#elif defined(_WIN32) || defined(_WIN64)
        DWORD result;
        if (!WriteFile(_pipe[1], buffer, (DWORD)size, &result, nullptr))
            throwex SystemException("Cannot write into the pipe!");
        return (size_t)result;
#endif
    }

    void CloseRead()
    {
        assert(IsPipeReadOpened() && "Pipe is not opened for reading!");
        if (!IsPipeReadOpened())
            throwex SystemException("Pipe is not opened for reading!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = close(_pipe[0]);
        if (result != 0)
            throwex SystemException("Cannot close the read pipe endpoint!");
        _pipe[0] = -1;
#elif defined(_WIN32) || defined(_WIN64)
        if (!CloseHandle(_pipe[0]))
            throwex SystemException("Cannot close the read pipe endpoint!");
        _pipe[0] = INVALID_HANDLE_VALUE;
#endif
    }

    void CloseWrite()
    {
        assert(IsPipeWriteOpened() && "Pipe is not opened for writing!");
        if (!IsPipeWriteOpened())
            throwex SystemException("Pipe is not opened for writing!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = close(_pipe[1]);
        if (result != 0)
            throwex SystemException("Cannot close the write pipe endpoint!");
        _pipe[1] = -1;
#elif defined(_WIN32) || defined(_WIN64)
        if (!CloseHandle(_pipe[1]))
            throwex SystemException("Cannot close the write pipe endpoint!");
        _pipe[1] = INVALID_HANDLE_VALUE;
#endif
    }

    void Close()
    {
        if (IsPipeReadOpened())
            CloseRead();
        if (IsPipeWriteOpened())
            CloseWrite();
    }

private:
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    int _pipe[2];
#elif defined(_WIN32) || defined(_WIN64)
    HANDLE _pipe[2];
#endif
};

//! @endcond

Pipe::Pipe() : _pimpl(std::make_unique<Impl>())
{
}

Pipe::~Pipe()
{
}

void* Pipe::reader() const noexcept
{
    return _pimpl->reader();
}

void* Pipe::writer() const noexcept
{
    return _pimpl->writer();
}

bool Pipe::IsPipeOpened() const noexcept
{
    return _pimpl->IsPipeOpened();
}

bool Pipe::IsPipeReadOpened() const noexcept
{
    return _pimpl->IsPipeReadOpened();
}

bool Pipe::IsPipeWriteOpened() const noexcept
{
    return _pimpl->IsPipeWriteOpened();
}

size_t Pipe::Read(void* buffer, size_t size)
{
    return _pimpl->Read(buffer, size);
}

size_t Pipe::Write(const void* buffer, size_t size)
{
    return _pimpl->Write(buffer, size);
}

void Pipe::CloseRead()
{
    return _pimpl->CloseRead();
}

void Pipe::CloseWrite()
{
    return _pimpl->CloseWrite();
}

void Pipe::Close()
{
    return _pimpl->Close();
}

} // namespace CppCommon
<commit_msg>Fixed pipe IO operations<commit_after>/*!
    \file pipe.cpp
    \brief Pipe implementation
    \author Ivan Shynkarenka
    \date 30.11.2016
    \copyright MIT License
*/

#include "system/pipe.h"

#include "errors/fatal.h"

#include <cassert>

#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
#include <unistd.h>
#elif defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif

namespace CppCommon {

//! @cond INTERNALS

class Pipe::Impl
{
public:
    Impl()
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = pipe(_pipe);
        if (result != 0)
            throwex SystemException("Failed to create a new pipe!");
#elif defined(_WIN32) || defined(_WIN64)
        if (!CreatePipe(&_pipe[0], &_pipe[1], nullptr, 0))
            throwex SystemException("Failed to create a new pipe!");
#endif
    }

    ~Impl()
    {
        try
        {
            if (IsPipeOpened())
                Close();
        }
        catch (const SystemException& ex)
        {
            fatality(SystemException(ex.string()));
        }
    }

    void* reader() const noexcept
    {
        return (void*)(size_t)_pipe[0];
    }

    void* writer() const noexcept
    {
        return (void*)(size_t)_pipe[1];
    }

    bool IsPipeOpened() const noexcept
    {
        return IsPipeReadOpened() || IsPipeWriteOpened();
    }

    bool IsPipeReadOpened() const noexcept
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        return (_pipe[0] >= 0);
#elif defined(_WIN32) || defined(_WIN64)
        return (_pipe[0] != INVALID_HANDLE_VALUE);
#endif
    }

    bool IsPipeWriteOpened() const noexcept
    {
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        return (_pipe[1] >= 0);
#elif defined(_WIN32) || defined(_WIN64)
        return (_pipe[1] != INVALID_HANDLE_VALUE);
#endif
    }

    size_t Read(void* buffer, size_t size)
    {
        if ((buffer == nullptr) || (size == 0))
            return 0;

        assert(IsPipeReadOpened() && "Pipe is not opened for reading!");
        if (!IsPipeReadOpened())
            throwex SystemException("Cannot read from the closed pipe!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        ssize_t result = read(_pipe[0], buffer, size);
        if (result < 0)
            throwex SystemException("Cannot read from the pipe!");
        return (size_t)result;
#elif defined(_WIN32) || defined(_WIN64)
        DWORD result = 0;
        if (!ReadFile(_pipe[0], buffer, (DWORD)size, &result, nullptr))
            if (GetLastError() != ERROR_BROKEN_PIPE)
                throwex SystemException("Cannot read from the pipe!");
        return (size_t)result;
#endif
    }

    size_t Write(const void* buffer, size_t size)
    {
        if ((buffer == nullptr) || (size == 0))
            return 0;

        assert(IsPipeWriteOpened() && "Pipe is not opened for writing!");
        if (!IsPipeWriteOpened())
            throwex SystemException("Cannot write into the closed pipe!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        ssize_t result = write(_pipe[1], buffer, size);
        if (result < 0)
            throwex SystemException("Cannot write into the pipe!");
        return (size_t)result;
#elif defined(_WIN32) || defined(_WIN64)
        DWORD result = 0;
        if (!WriteFile(_pipe[1], buffer, (DWORD)size, &result, nullptr))
            if (GetLastError() != ERROR_BROKEN_PIPE)
                throwex SystemException("Cannot write into the pipe!");
        return (size_t)result;
#endif
    }

    void CloseRead()
    {
        assert(IsPipeReadOpened() && "Pipe is not opened for reading!");
        if (!IsPipeReadOpened())
            throwex SystemException("Pipe is not opened for reading!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = close(_pipe[0]);
        if (result != 0)
            throwex SystemException("Cannot close the read pipe endpoint!");
        _pipe[0] = -1;
#elif defined(_WIN32) || defined(_WIN64)
        if (!CloseHandle(_pipe[0]))
            throwex SystemException("Cannot close the read pipe endpoint!");
        _pipe[0] = INVALID_HANDLE_VALUE;
#endif
    }

    void CloseWrite()
    {
        assert(IsPipeWriteOpened() && "Pipe is not opened for writing!");
        if (!IsPipeWriteOpened())
            throwex SystemException("Pipe is not opened for writing!");
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
        int result = close(_pipe[1]);
        if (result != 0)
            throwex SystemException("Cannot close the write pipe endpoint!");
        _pipe[1] = -1;
#elif defined(_WIN32) || defined(_WIN64)
        if (!CloseHandle(_pipe[1]))
            throwex SystemException("Cannot close the write pipe endpoint!");
        _pipe[1] = INVALID_HANDLE_VALUE;
#endif
    }

    void Close()
    {
        if (IsPipeReadOpened())
            CloseRead();
        if (IsPipeWriteOpened())
            CloseWrite();
    }

private:
#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
    int _pipe[2];
#elif defined(_WIN32) || defined(_WIN64)
    HANDLE _pipe[2];
#endif
};

//! @endcond

Pipe::Pipe() : _pimpl(std::make_unique<Impl>())
{
}

Pipe::~Pipe()
{
}

void* Pipe::reader() const noexcept
{
    return _pimpl->reader();
}

void* Pipe::writer() const noexcept
{
    return _pimpl->writer();
}

bool Pipe::IsPipeOpened() const noexcept
{
    return _pimpl->IsPipeOpened();
}

bool Pipe::IsPipeReadOpened() const noexcept
{
    return _pimpl->IsPipeReadOpened();
}

bool Pipe::IsPipeWriteOpened() const noexcept
{
    return _pimpl->IsPipeWriteOpened();
}

size_t Pipe::Read(void* buffer, size_t size)
{
    return _pimpl->Read(buffer, size);
}

size_t Pipe::Write(const void* buffer, size_t size)
{
    return _pimpl->Write(buffer, size);
}

void Pipe::CloseRead()
{
    return _pimpl->CloseRead();
}

void Pipe::CloseWrite()
{
    return _pimpl->CloseWrite();
}

void Pipe::Close()
{
    return _pimpl->Close();
}

} // namespace CppCommon
<|endoftext|>


<commit_before>#ifndef MJOLNIR_BOUNDARY_CONDITION
#define MJOLNIR_BOUNDARY_CONDITION
#include <cassert>

namespace mjolnir
{

template<typename traitsT>
struct UnlimitedBoundary
{
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

    UnlimitedBoundary() = default;
    ~UnlimitedBoundary() = default;

    coordinate_type adjust_direction(coordinate_type dr) const {return dr;}
    coordinate_type adjust_position (coordinate_type r ) const {return r;}
};

template<typename traitsT>
struct CubicPeriodicBoundary
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::real_type real_type;
    typedef typename traits_type::coordinate_type coordinate_type;

  public:
    CubicPeriodicBoundary() = default;
    ~CubicPeriodicBoundary() = default;
    CubicPeriodicBoundary(const coordinate_type& lw, const coordinate_type& up)
    : lower(lw), upper(up), system_size(up-lw), system_size_half(0.5*(up-lw))
    {}

    coordinate_type adjust_direction(coordinate_type dr) const
    {
             if(dr[0] < -system_size_half[0]) dr[0] += system_size[0];
        else if(dr[0] >  system_size_half[0]) dr[0] -= system_size[0];
             if(dr[1] < -system_size_half[1]) dr[1] += system_size[1];
        else if(dr[1] >  system_size_half[1]) dr[1] -= system_size[1];
             if(dr[2] < -system_size_half[2]) dr[2] += system_size[2];
        else if(dr[2] >  system_size_half[2]) dr[2] -= system_size[2];
        return dr;
    }

    coordinate_type adjust_position(coordinate_type pos) const
    {
             if(pos[0] < lower[0]) pos[0] += system_size_[0];
        else if(pos[0] > upper[0]) pos[0] -= system_size_[0];
             if(pos[1] < lower[1]) pos[1] += system_size_[1];
        else if(pos[1] > upper[1]) pos[1] -= system_size_[1];
             if(pos[2] < lower[2]) pos[2] += system_size_[2];
        else if(pos[2] > upper[2]) pos[2] -= system_size_[2];
        return pos;
    }

    coordinate_type&       lower_bound()       {return lower;}
    coordinate_type const& lower_bound() const {return lower;}
    coordinate_type&       upper_bound()       {return upper;}
    coordinate_type const& upper_bound() const {return upper;}
    coordinate_type const& range() const {return system_size;}

  private:

    coordinate_type lower;
    coordinate_type upper;
    coordinate_type system_size;
    coordinate_type system_size_half;
};

}//mjolnir
#endif /* MJOLNIR_BOUNDARY_CONDITION */
<commit_msg>change template argument for BoundaryCondition<commit_after>#ifndef MJOLNIR_BOUNDARY_CONDITION
#define MJOLNIR_BOUNDARY_CONDITION
#include <cassert>

namespace mjolnir
{

template<typename realT, typename coordT>
struct UnlimitedBoundary
{
    typedef realT  real_type;
    typedef coordT coordiante_type;

    UnlimitedBoundary() = default;
    ~UnlimitedBoundary() = default;

    coordinate_type adjust_direction(coordinate_type dr) const {return dr;}
    coordinate_type adjust_position (coordinate_type r ) const {return r;}
};

template<typename realT, typename coordT>
struct CubicPeriodicBoundary
{
  public:
    typedef realT  real_type;
    typedef coordT coordiante_type;

  public:
    CubicPeriodicBoundary() = default;
    ~CubicPeriodicBoundary() = default;
    CubicPeriodicBoundary(const coordinate_type& lw, const coordinate_type& up)
    : lower(lw), upper(up), system_size(up-lw), system_size_half(0.5*(up-lw))
    {}

    coordinate_type adjust_direction(coordinate_type dr) const
    {
             if(dr[0] < -system_size_half[0]) dr[0] += system_size[0];
        else if(dr[0] >  system_size_half[0]) dr[0] -= system_size[0];
             if(dr[1] < -system_size_half[1]) dr[1] += system_size[1];
        else if(dr[1] >  system_size_half[1]) dr[1] -= system_size[1];
             if(dr[2] < -system_size_half[2]) dr[2] += system_size[2];
        else if(dr[2] >  system_size_half[2]) dr[2] -= system_size[2];
        return dr;
    }

    coordinate_type adjust_position(coordinate_type pos) const
    {
             if(pos[0] < lower[0]) pos[0] += system_size_[0];
        else if(pos[0] > upper[0]) pos[0] -= system_size_[0];
             if(pos[1] < lower[1]) pos[1] += system_size_[1];
        else if(pos[1] > upper[1]) pos[1] -= system_size_[1];
             if(pos[2] < lower[2]) pos[2] += system_size_[2];
        else if(pos[2] > upper[2]) pos[2] -= system_size_[2];
        return pos;
    }

    coordinate_type&       lower_bound()       {return lower;}
    coordinate_type const& lower_bound() const {return lower;}
    coordinate_type&       upper_bound()       {return upper;}
    coordinate_type const& upper_bound() const {return upper;}
    coordinate_type const& range() const {return system_size;}

  private:

    coordinate_type lower;
    coordinate_type upper;
    coordinate_type system_size;
    coordinate_type system_size_half;
};

}//mjolnir
#endif /* MJOLNIR_BOUNDARY_CONDITION */
<|endoftext|>


<commit_before>/** Copyright (C) 2017 Tim Kuipers - Released under terms of the AGPLv3 License */
#include "SpaceFillingTreeFill.h"

#include "../utils/linearAlg2D.h" // rotateAround

namespace cura {

SpaceFillingTreeFill::SpaceFillingTreeFill(coord_t line_distance, AABB3D model_aabb) : SpaceFillingTreeFill(line_distance, model_aabb, getTreeParams(line_distance, model_aabb))
{
}

SpaceFillingTreeFill::SpaceFillingTreeFill(coord_t line_distance, AABB3D model_aabb, SpaceFillingTreeFill::TreeParams tree_params)
: model_aabb(model_aabb)
, line_distance(line_distance)
, tree(tree_params.middle, tree_params.radius, tree_params.depth)
{
}

void SpaceFillingTreeFill::generate(const Polygons& outlines, coord_t shift, bool zig_zaggify, double fill_angle, bool alternate, bool use_odd_in_junctions, bool use_odd_out_junctions, coord_t pocket_size, Polygons& result_polygons, Polygons& result_lines) const
{
    Point3 model_middle = model_aabb.getMiddle();
    Point3Matrix transformation = LinearAlg2D::rotateAround(Point(model_middle.x, model_middle.y), fill_angle + 45);

    Polygon infill_poly;
    if (alternate)
    {
        std::vector<const SpaceFillingTree::Node*> nodes;
        generateTreePathAndDepths(nodes);
        offsetTreePathAlternating(nodes, shift, pocket_size, use_odd_in_junctions, use_odd_out_junctions, infill_poly);
    }
    else
    {
        Polygon tree_path;
        generateTreePath(tree_path);
        offsetTreePath(tree_path, shift, pocket_size, infill_poly);
    }
    infill_poly.applyMatrix(transformation); // apply rotation

    if (zig_zaggify)
    {
        Polygons infill_pattern;
        infill_pattern.add(infill_poly);
        result_polygons = infill_pattern.intersection(outlines);
    }
    else
    {
        if (infill_poly.size() > 0)
        {
            infill_poly.add(infill_poly[0]);
        }
        Polygons infill_pattern;
        infill_pattern.add(infill_poly);
        Polygons poly_lines = outlines.intersectionPolyLines(infill_pattern);
        for (PolygonRef poly_line : poly_lines)
        {
            for (unsigned int point_idx = 1; point_idx < poly_line.size(); point_idx++)
            {
                result_lines.addLine(poly_line[point_idx - 1], poly_line[point_idx]);
            }
        }
    }
}

SpaceFillingTreeFill::TreeParams SpaceFillingTreeFill::getTreeParams(coord_t line_distance, AABB3D model_aabb)
{
    TreeParams ret;
    AABB aabb(Point(model_aabb.min.x, model_aabb.min.y), Point(model_aabb.max.x, model_aabb.max.y));
    const Point aabb_size = aabb.max - aabb.min;
    coord_t minimal_radius = std::max(aabb_size.X, aabb_size.Y) / 2;
    minimal_radius *= sqrt(2.0); // to account for any possible infill angle

    /*
     * For the normal cross infill the width of the cross is equal to the width of the crosses which are left out.
     *
     *  ▉  ▄    white cross left out          r/2
     * ▀▉▀▀▉▀ ↙       by the black          ^^^^^^^^
     * ▄▉▄   ▄▉▄                     +--------------+
     *  ▉  ▄  ▉                      :              |
     * ▀▉▀▀▉▀▀▉▀▀                    :  -------+    |   quarter of the fractal with depth one
     *                               :,'       |    | the fractal with depth one
     *                               :    .'   |    |
     *                            .  :  .' :   |    |
     *                          .    :.'...:.,'.....+
     *                        .            :
     *                         '-.       . :   :
     *                          l  '-, .   vvvvv
     *                                       n
     * minimum offset = 0
     * maximum offset is such that n = r /2
     * 2n^2 = l/2  ==>  n = 1/4 sqrt(2) l
     * r = .5 sqrt(2) l
     */
    ret.middle = aabb.getMiddle();
    ret.depth = -1;
    ret.radius = line_distance / 2 * sqrt(2.0);
    while (ret.radius <= minimal_radius)
    {
        ret.depth++;
        ret.radius *= 2;
    }
    return ret;
}

void SpaceFillingTreeFill::generateTreePath(PolygonRef path) const
{
    class Visitor : public SpaceFillingTree::LocationVisitor
    {
        PolygonRef path;
        bool is_first_point = true;
    public:
        Visitor(PolygonRef path)
        : path(path)
        {}
        void visit(const SpaceFillingTree::Node* node)
        {
            if (is_first_point)
            { // skip first point because it is the same as the last
                is_first_point = false;
                return;
            }
            path.add(node->middle);
        }
    };
    Visitor visitor(path);
    tree.walk(visitor);
}

void SpaceFillingTreeFill::offsetTreePath(const ConstPolygonRef path, coord_t offset, coord_t pocket_size, PolygonRef infill) const
{
    coord_t corner_bevel = std::max(coord_t(0), pocket_size / 2 - offset) * 1.4142;
    coord_t point_bevel = std::max(coord_t(0), pocket_size / 2 - (line_distance - offset)) * 1.4142;
    for (unsigned int point_idx = 0; point_idx < path.size(); point_idx++)
    {
        const Point a = path[point_idx];
        const Point b = path[(point_idx + 1) % path.size()];
        const Point c = path[(point_idx + 2) % path.size()];

        const Point bc = c - b;
        const Point bc_T = turn90CCW(bc);
        const Point bc_offset = normal(bc_T, offset);

        if (a == c)
        { // pointy case
            const Point left_point = b - bc_offset;
            const Point pointy_point = b - normal(bc, offset);
            const Point right_point = b + bc_offset;
            infill.add(left_point);
            if (point_bevel)
            {
                infill.add(pointy_point - normal(pointy_point - left_point, point_bevel));
                infill.add(pointy_point - normal(pointy_point - right_point, point_bevel));
            }
            else
            {
                infill.add(pointy_point);
            }
            infill.add(right_point);
        }
        else
        {
            const Point ab = b - a;
            const Point ab_T = turn90CCW(ab);
            const Point normal_corner = b + normal(ab_T, offset) + bc_offset; // WARNING: offset is not based on the directions of the two segments, rather than assuming 90 degree corners
            if (corner_bevel)
            {
                infill.add(normal_corner - normal(ab, corner_bevel));
                infill.add(normal_corner + normal(bc, corner_bevel));
            }
            else
            {
                infill.add(normal_corner);
            }
        }
    }
}

void SpaceFillingTreeFill::generateTreePathAndDepths(std::vector<const SpaceFillingTree::Node*>& nodes) const
{
    class Visitor : public SpaceFillingTree::LocationVisitor
    {
        std::vector<const SpaceFillingTree::Node*>& nodes;
        bool is_first_point = true;
    public:
        Visitor(std::vector<const SpaceFillingTree::Node*>& nodes)
        : nodes(nodes)
        {}
        void visit(const SpaceFillingTree::Node* node)
        {
            if (is_first_point)
            { // skip first point because it is the same as the last
                is_first_point = false;
                return;
            }
            nodes.push_back(node);
        }
    };
    Visitor visitor(nodes);
    tree.walk(visitor);
}

void SpaceFillingTreeFill::offsetTreePathAlternating(std::vector<const SpaceFillingTree::Node*>& nodes, coord_t offset, coord_t pocket_size, bool use_odd_in_junctions, bool use_odd_out_junctions, PolygonRef infill) const
{
    coord_t corner_bevel_even = std::max(coord_t(0), pocket_size / 2 - offset) * std::sqrt(2);
    coord_t corner_bevel_odd = 0;
    if (use_odd_in_junctions)
    {
        std::swap(corner_bevel_even, corner_bevel_odd);
    }
    coord_t point_bevel_even = std::max(coord_t(0), pocket_size / 2 - (line_distance - offset)) * std::sqrt(2);
    coord_t point_bevel_odd = 0;
    if (use_odd_out_junctions)
    {
        std::swap(point_bevel_even, point_bevel_odd);
    }
    for (unsigned int point_idx = 0; point_idx < nodes.size(); point_idx++)
    {
        const SpaceFillingTree::Node* a_node = nodes[point_idx];
        const SpaceFillingTree::Node* b_node = nodes[(point_idx + 1) % nodes.size()];
        const SpaceFillingTree::Node* c_node = nodes[(point_idx + 2) % nodes.size()];

        const Point a = a_node->middle;
        const Point b = b_node->middle;
        const Point c = c_node->middle;

        coord_t bc_offset_length = offset;

        Point bc = c - b;
        Point bc_T = turn90CCW(bc);
        Point bc_offset = normal(bc_T, bc_offset_length);

        if (a == c)
        { // pointy case
            coord_t point_offset_length = offset;
            const Point left_point = b - bc_offset - normal(bc, std::max(coord_t(0), point_offset_length - bc_offset_length));
            const Point pointy_point = b - normal(bc, point_offset_length);
            const Point right_point = b + bc_offset - normal(bc, std::max(coord_t(0), point_offset_length - bc_offset_length));

            infill.add(left_point);
            const coord_t point_bevel_here = (b_node->parent && b_node->parent_to_here_direction == b_node->parent->parent_to_here_direction)? point_bevel_even : point_bevel_odd;
            if (point_bevel_here)
            {
                infill.add(pointy_point - normal(pointy_point - left_point, point_bevel_here));
                infill.add(pointy_point - normal(pointy_point - right_point, point_bevel_here));
            }
            else
            {
                infill.add(pointy_point);
            }
            infill.add(right_point);
        }
        else
        {
            coord_t ab_offset_length = offset;
            Point ab = b - a;
            Point ab_T = turn90CCW(ab);
            const Point normal_corner = b + normal(ab_T, ab_offset_length) + bc_offset; // WARNING: offset is not based on the directions of the two segments, rather than assuming 90 degree corners
            const coord_t corner_bevel_here = (a_node->distance_depth % 2 == 1)? corner_bevel_even : corner_bevel_odd;
            if (corner_bevel_here)
            {
                infill.add(normal_corner - normal(ab, corner_bevel_here));
                infill.add(normal_corner + normal(bc, corner_bevel_here));
            }
            else
            {
                infill.add(normal_corner);
            }
        }
    }
}

}; // namespace cura
<commit_msg>Attempt at improving documentation and ASCII art<commit_after>/** Copyright (C) 2017 Tim Kuipers - Released under terms of the AGPLv3 License */
#include "SpaceFillingTreeFill.h"

#include "../utils/linearAlg2D.h" // rotateAround

namespace cura {

SpaceFillingTreeFill::SpaceFillingTreeFill(coord_t line_distance, AABB3D model_aabb) : SpaceFillingTreeFill(line_distance, model_aabb, getTreeParams(line_distance, model_aabb))
{
}

SpaceFillingTreeFill::SpaceFillingTreeFill(coord_t line_distance, AABB3D model_aabb, SpaceFillingTreeFill::TreeParams tree_params)
: model_aabb(model_aabb)
, line_distance(line_distance)
, tree(tree_params.middle, tree_params.radius, tree_params.depth)
{
}

void SpaceFillingTreeFill::generate(const Polygons& outlines, coord_t shift, bool zig_zaggify, double fill_angle, bool alternate, bool use_odd_in_junctions, bool use_odd_out_junctions, coord_t pocket_size, Polygons& result_polygons, Polygons& result_lines) const
{
    Point3 model_middle = model_aabb.getMiddle();
    Point3Matrix transformation = LinearAlg2D::rotateAround(Point(model_middle.x, model_middle.y), fill_angle + 45);

    Polygon infill_poly;
    if (alternate)
    {
        std::vector<const SpaceFillingTree::Node*> nodes;
        generateTreePathAndDepths(nodes);
        offsetTreePathAlternating(nodes, shift, pocket_size, use_odd_in_junctions, use_odd_out_junctions, infill_poly);
    }
    else
    {
        Polygon tree_path;
        generateTreePath(tree_path);
        offsetTreePath(tree_path, shift, pocket_size, infill_poly);
    }
    infill_poly.applyMatrix(transformation); // apply rotation

    if (zig_zaggify)
    {
        Polygons infill_pattern;
        infill_pattern.add(infill_poly);
        result_polygons = infill_pattern.intersection(outlines);
    }
    else
    {
        if (infill_poly.size() > 0)
        {
            infill_poly.add(infill_poly[0]);
        }
        Polygons infill_pattern;
        infill_pattern.add(infill_poly);
        Polygons poly_lines = outlines.intersectionPolyLines(infill_pattern);
        for (PolygonRef poly_line : poly_lines)
        {
            for (unsigned int point_idx = 1; point_idx < poly_line.size(); point_idx++)
            {
                result_lines.addLine(poly_line[point_idx - 1], poly_line[point_idx]);
            }
        }
    }
}

SpaceFillingTreeFill::TreeParams SpaceFillingTreeFill::getTreeParams(coord_t line_distance, AABB3D model_aabb)
{
    TreeParams ret;
    AABB aabb(Point(model_aabb.min.x, model_aabb.min.y), Point(model_aabb.max.x, model_aabb.max.y));
    const Point aabb_size = aabb.max - aabb.min;
    coord_t minimal_radius = std::max(aabb_size.X, aabb_size.Y) / 2;
    minimal_radius *= sqrt(2.0); // to account for any possible infill angle

    /*
     * For the normal cross infill the width of the cross is equal to the width of the crosses which are left out.
     *
     *  ▉  ▄    white cross left out          r/2
     * ▀▉▀▀▉▀ ↙       by the black          ^^^^^^^^
     * ▄▉▄   ▄▉▄                     +--------------+
     *  ▉  ▄  ▉                      :              |
     * ▀▉▀▀▉▀▀▉▀▀                    : --------+    |   quarter of the fractal with depth one
     *                               :,.       |    | the fractal with depth one
     *                              .:  '-.    |    |
     *                            .' :  .' :'. |    |
     *                          .'   :.'...:.,'.....+
     *                        .'           :
     *                         '-.       .':   :
     *                          L  '-, .'  vvvvv
     *                                       n
     * minimum offset = 0
     * maximum offset is such that n = r/2
     * 2n^2 = L/2  ==>  n = 1/4 sqrt(2) L
     * r = .5 sqrt(2) L
     */
    ret.middle = aabb.getMiddle();
    ret.depth = -1;
    ret.radius = line_distance / 2 * sqrt(2.0);
    while (ret.radius <= minimal_radius)
    {
        ret.depth++;
        ret.radius *= 2;
    }
    return ret;
}

void SpaceFillingTreeFill::generateTreePath(PolygonRef path) const
{
    class Visitor : public SpaceFillingTree::LocationVisitor
    {
        PolygonRef path;
        bool is_first_point = true;
    public:
        Visitor(PolygonRef path)
        : path(path)
        {}
        void visit(const SpaceFillingTree::Node* node)
        {
            if (is_first_point)
            { // skip first point because it is the same as the last
                is_first_point = false;
                return;
            }
            path.add(node->middle);
        }
    };
    Visitor visitor(path);
    tree.walk(visitor);
}

void SpaceFillingTreeFill::offsetTreePath(const ConstPolygonRef path, coord_t offset, coord_t pocket_size, PolygonRef infill) const
{
    coord_t corner_bevel = std::max(coord_t(0), pocket_size / 2 - offset) * 1.4142;
    coord_t point_bevel = std::max(coord_t(0), pocket_size / 2 - (line_distance - offset)) * 1.4142;
    for (unsigned int point_idx = 0; point_idx < path.size(); point_idx++)
    {
        const Point a = path[point_idx];
        const Point b = path[(point_idx + 1) % path.size()];
        const Point c = path[(point_idx + 2) % path.size()];

        const Point bc = c - b;
        const Point bc_T = turn90CCW(bc);
        const Point bc_offset = normal(bc_T, offset);

        if (a == c)
        { // pointy case
            const Point left_point = b - bc_offset;
            const Point pointy_point = b - normal(bc, offset);
            const Point right_point = b + bc_offset;
            infill.add(left_point);
            if (point_bevel)
            {
                infill.add(pointy_point - normal(pointy_point - left_point, point_bevel));
                infill.add(pointy_point - normal(pointy_point - right_point, point_bevel));
            }
            else
            {
                infill.add(pointy_point);
            }
            infill.add(right_point);
        }
        else
        {
            const Point ab = b - a;
            const Point ab_T = turn90CCW(ab);
            const Point normal_corner = b + normal(ab_T, offset) + bc_offset; // WARNING: offset is not based on the directions of the two segments, rather than assuming 90 degree corners
            if (corner_bevel)
            {
                infill.add(normal_corner - normal(ab, corner_bevel));
                infill.add(normal_corner + normal(bc, corner_bevel));
            }
            else
            {
                infill.add(normal_corner);
            }
        }
    }
}

void SpaceFillingTreeFill::generateTreePathAndDepths(std::vector<const SpaceFillingTree::Node*>& nodes) const
{
    class Visitor : public SpaceFillingTree::LocationVisitor
    {
        std::vector<const SpaceFillingTree::Node*>& nodes;
        bool is_first_point = true;
    public:
        Visitor(std::vector<const SpaceFillingTree::Node*>& nodes)
        : nodes(nodes)
        {}
        void visit(const SpaceFillingTree::Node* node)
        {
            if (is_first_point)
            { // skip first point because it is the same as the last
                is_first_point = false;
                return;
            }
            nodes.push_back(node);
        }
    };
    Visitor visitor(nodes);
    tree.walk(visitor);
}

void SpaceFillingTreeFill::offsetTreePathAlternating(std::vector<const SpaceFillingTree::Node*>& nodes, coord_t offset, coord_t pocket_size, bool use_odd_in_junctions, bool use_odd_out_junctions, PolygonRef infill) const
{
    coord_t corner_bevel_even = std::max(coord_t(0), pocket_size / 2 - offset) * std::sqrt(2);
    coord_t corner_bevel_odd = 0;
    if (use_odd_in_junctions)
    {
        std::swap(corner_bevel_even, corner_bevel_odd);
    }
    coord_t point_bevel_even = std::max(coord_t(0), pocket_size / 2 - (line_distance - offset)) * std::sqrt(2);
    coord_t point_bevel_odd = 0;
    if (use_odd_out_junctions)
    {
        std::swap(point_bevel_even, point_bevel_odd);
    }
    for (unsigned int point_idx = 0; point_idx < nodes.size(); point_idx++)
    {
        const SpaceFillingTree::Node* a_node = nodes[point_idx];
        const SpaceFillingTree::Node* b_node = nodes[(point_idx + 1) % nodes.size()];
        const SpaceFillingTree::Node* c_node = nodes[(point_idx + 2) % nodes.size()];

        const Point a = a_node->middle;
        const Point b = b_node->middle;
        const Point c = c_node->middle;

        coord_t bc_offset_length = offset;

        Point bc = c - b;
        Point bc_T = turn90CCW(bc);
        Point bc_offset = normal(bc_T, bc_offset_length);

        if (a == c)
        { // pointy case
            coord_t point_offset_length = offset;
            const Point left_point = b - bc_offset - normal(bc, std::max(coord_t(0), point_offset_length - bc_offset_length));
            const Point pointy_point = b - normal(bc, point_offset_length);
            const Point right_point = b + bc_offset - normal(bc, std::max(coord_t(0), point_offset_length - bc_offset_length));

            infill.add(left_point);
            const coord_t point_bevel_here = (b_node->parent && b_node->parent_to_here_direction == b_node->parent->parent_to_here_direction)? point_bevel_even : point_bevel_odd;
            if (point_bevel_here)
            {
                infill.add(pointy_point - normal(pointy_point - left_point, point_bevel_here));
                infill.add(pointy_point - normal(pointy_point - right_point, point_bevel_here));
            }
            else
            {
                infill.add(pointy_point);
            }
            infill.add(right_point);
        }
        else
        {
            coord_t ab_offset_length = offset;
            Point ab = b - a;
            Point ab_T = turn90CCW(ab);
            const Point normal_corner = b + normal(ab_T, ab_offset_length) + bc_offset; // WARNING: offset is not based on the directions of the two segments, rather than assuming 90 degree corners
            const coord_t corner_bevel_here = (a_node->distance_depth % 2 == 1)? corner_bevel_even : corner_bevel_odd;
            if (corner_bevel_here)
            {
                infill.add(normal_corner - normal(ab, corner_bevel_here));
                infill.add(normal_corner + normal(bc, corner_bevel_here));
            }
            else
            {
                infill.add(normal_corner);
            }
        }
    }
}

}; // namespace cura
<|endoftext|>