<commit_before>/*
 * Copyright 2006-2010 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>

#include <zorba/diagnostic_list.h>
#include <zorba/store_consts.h>
#include <zorba/store_manager.h>
#include <zorba/tokenizer.h>
#include <zorba/user_exception.h>
#include <zorba/zorba.h>
#include <zorba/zorba_exception.h>

#include "system/properties.h"

using namespace std;
using namespace zorba;
using namespace zorba::locale;

static bool destroy_called;

///////////////////////////////////////////////////////////////////////////////

namespace std {

template<> struct less<char const*> :
  std::binary_function<char const*,char const*,bool>
{
  less() { }

  result_type
  operator()( first_argument_type a, second_argument_type b ) const {
    return std::strcmp( a, b ) < 0;
  }
};

} // namespace std

///////////////////////////////////////////////////////////////////////////////

class TestTokenizer : public Tokenizer {
public:
  TestTokenizer( Numbers &num ) : Tokenizer( num ) { }
  ~TestTokenizer();

  // inherited
  void destroy() const;
  void properties( Properties* ) const;
  void tokenize_string( char const*, size_type, iso639_1::type, bool,
                        Callback&, Item const* );

protected:
  // inherited
  void item( Item const&, bool );
  void tokenize_node_impl( Item const&, iso639_1::type, Callback&, void* );

private:
  typedef std::string token_t;

  enum token_type {
    t_generic,
    t_integer,
    t_floating_point,
    t_time
  };

  token_type t_type_;

  static bool is_word_begin_char( char );
  bool is_word_char( char );
  static char peek( char const *s, char const *end );
  bool send_token( token_t const &token, iso639_1::type, Callback&,
                   Item const* );
};

TestTokenizer::~TestTokenizer() {
  // out-of-line since it's virtual
}

void TestTokenizer::destroy() const {
  destroy_called = true;
  delete this;
}

void TestTokenizer::item( Item const &item, bool entering ) {
  static char const *const block_elements[] = {
    "address",
    "blockquote",
    "dd",
    "div",
    "dt",
    "h1", "h2", "h3", "h4", "h5", "h6",
    "legend",
    "li",
    "p",
    "pre",
    "td",
    "th",
  };
  static char const *const *const end =
    block_elements + sizeof( block_elements ) / sizeof( char* );

  if ( entering && item.isNode() &&
       item.getNodeKind() == store::StoreConsts::elementNode ) {
    Item qname;
    item.getNodeName( qname );
    if ( ::binary_search( block_elements, end, qname.getLocalName().c_str(),
                          less<char const*>() ) ) {
      ++numbers().para;
    }
  }
}

inline bool TestTokenizer::is_word_begin_char( char c ) {
  return !!isalnum( c );
}

bool TestTokenizer::is_word_char( char c ) {
  if ( t_type_ == t_integer ) {
    switch ( c ) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        break;
      case ',':
      case '.':
        t_type_ = t_floating_point;
        break;
      case ':':
        t_type_ = t_time;
        break;
      default:
        t_type_ = t_generic;
    }
  }
  switch ( t_type_ ) {
    case t_floating_point:
      return isdigit( c ) || c == '.' || c == ',';
    case t_time:
      return isdigit( c ) || c == ':';
    default:
      return is_word_begin_char( c )
          || c == '&'                   // AT&T
          || c == '\''                  // men's
          || c == '-'                   // set-top
          || c == '_';                  // language_identifier
  }
}

inline char TestTokenizer::peek( char const *s, char const *end ) {
  return ++s < end ? *s : '\0';
}

void TestTokenizer::properties( Properties *p ) const {
  ::memset( p, 0, sizeof( Properties ) );
  p->elements_separate_tokens = true;
  p->uri = "http://www.zorba-xquery.com/full-text/tokenizer/unit-test";
}

#define HANDLE_BACKSLASH()            \
  if ( !got_backslash ) ; else {      \
    got_backslash = in_wild = false;  \
    break;                            \
  }

void TestTokenizer::tokenize_string( char const *s, size_type s_len,
                                     iso639_1::type lang, bool wildcards,
                                     Callback &callback, Item const *item ) {
  bool got_backslash = false;
  bool in_wild = false;
  token_t token;

  t_type_ = t_generic;

  for ( char const *const end = s + s_len; s < end; ++s ) {
    if ( wildcards ) {
      switch ( *s ) {
        case '.':
          HANDLE_BACKSLASH();
          in_wild = true;
          break;
        case '\\':
          HANDLE_BACKSLASH();
          got_backslash = true;
          break;
        case '*':
        case '+':
        case '?':
          HANDLE_BACKSLASH();
          break;
        case '{':
          HANDLE_BACKSLASH();
          if ( in_wild ) {
            //
            // Validate that the regex "{[0-9]+,[0-9]+}" is matched.
            //
            bool got_comma = false;
            bool got_digit = false;
            while ( ++s < end ) {
              if ( got_digit && *s == ',' ) {
                got_comma = true;
                break;
              }
              if ( !isdigit( *s ) )
                throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
              got_digit = true;
            }
            if ( !got_comma )
              throw USER_EXCEPTION( err::FTDY0020, "',' expected" );
            got_digit = false;
            while ( ++s < end ) {
              if ( *s == '}' ) {
                if ( !got_digit )
                  throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
                break;
              }
              if ( !isdigit( *s ) )
                throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
              got_digit = true;
            }
          }
          break;
        default:
          in_wild = false;
      } // switch
    } // if ( wildcards )

    if ( token.empty() ) {
      if ( is_word_begin_char( *s ) ) {
        token += *s;
        if ( isdigit( *s ) )
          t_type_ = t_integer;
      }
    } else {
      if ( is_word_char( *s ) )
        token += *s;
      else if ( send_token( token, lang, callback, item ) ) {
        token.clear();
        t_type_ = t_generic;
      }
    }

    if ( !wildcards )
      switch ( *s ) {
        case '.':
          switch ( peek( s, end ) ) {
            case '"':
            case '\'':
            case ')':
            case ']':
            case '}':
            case ' ':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
            case '\v':
              break;
            default:
              continue;
          }
          // no break;
        case '!':
        case '?':
          ++numbers().sent;
      }
  } // for

  send_token( token, lang, callback, item );
}

static char const *const tokens[] = {
  "It", "was", "the", "best", "of", "times",
  "It", "was", "the", "worst", "of", "times",
  "1.2", "10,000.00",
  "1,2", "10.000,00",
  "12:00", "1:42", "02:12:37",
};
static unsigned num_tokens = sizeof( tokens ) / sizeof( tokens[0] );

static void check_token( char const *token, Tokenizer::size_type t_no ) {
  static bool did_t0 = false;
  if ( !t_no ) {
    if ( did_t0 )
      return;
    did_t0 = true;
  }
  if ( t_no < num_tokens && ::strcmp( token, tokens[ t_no ] ) != 0 )
    ::exit( 3 );
}

#define PRINT_TOKENS 0

bool TestTokenizer::send_token( token_t const &token, iso639_1::type lang,
                                Callback &callback, Item const *item ) {
  if ( !token.empty() ) {
#if PRINT_TOKENS
    cout <<   "t=" << setw(2) << numbers().token
         << ", s=" << setw(2) << numbers().sent
         << ", p=" << setw(2) << numbers().para
         << ": \"" << token << "\"\n";
#endif /* PRINT_TOKENS */

    check_token( token.c_str(), numbers().token );

    callback.token(
      token.data(), token.size(), lang,
      numbers().token, numbers().sent, numbers().para, item
    );
    ++numbers().token;
    return true;
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////

class TestTokenizerProvider : public TokenizerProvider {
public:
  // inherited
  Tokenizer::ptr getTokenizer( iso639_1::type, Tokenizer::Numbers& ) const;
};

Tokenizer::ptr
TestTokenizerProvider::getTokenizer( iso639_1::type lang,
                                     Tokenizer::Numbers &num ) const {
  return Tokenizer::ptr( new TestTokenizer( num ) );
}

///////////////////////////////////////////////////////////////////////////////

int tokenizer( int argc, char *argv[] ) {
  int result = 0;

  void *const zstore = StoreManager::getStore();
  Zorba *const zorba = Zorba::getInstance( zstore );

  try {
    Properties::load( 0, NULL );

    char const *const query_src =
      "let $x :="
      "  <content>"
      "    <quote>"
      "
It was the best of times.
"
      "
It was the worst of times
."
      "    </quote>"
      "    <numbers-us>1.2 10,000.00</numbers-us>"
      "    <numbers-eu>1,2 10.000,00</numbers-eu>"
      "    <times>12:00 1:42 02:12:37</times>"
      "  </content>"
      "return $x contains text \"times\"";

    TestTokenizerProvider provider;
    zorba->getXmlDataManager()->registerTokenizerProvider( &provider );

    StaticContext_t sctx = zorba->createStaticContext();
    XQuery_t xquery = zorba->compileQuery( query_src, sctx );

    Zorba_SerializerOptions ser_options;
    ser_options.omit_xml_declaration = ZORBA_OMIT_XML_DECLARATION_YES;
    std::ostringstream sout;
    xquery->execute( sout, &ser_options );

    if ( !destroy_called )
      result = 1;

    if ( sout.str() != "true" )
      result = 2;
  }
  catch ( ZorbaException const &e ) {
    cerr << e << endl;
    result = 2;
  }

  zorba->shutdown();
  StoreManager::shutdownStore( zstore );
  return result;
}
/* vim:set et sw=2 ts=2: */
<commit_msg>Removed unused function.<commit_after>/*
 * Copyright 2006-2010 The FLWOR Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>

#include <zorba/diagnostic_list.h>
#include <zorba/store_consts.h>
#include <zorba/store_manager.h>
#include <zorba/tokenizer.h>
#include <zorba/user_exception.h>
#include <zorba/zorba.h>
#include <zorba/zorba_exception.h>

#include "system/properties.h"

using namespace std;
using namespace zorba;
using namespace zorba::locale;

static bool destroy_called;

///////////////////////////////////////////////////////////////////////////////

namespace std {

template<> struct less<char const*> :
  std::binary_function<char const*,char const*,bool>
{
  less() { }

  result_type
  operator()( first_argument_type a, second_argument_type b ) const {
    return std::strcmp( a, b ) < 0;
  }
};

} // namespace std

///////////////////////////////////////////////////////////////////////////////

class TestTokenizer : public Tokenizer {
public:
  TestTokenizer( Numbers &num ) : Tokenizer( num ) { }
  ~TestTokenizer();

  // inherited
  void destroy() const;
  void properties( Properties* ) const;
  void tokenize_string( char const*, size_type, iso639_1::type, bool,
                        Callback&, Item const* );

protected:
  // inherited
  void item( Item const&, bool );

private:
  typedef std::string token_t;

  enum token_type {
    t_generic,
    t_integer,
    t_floating_point,
    t_time
  };

  token_type t_type_;

  static bool is_word_begin_char( char );
  bool is_word_char( char );
  static char peek( char const *s, char const *end );
  bool send_token( token_t const &token, iso639_1::type, Callback&,
                   Item const* );
};

TestTokenizer::~TestTokenizer() {
  // out-of-line since it's virtual
}

void TestTokenizer::destroy() const {
  destroy_called = true;
  delete this;
}

void TestTokenizer::item( Item const &item, bool entering ) {
  static char const *const block_elements[] = {
    "address",
    "blockquote",
    "dd",
    "div",
    "dt",
    "h1", "h2", "h3", "h4", "h5", "h6",
    "legend",
    "li",
    "p",
    "pre",
    "td",
    "th",
  };
  static char const *const *const end =
    block_elements + sizeof( block_elements ) / sizeof( char* );

  if ( entering && item.isNode() &&
       item.getNodeKind() == store::StoreConsts::elementNode ) {
    Item qname;
    item.getNodeName( qname );
    if ( ::binary_search( block_elements, end, qname.getLocalName().c_str(),
                          less<char const*>() ) ) {
      ++numbers().para;
    }
  }
}

inline bool TestTokenizer::is_word_begin_char( char c ) {
  return !!isalnum( c );
}

bool TestTokenizer::is_word_char( char c ) {
  if ( t_type_ == t_integer ) {
    switch ( c ) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        break;
      case ',':
      case '.':
        t_type_ = t_floating_point;
        break;
      case ':':
        t_type_ = t_time;
        break;
      default:
        t_type_ = t_generic;
    }
  }
  switch ( t_type_ ) {
    case t_floating_point:
      return isdigit( c ) || c == '.' || c == ',';
    case t_time:
      return isdigit( c ) || c == ':';
    default:
      return is_word_begin_char( c )
          || c == '&'                   // AT&T
          || c == '\''                  // men's
          || c == '-'                   // set-top
          || c == '_';                  // language_identifier
  }
}

inline char TestTokenizer::peek( char const *s, char const *end ) {
  return ++s < end ? *s : '\0';
}

void TestTokenizer::properties( Properties *p ) const {
  ::memset( p, 0, sizeof( Properties ) );
  p->elements_separate_tokens = true;
  p->uri = "http://www.zorba-xquery.com/full-text/tokenizer/unit-test";
}

#define HANDLE_BACKSLASH()            \
  if ( !got_backslash ) ; else {      \
    got_backslash = in_wild = false;  \
    break;                            \
  }

void TestTokenizer::tokenize_string( char const *s, size_type s_len,
                                     iso639_1::type lang, bool wildcards,
                                     Callback &callback, Item const *item ) {
  bool got_backslash = false;
  bool in_wild = false;
  token_t token;

  t_type_ = t_generic;

  for ( char const *const end = s + s_len; s < end; ++s ) {
    if ( wildcards ) {
      switch ( *s ) {
        case '.':
          HANDLE_BACKSLASH();
          in_wild = true;
          break;
        case '\\':
          HANDLE_BACKSLASH();
          got_backslash = true;
          break;
        case '*':
        case '+':
        case '?':
          HANDLE_BACKSLASH();
          break;
        case '{':
          HANDLE_BACKSLASH();
          if ( in_wild ) {
            //
            // Validate that the regex "{[0-9]+,[0-9]+}" is matched.
            //
            bool got_comma = false;
            bool got_digit = false;
            while ( ++s < end ) {
              if ( got_digit && *s == ',' ) {
                got_comma = true;
                break;
              }
              if ( !isdigit( *s ) )
                throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
              got_digit = true;
            }
            if ( !got_comma )
              throw USER_EXCEPTION( err::FTDY0020, "',' expected" );
            got_digit = false;
            while ( ++s < end ) {
              if ( *s == '}' ) {
                if ( !got_digit )
                  throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
                break;
              }
              if ( !isdigit( *s ) )
                throw USER_EXCEPTION( err::FTDY0020, "digit expected" );
              got_digit = true;
            }
          }
          break;
        default:
          in_wild = false;
      } // switch
    } // if ( wildcards )

    if ( token.empty() ) {
      if ( is_word_begin_char( *s ) ) {
        token += *s;
        if ( isdigit( *s ) )
          t_type_ = t_integer;
      }
    } else {
      if ( is_word_char( *s ) )
        token += *s;
      else if ( send_token( token, lang, callback, item ) ) {
        token.clear();
        t_type_ = t_generic;
      }
    }

    if ( !wildcards )
      switch ( *s ) {
        case '.':
          switch ( peek( s, end ) ) {
            case '"':
            case '\'':
            case ')':
            case ']':
            case '}':
            case ' ':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
            case '\v':
              break;
            default:
              continue;
          }
          // no break;
        case '!':
        case '?':
          ++numbers().sent;
      }
  } // for

  send_token( token, lang, callback, item );
}

static char const *const tokens[] = {
  "It", "was", "the", "best", "of", "times",
  "It", "was", "the", "worst", "of", "times",
  "1.2", "10,000.00",
  "1,2", "10.000,00",
  "12:00", "1:42", "02:12:37",
};
static unsigned num_tokens = sizeof( tokens ) / sizeof( tokens[0] );

static void check_token( char const *token, Tokenizer::size_type t_no ) {
  static bool did_t0 = false;
  if ( !t_no ) {
    if ( did_t0 )
      return;
    did_t0 = true;
  }
  if ( t_no < num_tokens && ::strcmp( token, tokens[ t_no ] ) != 0 )
    ::exit( 3 );
}

#define PRINT_TOKENS 0

bool TestTokenizer::send_token( token_t const &token, iso639_1::type lang,
                                Callback &callback, Item const *item ) {
  if ( !token.empty() ) {
#if PRINT_TOKENS
    cout <<   "t=" << setw(2) << numbers().token
         << ", s=" << setw(2) << numbers().sent
         << ", p=" << setw(2) << numbers().para
         << ": \"" << token << "\"\n";
#endif /* PRINT_TOKENS */

    check_token( token.c_str(), numbers().token );

    callback.token(
      token.data(), token.size(), lang,
      numbers().token, numbers().sent, numbers().para, item
    );
    ++numbers().token;
    return true;
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////

class TestTokenizerProvider : public TokenizerProvider {
public:
  // inherited
  Tokenizer::ptr getTokenizer( iso639_1::type, Tokenizer::Numbers& ) const;
};

Tokenizer::ptr
TestTokenizerProvider::getTokenizer( iso639_1::type lang,
                                     Tokenizer::Numbers &num ) const {
  return Tokenizer::ptr( new TestTokenizer( num ) );
}

///////////////////////////////////////////////////////////////////////////////

int tokenizer( int argc, char *argv[] ) {
  int result = 0;

  void *const zstore = StoreManager::getStore();
  Zorba *const zorba = Zorba::getInstance( zstore );

  try {
    Properties::load( 0, NULL );

    char const *const query_src =
      "let $x :="
      "  <content>"
      "    <quote>"
      "
It was the best of times.
"
      "
It was the worst of times
."
      "    </quote>"
      "    <numbers-us>1.2 10,000.00</numbers-us>"
      "    <numbers-eu>1,2 10.000,00</numbers-eu>"
      "    <times>12:00 1:42 02:12:37</times>"
      "  </content>"
      "return $x contains text \"times\"";

    TestTokenizerProvider provider;
    zorba->getXmlDataManager()->registerTokenizerProvider( &provider );

    StaticContext_t sctx = zorba->createStaticContext();
    XQuery_t xquery = zorba->compileQuery( query_src, sctx );

    Zorba_SerializerOptions ser_options;
    ser_options.omit_xml_declaration = ZORBA_OMIT_XML_DECLARATION_YES;
    std::ostringstream sout;
    xquery->execute( sout, &ser_options );

    if ( !destroy_called )
      result = 1;

    if ( sout.str() != "true" )
      result = 2;
  }
  catch ( ZorbaException const &e ) {
    cerr << e << endl;
    result = 2;
  }

  zorba->shutdown();
  StoreManager::shutdownStore( zstore );
  return result;
}
/* vim:set et sw=2 ts=2: */
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <string>

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "./vpx_config.h"
#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/md5_helper.h"
#if CONFIG_WEBM_IO
#include "test/webm_video_source.h"
#endif
#include "vp9/common/vp9_thread.h"

namespace {

using std::string;

class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> {
 protected:
  virtual ~VP9WorkerThreadTest() {}
  virtual void SetUp() {
    vp9_get_worker_interface()->init(&worker_);
  }

  virtual void TearDown() {
    vp9_get_worker_interface()->end(&worker_);
  }

  void Run(VP9Worker* worker) {
    const bool synchronous = GetParam();
    if (synchronous) {
      vp9_get_worker_interface()->execute(worker);
    } else {
      vp9_get_worker_interface()->launch(worker);
    }
  }

  VP9Worker worker_;
};

int ThreadHook(void* data, void* return_value) {
  int* const hook_data = reinterpret_cast<int*>(data);
  *hook_data = 5;
  return *reinterpret_cast<int*>(return_value);
}

TEST_P(VP9WorkerThreadTest, HookSuccess) {
  // should be a no-op.
  EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);

  for (int i = 0; i < 2; ++i) {
    EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);

    int hook_data = 0;
    int return_value = 1;  // return successfully from the hook
    worker_.hook = ThreadHook;
    worker_.data1 = &hook_data;
    worker_.data2 = &return_value;

    Run(&worker_);
    EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
    EXPECT_FALSE(worker_.had_error);
    EXPECT_EQ(5, hook_data);

    // should be a no-op.
    EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
  }
}

TEST_P(VP9WorkerThreadTest, HookFailure) {
  EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);

  int hook_data = 0;
  int return_value = 0;  // return failure from the hook
  worker_.hook = ThreadHook;
  worker_.data1 = &hook_data;
  worker_.data2 = &return_value;

  Run(&worker_);
  EXPECT_FALSE(vp9_get_worker_interface()->sync(&worker_));
  EXPECT_EQ(1, worker_.had_error);

  // Ensure _reset() clears the error and _launch() can be called again.
  return_value = 1;
  EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
  vp9_get_worker_interface()->launch(&worker_);
  EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
}

TEST_P(VP9WorkerThreadTest, EndWithoutSync) {
  // Create a large number of threads to increase the chances of detecting a
  // race. Doing more work in the hook is no guarantee as any race would occur
  // post hook execution in the main thread loop driver.
  static const int kNumWorkers = 64;
  VP9Worker workers[kNumWorkers];
  int hook_data[kNumWorkers];
  int return_value[kNumWorkers];

  for (int n = 0; n < kNumWorkers; ++n) {
    vp9_get_worker_interface()->init(&workers[n]);
    return_value[n] = 1;  // return successfully from the hook
    workers[n].hook = ThreadHook;
    workers[n].data1 = &hook_data[n];
    workers[n].data2 = &return_value[n];
  }

  for (int i = 0; i < 2; ++i) {
    for (int n = 0; n < kNumWorkers; ++n) {
      EXPECT_NE(vp9_get_worker_interface()->reset(&workers[n]), 0);
      hook_data[n] = 0;
    }

    for (int n = 0; n < kNumWorkers; ++n) {
      Run(&workers[n]);
    }

    for (int n = kNumWorkers - 1; n >= 0; --n) {
      vp9_get_worker_interface()->end(&workers[n]);
    }
  }
}

TEST(VP9WorkerThreadTest, TestInterfaceAPI) {
  EXPECT_EQ(0, vp9_set_worker_interface(NULL));
  EXPECT_TRUE(vp9_get_worker_interface() != NULL);
  for (int i = 0; i < 6; ++i) {
    VP9WorkerInterface winterface = *vp9_get_worker_interface();
    switch (i) {
      default:
      case 0: winterface.init = NULL; break;
      case 1: winterface.reset = NULL; break;
      case 2: winterface.sync = NULL; break;
      case 3: winterface.launch = NULL; break;
      case 4: winterface.execute = NULL; break;
      case 5: winterface.end = NULL; break;
    }
    EXPECT_EQ(0, vp9_set_worker_interface(&winterface));
  }
}

// -----------------------------------------------------------------------------
// Multi-threaded decode tests

#if CONFIG_WEBM_IO
struct FileList {
  const char *name;
  const char *expected_md5;
};

// Decodes |filename| with |num_threads|. Returns the md5 of the decoded frames.
string DecodeFile(const string& filename, int num_threads) {
  libvpx_test::WebMVideoSource video(filename);
  video.Init();

  vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
  cfg.threads = num_threads;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  libvpx_test::MD5 md5;
  for (video.Begin(); video.cxdata(); video.Next()) {
    const vpx_codec_err_t res =
        decoder.DecodeFrame(video.cxdata(), video.frame_size());
    if (res != VPX_CODEC_OK) {
      EXPECT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError();
      break;
    }

    libvpx_test::DxDataIterator dec_iter = decoder.GetDxData();
    const vpx_image_t *img = NULL;

    // Get decompressed data
    while ((img = dec_iter.Next())) {
      md5.Add(img);
    }
  }
  return string(md5.Get());
}

void DecodeFiles(const FileList files[]) {
  for (const FileList *iter = files; iter->name != NULL; ++iter) {
    SCOPED_TRACE(iter->name);
    for (int t = 2; t <= 8; ++t) {
      EXPECT_EQ(iter->expected_md5, DecodeFile(iter->name, t))
          << "threads = " << t;
    }
  }
}

// Trivial serialized thread worker interface implementation.
// Note any worker that requires synchronization between other workers will
// hang.
namespace impl {

void Init(VP9Worker *const worker) { memset(worker, 0, sizeof(*worker)); }
int Reset(VP9Worker *const /*worker*/) { return 1; }
int Sync(VP9Worker *const worker) { return !worker->had_error; }

void Execute(VP9Worker *const worker) {
  worker->had_error |= worker->hook(worker->data1, worker->data2);
}

void Launch(VP9Worker *const worker) { Execute(worker); }
void End(VP9Worker *const /*worker*/) {}

}  // namespace impl

TEST(VP9WorkerThreadTest, TestSerialInterface) {
  static const VP9WorkerInterface serial_interface = {
    impl::Init, impl::Reset, impl::Sync, impl::Launch, impl::Execute, impl::End
  };
  // TODO(jzern): Avoid using a file that will use the row-based thread
  // loopfilter, with the simple serialized implementation it will hang. This is
  // due to its expectation that rows will be run in parallel as they wait on
  // progress in the row above before proceeding.
  static const char expected_md5[] = "b35a1b707b28e82be025d960aba039bc";
  static const char filename[] = "vp90-2-03-size-226x226.webm";
  VP9WorkerInterface default_interface = *vp9_get_worker_interface();

  EXPECT_NE(vp9_set_worker_interface(&serial_interface), 0);
  EXPECT_EQ(expected_md5, DecodeFile(filename, 2));

  // Reset the interface.
  EXPECT_NE(vp9_set_worker_interface(&default_interface), 0);
  EXPECT_EQ(expected_md5, DecodeFile(filename, 2));
}

TEST(VP9DecodeMultiThreadedTest, Decode) {
  // no tiles or frame parallel; this exercises loop filter threading.
  EXPECT_EQ("b35a1b707b28e82be025d960aba039bc",
            DecodeFile("vp90-2-03-size-226x226.webm", 2));
}

TEST(VP9DecodeMultiThreadedTest, Decode2) {
  static const FileList files[] = {
    { "vp90-2-08-tile_1x2_frame_parallel.webm",
      "68ede6abd66bae0a2edf2eb9232241b6" },
    { "vp90-2-08-tile_1x4_frame_parallel.webm",
      "368ebc6ebf3a5e478d85b2c3149b2848" },
    { "vp90-2-08-tile_1x8_frame_parallel.webm",
      "17e439da2388aff3a0f69cb22579c6c1" },
    { NULL, NULL }
  };

  DecodeFiles(files);
}

// Test tile quantity changes within one file.
TEST(VP9DecodeMultiThreadedTest, Decode3) {
  static const FileList files[] = {
    { "vp90-2-14-resize-fp-tiles-1-16.webm",
      "0cd5e632c326297e975f38949c31ea94" },
    { "vp90-2-14-resize-fp-tiles-1-2-4-8-16.webm",
      "5c78a96a42e7f4a4f6b2edcdb791e44c" },
    { "vp90-2-14-resize-fp-tiles-1-2.webm",
      "e030450ae85c3277be2a418769df98e2" },
    { "vp90-2-14-resize-fp-tiles-1-4.webm",
      "312eed4e2b64eb7a4e7f18916606a430" },
    { "vp90-2-14-resize-fp-tiles-16-1.webm",
      "1755c16d8af16a9cb3fe7338d90abe52" },
    { "vp90-2-14-resize-fp-tiles-16-2.webm",
      "500300592d3fcb6f12fab25e48aaf4df" },
    { "vp90-2-14-resize-fp-tiles-16-4.webm",
      "47c48379fa6331215d91c67648e1af6e" },
    { "vp90-2-14-resize-fp-tiles-16-8-4-2-1.webm",
      "eecf17290739bc708506fa4827665989" },
    { "vp90-2-14-resize-fp-tiles-16-8.webm",
      "29b6bb54e4c26b5ca85d5de5fed94e76" },
    { "vp90-2-14-resize-fp-tiles-1-8.webm",
      "1b6f175e08cd82cf84bb800ac6d1caa3" },
    { "vp90-2-14-resize-fp-tiles-2-16.webm",
      "ca3b03e4197995d8d5444ede7a6c0804" },
    { "vp90-2-14-resize-fp-tiles-2-1.webm",
      "99aec065369d70bbb78ccdff65afed3f" },
    { "vp90-2-14-resize-fp-tiles-2-4.webm",
      "22d0ebdb49b87d2920a85aea32e1afd5" },
    { "vp90-2-14-resize-fp-tiles-2-8.webm",
      "c2115cf051c62e0f7db1d4a783831541" },
    { "vp90-2-14-resize-fp-tiles-4-16.webm",
      "c690d7e1719b31367564cac0af0939cb" },
    { "vp90-2-14-resize-fp-tiles-4-1.webm",
      "a926020b2cc3e15ad4cc271853a0ff26" },
    { "vp90-2-14-resize-fp-tiles-4-2.webm",
      "42699063d9e581f1993d0cf890c2be78" },
    { "vp90-2-14-resize-fp-tiles-4-8.webm",
      "7f76d96036382f45121e3d5aa6f8ec52" },
    { "vp90-2-14-resize-fp-tiles-8-16.webm",
      "76a43fcdd7e658542913ea43216ec55d" },
    { "vp90-2-14-resize-fp-tiles-8-1.webm",
      "8e3fbe89486ca60a59299dea9da91378" },
    { "vp90-2-14-resize-fp-tiles-8-2.webm",
      "ae96f21f21b6370cc0125621b441fc52" },
    { "vp90-2-14-resize-fp-tiles-8-4.webm",
      "3eb4f24f10640d42218f7fd7b9fd30d4" },
    { NULL, NULL }
  };

  DecodeFiles(files);
}
#endif  // CONFIG_WEBM_IO

INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool());

}  // namespace
<commit_msg>vp9_thread_test: fix 'had_error' assignment<commit_after>/*
 *  Copyright (c) 2013 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <string>

#include "third_party/googletest/src/include/gtest/gtest.h"
#include "./vpx_config.h"
#include "test/codec_factory.h"
#include "test/decode_test_driver.h"
#include "test/md5_helper.h"
#if CONFIG_WEBM_IO
#include "test/webm_video_source.h"
#endif
#include "vp9/common/vp9_thread.h"

namespace {

using std::string;

class VP9WorkerThreadTest : public ::testing::TestWithParam<bool> {
 protected:
  virtual ~VP9WorkerThreadTest() {}
  virtual void SetUp() {
    vp9_get_worker_interface()->init(&worker_);
  }

  virtual void TearDown() {
    vp9_get_worker_interface()->end(&worker_);
  }

  void Run(VP9Worker* worker) {
    const bool synchronous = GetParam();
    if (synchronous) {
      vp9_get_worker_interface()->execute(worker);
    } else {
      vp9_get_worker_interface()->launch(worker);
    }
  }

  VP9Worker worker_;
};

int ThreadHook(void* data, void* return_value) {
  int* const hook_data = reinterpret_cast<int*>(data);
  *hook_data = 5;
  return *reinterpret_cast<int*>(return_value);
}

TEST_P(VP9WorkerThreadTest, HookSuccess) {
  // should be a no-op.
  EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);

  for (int i = 0; i < 2; ++i) {
    EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);

    int hook_data = 0;
    int return_value = 1;  // return successfully from the hook
    worker_.hook = ThreadHook;
    worker_.data1 = &hook_data;
    worker_.data2 = &return_value;

    Run(&worker_);
    EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
    EXPECT_FALSE(worker_.had_error);
    EXPECT_EQ(5, hook_data);

    // should be a no-op.
    EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
  }
}

TEST_P(VP9WorkerThreadTest, HookFailure) {
  EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);

  int hook_data = 0;
  int return_value = 0;  // return failure from the hook
  worker_.hook = ThreadHook;
  worker_.data1 = &hook_data;
  worker_.data2 = &return_value;

  Run(&worker_);
  EXPECT_FALSE(vp9_get_worker_interface()->sync(&worker_));
  EXPECT_EQ(1, worker_.had_error);

  // Ensure _reset() clears the error and _launch() can be called again.
  return_value = 1;
  EXPECT_NE(vp9_get_worker_interface()->reset(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
  vp9_get_worker_interface()->launch(&worker_);
  EXPECT_NE(vp9_get_worker_interface()->sync(&worker_), 0);
  EXPECT_FALSE(worker_.had_error);
}

TEST_P(VP9WorkerThreadTest, EndWithoutSync) {
  // Create a large number of threads to increase the chances of detecting a
  // race. Doing more work in the hook is no guarantee as any race would occur
  // post hook execution in the main thread loop driver.
  static const int kNumWorkers = 64;
  VP9Worker workers[kNumWorkers];
  int hook_data[kNumWorkers];
  int return_value[kNumWorkers];

  for (int n = 0; n < kNumWorkers; ++n) {
    vp9_get_worker_interface()->init(&workers[n]);
    return_value[n] = 1;  // return successfully from the hook
    workers[n].hook = ThreadHook;
    workers[n].data1 = &hook_data[n];
    workers[n].data2 = &return_value[n];
  }

  for (int i = 0; i < 2; ++i) {
    for (int n = 0; n < kNumWorkers; ++n) {
      EXPECT_NE(vp9_get_worker_interface()->reset(&workers[n]), 0);
      hook_data[n] = 0;
    }

    for (int n = 0; n < kNumWorkers; ++n) {
      Run(&workers[n]);
    }

    for (int n = kNumWorkers - 1; n >= 0; --n) {
      vp9_get_worker_interface()->end(&workers[n]);
    }
  }
}

TEST(VP9WorkerThreadTest, TestInterfaceAPI) {
  EXPECT_EQ(0, vp9_set_worker_interface(NULL));
  EXPECT_TRUE(vp9_get_worker_interface() != NULL);
  for (int i = 0; i < 6; ++i) {
    VP9WorkerInterface winterface = *vp9_get_worker_interface();
    switch (i) {
      default:
      case 0: winterface.init = NULL; break;
      case 1: winterface.reset = NULL; break;
      case 2: winterface.sync = NULL; break;
      case 3: winterface.launch = NULL; break;
      case 4: winterface.execute = NULL; break;
      case 5: winterface.end = NULL; break;
    }
    EXPECT_EQ(0, vp9_set_worker_interface(&winterface));
  }
}

// -----------------------------------------------------------------------------
// Multi-threaded decode tests

#if CONFIG_WEBM_IO
struct FileList {
  const char *name;
  const char *expected_md5;
};

// Decodes |filename| with |num_threads|. Returns the md5 of the decoded frames.
string DecodeFile(const string& filename, int num_threads) {
  libvpx_test::WebMVideoSource video(filename);
  video.Init();

  vpx_codec_dec_cfg_t cfg = vpx_codec_dec_cfg_t();
  cfg.threads = num_threads;
  libvpx_test::VP9Decoder decoder(cfg, 0);

  libvpx_test::MD5 md5;
  for (video.Begin(); video.cxdata(); video.Next()) {
    const vpx_codec_err_t res =
        decoder.DecodeFrame(video.cxdata(), video.frame_size());
    if (res != VPX_CODEC_OK) {
      EXPECT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError();
      break;
    }

    libvpx_test::DxDataIterator dec_iter = decoder.GetDxData();
    const vpx_image_t *img = NULL;

    // Get decompressed data
    while ((img = dec_iter.Next())) {
      md5.Add(img);
    }
  }
  return string(md5.Get());
}

void DecodeFiles(const FileList files[]) {
  for (const FileList *iter = files; iter->name != NULL; ++iter) {
    SCOPED_TRACE(iter->name);
    for (int t = 2; t <= 8; ++t) {
      EXPECT_EQ(iter->expected_md5, DecodeFile(iter->name, t))
          << "threads = " << t;
    }
  }
}

// Trivial serialized thread worker interface implementation.
// Note any worker that requires synchronization between other workers will
// hang.
namespace impl {

void Init(VP9Worker *const worker) { memset(worker, 0, sizeof(*worker)); }
int Reset(VP9Worker *const /*worker*/) { return 1; }
int Sync(VP9Worker *const worker) { return !worker->had_error; }

void Execute(VP9Worker *const worker) {
  worker->had_error |= !worker->hook(worker->data1, worker->data2);
}

void Launch(VP9Worker *const worker) { Execute(worker); }
void End(VP9Worker *const /*worker*/) {}

}  // namespace impl

TEST(VP9WorkerThreadTest, TestSerialInterface) {
  static const VP9WorkerInterface serial_interface = {
    impl::Init, impl::Reset, impl::Sync, impl::Launch, impl::Execute, impl::End
  };
  // TODO(jzern): Avoid using a file that will use the row-based thread
  // loopfilter, with the simple serialized implementation it will hang. This is
  // due to its expectation that rows will be run in parallel as they wait on
  // progress in the row above before proceeding.
  static const char expected_md5[] = "b35a1b707b28e82be025d960aba039bc";
  static const char filename[] = "vp90-2-03-size-226x226.webm";
  VP9WorkerInterface default_interface = *vp9_get_worker_interface();

  EXPECT_NE(vp9_set_worker_interface(&serial_interface), 0);
  EXPECT_EQ(expected_md5, DecodeFile(filename, 2));

  // Reset the interface.
  EXPECT_NE(vp9_set_worker_interface(&default_interface), 0);
  EXPECT_EQ(expected_md5, DecodeFile(filename, 2));
}

TEST(VP9DecodeMultiThreadedTest, Decode) {
  // no tiles or frame parallel; this exercises loop filter threading.
  EXPECT_EQ("b35a1b707b28e82be025d960aba039bc",
            DecodeFile("vp90-2-03-size-226x226.webm", 2));
}

TEST(VP9DecodeMultiThreadedTest, Decode2) {
  static const FileList files[] = {
    { "vp90-2-08-tile_1x2_frame_parallel.webm",
      "68ede6abd66bae0a2edf2eb9232241b6" },
    { "vp90-2-08-tile_1x4_frame_parallel.webm",
      "368ebc6ebf3a5e478d85b2c3149b2848" },
    { "vp90-2-08-tile_1x8_frame_parallel.webm",
      "17e439da2388aff3a0f69cb22579c6c1" },
    { NULL, NULL }
  };

  DecodeFiles(files);
}

// Test tile quantity changes within one file.
TEST(VP9DecodeMultiThreadedTest, Decode3) {
  static const FileList files[] = {
    { "vp90-2-14-resize-fp-tiles-1-16.webm",
      "0cd5e632c326297e975f38949c31ea94" },
    { "vp90-2-14-resize-fp-tiles-1-2-4-8-16.webm",
      "5c78a96a42e7f4a4f6b2edcdb791e44c" },
    { "vp90-2-14-resize-fp-tiles-1-2.webm",
      "e030450ae85c3277be2a418769df98e2" },
    { "vp90-2-14-resize-fp-tiles-1-4.webm",
      "312eed4e2b64eb7a4e7f18916606a430" },
    { "vp90-2-14-resize-fp-tiles-16-1.webm",
      "1755c16d8af16a9cb3fe7338d90abe52" },
    { "vp90-2-14-resize-fp-tiles-16-2.webm",
      "500300592d3fcb6f12fab25e48aaf4df" },
    { "vp90-2-14-resize-fp-tiles-16-4.webm",
      "47c48379fa6331215d91c67648e1af6e" },
    { "vp90-2-14-resize-fp-tiles-16-8-4-2-1.webm",
      "eecf17290739bc708506fa4827665989" },
    { "vp90-2-14-resize-fp-tiles-16-8.webm",
      "29b6bb54e4c26b5ca85d5de5fed94e76" },
    { "vp90-2-14-resize-fp-tiles-1-8.webm",
      "1b6f175e08cd82cf84bb800ac6d1caa3" },
    { "vp90-2-14-resize-fp-tiles-2-16.webm",
      "ca3b03e4197995d8d5444ede7a6c0804" },
    { "vp90-2-14-resize-fp-tiles-2-1.webm",
      "99aec065369d70bbb78ccdff65afed3f" },
    { "vp90-2-14-resize-fp-tiles-2-4.webm",
      "22d0ebdb49b87d2920a85aea32e1afd5" },
    { "vp90-2-14-resize-fp-tiles-2-8.webm",
      "c2115cf051c62e0f7db1d4a783831541" },
    { "vp90-2-14-resize-fp-tiles-4-16.webm",
      "c690d7e1719b31367564cac0af0939cb" },
    { "vp90-2-14-resize-fp-tiles-4-1.webm",
      "a926020b2cc3e15ad4cc271853a0ff26" },
    { "vp90-2-14-resize-fp-tiles-4-2.webm",
      "42699063d9e581f1993d0cf890c2be78" },
    { "vp90-2-14-resize-fp-tiles-4-8.webm",
      "7f76d96036382f45121e3d5aa6f8ec52" },
    { "vp90-2-14-resize-fp-tiles-8-16.webm",
      "76a43fcdd7e658542913ea43216ec55d" },
    { "vp90-2-14-resize-fp-tiles-8-1.webm",
      "8e3fbe89486ca60a59299dea9da91378" },
    { "vp90-2-14-resize-fp-tiles-8-2.webm",
      "ae96f21f21b6370cc0125621b441fc52" },
    { "vp90-2-14-resize-fp-tiles-8-4.webm",
      "3eb4f24f10640d42218f7fd7b9fd30d4" },
    { NULL, NULL }
  };

  DecodeFiles(files);
}
#endif  // CONFIG_WEBM_IO

INSTANTIATE_TEST_CASE_P(Synchronous, VP9WorkerThreadTest, ::testing::Bool());

}  // namespace
<|endoftext|>


<commit_before>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/Application.h" //To provide settings for the layer plan.
#include "../src/LayerPlan.h" //The code under test.
#include "../src/RetractionConfig.h" //To provide retraction settings.
#include "../src/Slice.h" //To provide settings for the layer plan.
#include "../src/sliceDataStorage.h" //To provide slice data as input for the planning stage.

namespace cura
{

/*!
 * A fixture to test layer plans with.
 *
 * This fixture gets the previous location initialised to 0,0. You can
 * optionally fill it with some layer data.
 */
class LayerPlanTest : public testing::Test
{
public:
    /*!
     * A pre-filled layer plan.
     */
    LayerPlan layer_plan;

    /*!
     * Sliced layers divided up into regions for each structure.
     */
    SliceDataStorage storage;

    /*!
     * Cooling settings, which are passed to the layer plan by reference.
     *
     * One for each extruder. There is only one extruder by default in this
     * fixture.
     */
    std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings;

    //Some generic settings.
    LayerIndex layer_nr = 100;
    coord_t layer_thickness = 100; //0.1mm.
    coord_t z = layer_thickness * layer_nr;
    size_t extruder_nr = 0;
    size_t comb_boundary_offset = 2000; //2mm.
    size_t comb_move_inside_distance = 1000; //1mm.
    size_t travel_avoid_distance = 5000; //5mm.

    LayerPlanTest() : layer_plan(setUp(storage), layer_nr, z, layer_thickness, extruder_nr, fan_speed_layer_time_settings, comb_boundary_offset, comb_move_inside_distance, travel_avoid_distance)
    {
    }

    /*!
     * Prepares the slice data storage before passing it to the layer plan.
     *
     * In order to prepare the slice data storage, the Application class is also
     * initialized with a proper current slice and all of the settings it needs.
     *
     * This needs to be done in a separate function so that it can be executed
     * in the initializer list.
     * \param storage The SliceDataStorage to fill.
     * \return That same SliceDataStorage.
     */
    SliceDataStorage& setUp(SliceDataStorage& storage)
    {
        constexpr size_t num_mesh_groups = 1;
        Application::getInstance().current_slice = new Slice(num_mesh_groups);

        //Define all settings in the mesh group. The extruder train and model settings will fall back on that then.
        Settings& settings = Application::getInstance().current_slice->scene.current_mesh_group->settings;
        //Path config storage settings.
        settings.add("acceleration_prime_tower", "5008");
        settings.add("acceleration_skirt_brim", "5007");
        settings.add("acceleration_support_bottom", "5005");
        settings.add("acceleration_support_infill", "5009");
        settings.add("acceleration_support_roof", "5004");
        settings.add("acceleration_travel", "5006");
        settings.add("adhesion_extruder_nr", "0");
        settings.add("adhesion_type", "brim");
        settings.add("initial_layer_line_width_factor", "1.0");
        settings.add("jerk_prime_tower", "5.8");
        settings.add("jerk_skirt_brim", "5.7");
        settings.add("jerk_support_bottom", "5.5");
        settings.add("jerk_support_infill", "5.9");
        settings.add("jerk_support_roof", "5.4");
        settings.add("jerk_travel", "5.6");
        settings.add("material_flow_layer_0", "100");
        settings.add("prime_tower_flow", "108");
        settings.add("prime_tower_line_width", "0.48");
        settings.add("raft_base_line_width", "0.401");
        settings.add("raft_base_acceleration", "5001");
        settings.add("raft_base_jerk", "5.1");
        settings.add("raft_base_speed", "51");
        settings.add("raft_base_thickness", "0.101");
        settings.add("raft_interface_acceleration", "5002");
        settings.add("raft_interface_jerk", "5.2");
        settings.add("raft_interface_line_width", "0.402");
        settings.add("raft_interface_speed", "52");
        settings.add("raft_interface_thickness", "0.102");
        settings.add("raft_surface_acceleration", "5003");
        settings.add("raft_surface_jerk", "5.3");
        settings.add("raft_surface_line_width", "0.403");
        settings.add("raft_surface_speed", "53");
        settings.add("raft_surface_thickness", "0.103");
        settings.add("skirt_brim_line_width", "0.47");
        settings.add("skirt_brim_material_flow", "107");
        settings.add("skirt_brim_speed", "57");
        settings.add("speed_prime_tower", "58");
        settings.add("speed_slowdown_layers", "1");
        settings.add("speed_support_bottom", "55");
        settings.add("speed_support_infill", "59");
        settings.add("speed_support_roof", "54");
        settings.add("speed_travel", "56");
        settings.add("support_bottom_extruder_nr", "0");
        settings.add("support_bottom_line_width", "0.405");
        settings.add("support_bottom_material_flow", "105");
        settings.add("support_infill_extruder_nr", "0");
        settings.add("support_line_width", "0.49");
        settings.add("support_material_flow", "109");
        settings.add("support_roof_extruder_nr", "0");
        settings.add("support_roof_line_width", "0.404");
        settings.add("support_roof_material_flow", "104");

        Application::getInstance().current_slice->scene.extruders.emplace_back(0, &settings); //Add an extruder train.
        return storage;
    }

    void SetUp()
    {
        FanSpeedLayerTimeSettings fan_settings;
        fan_settings.cool_min_layer_time = 5;
        fan_settings.cool_min_layer_time_fan_speed_max = 10;
        fan_settings.cool_fan_speed_0 = 0.0;
        fan_settings.cool_fan_speed_min = 75.0;
        fan_settings.cool_fan_speed_max = 100.0;
        fan_settings.cool_min_speed = 10;
        fan_settings.cool_fan_full_layer = 3;
        fan_speed_layer_time_settings.push_back(fan_settings);
    }
};

/*!
 * Tests planning a travel move through open space.
 */
TEST_F(LayerPlanTest, AddTravelOpen)
{

}

}<commit_msg>Properly call the constructor of SliceDataStorage<commit_after>//Copyright (c) 2019 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include <gtest/gtest.h>

#include "../src/Application.h" //To provide settings for the layer plan.
#include "../src/LayerPlan.h" //The code under test.
#include "../src/RetractionConfig.h" //To provide retraction settings.
#include "../src/Slice.h" //To provide settings for the layer plan.
#include "../src/sliceDataStorage.h" //To provide slice data as input for the planning stage.

namespace cura
{

/*!
 * A fixture to test layer plans with.
 *
 * This fixture gets the previous location initialised to 0,0. You can
 * optionally fill it with some layer data.
 */
class LayerPlanTest : public testing::Test
{
public:
    /*!
     * Sliced layers divided up into regions for each structure.
     */
    SliceDataStorage* storage;

    /*!
     * A pre-filled layer plan.
     */
    LayerPlan layer_plan;

    /*!
     * Cooling settings, which are passed to the layer plan by reference.
     *
     * One for each extruder. There is only one extruder by default in this
     * fixture.
     */
    std::vector<FanSpeedLayerTimeSettings> fan_speed_layer_time_settings;

    //Some generic settings.
    LayerIndex layer_nr = 100;
    coord_t layer_thickness = 100; //0.1mm.
    coord_t z = layer_thickness * layer_nr;
    size_t extruder_nr = 0;
    size_t comb_boundary_offset = 2000; //2mm.
    size_t comb_move_inside_distance = 1000; //1mm.
    size_t travel_avoid_distance = 5000; //5mm.

    LayerPlanTest() :
        storage(setUpStorage()),
        layer_plan(*storage, layer_nr, z, layer_thickness, extruder_nr, fan_speed_layer_time_settings, comb_boundary_offset, comb_move_inside_distance, travel_avoid_distance)
    {
    }

    /*!
     * Prepares the slice data storage before passing it to the layer plan.
     *
     * In order to prepare the slice data storage, the Application class is also
     * initialized with a proper current slice and all of the settings it needs.
     *
     * This needs to be done in a separate function so that it can be executed
     * in the initializer list.
     * \return That same SliceDataStorage.
     */
    SliceDataStorage* setUpStorage()
    {
        constexpr size_t num_mesh_groups = 1;
        Application::getInstance().current_slice = new Slice(num_mesh_groups);

        //Define all settings in the mesh group. The extruder train and model settings will fall back on that then.
        Settings& settings = Application::getInstance().current_slice->scene.current_mesh_group->settings;
        //Path config storage settings.
        settings.add("acceleration_prime_tower", "5008");
        settings.add("acceleration_skirt_brim", "5007");
        settings.add("acceleration_support_bottom", "5005");
        settings.add("acceleration_support_infill", "5009");
        settings.add("acceleration_support_roof", "5004");
        settings.add("acceleration_travel", "5006");
        settings.add("adhesion_extruder_nr", "0");
        settings.add("adhesion_type", "brim");
        settings.add("initial_layer_line_width_factor", "1.0");
        settings.add("jerk_prime_tower", "5.8");
        settings.add("jerk_skirt_brim", "5.7");
        settings.add("jerk_support_bottom", "5.5");
        settings.add("jerk_support_infill", "5.9");
        settings.add("jerk_support_roof", "5.4");
        settings.add("jerk_travel", "5.6");
        settings.add("material_flow_layer_0", "100");
        settings.add("prime_tower_flow", "108");
        settings.add("prime_tower_line_width", "0.48");
        settings.add("raft_base_line_width", "0.401");
        settings.add("raft_base_acceleration", "5001");
        settings.add("raft_base_jerk", "5.1");
        settings.add("raft_base_speed", "51");
        settings.add("raft_base_thickness", "0.101");
        settings.add("raft_interface_acceleration", "5002");
        settings.add("raft_interface_jerk", "5.2");
        settings.add("raft_interface_line_width", "0.402");
        settings.add("raft_interface_speed", "52");
        settings.add("raft_interface_thickness", "0.102");
        settings.add("raft_surface_acceleration", "5003");
        settings.add("raft_surface_jerk", "5.3");
        settings.add("raft_surface_line_width", "0.403");
        settings.add("raft_surface_speed", "53");
        settings.add("raft_surface_thickness", "0.103");
        settings.add("skirt_brim_line_width", "0.47");
        settings.add("skirt_brim_material_flow", "107");
        settings.add("skirt_brim_speed", "57");
        settings.add("speed_prime_tower", "58");
        settings.add("speed_slowdown_layers", "1");
        settings.add("speed_support_bottom", "55");
        settings.add("speed_support_infill", "59");
        settings.add("speed_support_roof", "54");
        settings.add("speed_travel", "56");
        settings.add("support_bottom_extruder_nr", "0");
        settings.add("support_bottom_line_width", "0.405");
        settings.add("support_bottom_material_flow", "105");
        settings.add("support_infill_extruder_nr", "0");
        settings.add("support_line_width", "0.49");
        settings.add("support_material_flow", "109");
        settings.add("support_roof_extruder_nr", "0");
        settings.add("support_roof_line_width", "0.404");
        settings.add("support_roof_material_flow", "104");

        Application::getInstance().current_slice->scene.extruders.emplace_back(0, &settings); //Add an extruder train.

        SliceDataStorage* result = new SliceDataStorage();
        return result;
    }

    void SetUp()
    {
        FanSpeedLayerTimeSettings fan_settings;
        fan_settings.cool_min_layer_time = 5;
        fan_settings.cool_min_layer_time_fan_speed_max = 10;
        fan_settings.cool_fan_speed_0 = 0.0;
        fan_settings.cool_fan_speed_min = 75.0;
        fan_settings.cool_fan_speed_max = 100.0;
        fan_settings.cool_min_speed = 10;
        fan_settings.cool_fan_full_layer = 3;
        fan_speed_layer_time_settings.push_back(fan_settings);
    }
};

/*!
 * Tests planning a travel move through open space.
 */
TEST_F(LayerPlanTest, AddTravelOpen)
{

}

}<|endoftext|>


<commit_before>//--------------------------------------------------------------------------
//    fe_tools.cc,v 1.1 2003/11/28 15:03:26 guido Exp
//    Version:
//
//    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2009 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//--------------------------------------------------------------------------

// Test the cell matrices generated in FETools and the local renumbering vector.

#include "../tests.h"
#include <iostream>
#include <iomanip>
#include <fstream>

#include <base/logstream.h>

#include <fe/fe_q.h>
#include <fe/fe_dgq.h>
#include <fe/fe_dgp.h>
#include <fe/fe_raviart_thomas.h>
#include <fe/fe_system.h>

#include <fe/fe_tools.h>

template <typename number>
void print_formatted (const FullMatrix<number> &A,
		      const unsigned int        precision,
		      const unsigned int        width)
{
  for (unsigned int i=0; i<A.m(); ++i)
    {
      for (unsigned int j=0; j<A.n(); ++j)
	{
	  if (A(i,j) != 0)
	    deallog << std::setw(width) << std::setprecision(precision)
		    << A(i,j);
	  else
	    deallog << std::setw(width) << std::setprecision(precision)
		    << "~";
	  deallog << ' ';
	};
      deallog << std::endl;
    };
}

template<int dim>
void test_embedding (const FiniteElement<dim>& fe)
{
  const std::string refine_case_names[8]=
    {"no_refinement",
     "cut_x",
     "cut_y",
     "cut_xy",
     "cut_z",
     "cut_xz",
     "cut_yz",
     "cut_xyz"};

  const unsigned int n = fe.dofs_per_cell;

  std::vector<std::vector<FullMatrix<double> > > P;
  P.resize(RefinementCase<dim>::isotropic_refinement);
  for (unsigned int ref_case=RefinementCase<dim>::cut_x;
       ref_case<RefinementCase<dim>::isotropic_refinement+1;
       ++ref_case)
    for (unsigned int c=0;
	 c<GeometryInfo<dim>::n_children(RefinementCase<dim>(ref_case));
	 ++c)
      P[ref_case-1].push_back(FullMatrix<double>(n));

  FETools::compute_embedding_matrices(fe, P);

  for (unsigned int ref_case=RefinementCase<dim>::cut_x;
       ref_case<RefinementCase<dim>::isotropic_refinement+1;
       ++ref_case)
    for (unsigned int c=0;
	 c<GeometryInfo<dim>::n_children(RefinementCase<dim>(ref_case));
	 ++c)
      {
	deallog << fe.get_name() << " embedding, RefinementCase<dim>:: " << refine_case_names[ref_case] << ", child " << c << std::endl;
	print_formatted(P[ref_case-1][c], 4, 6);
      }
}

template<int dim>
void test_projection (const FiniteElement<dim>& fe1,
		      const FiniteElement<dim>& fe2,
		      std::ostream& out)
{
  out << fe1.get_name() << " -> "
      << fe2.get_name() << std::endl;

  const unsigned int n1 = fe1.dofs_per_cell;
  const unsigned int n2 = fe2.dofs_per_cell;

  FullMatrix<double> P(n2,n1);

  FETools::get_projection_matrix(fe1, fe2, P);
  P.print_formatted(out, 3, false, 5);
}

template<int dim>
void test_projection (std::ostream& out)
{
  FE_DGQ<dim> q0(0);
  FE_DGQ<dim> q1(1);
  FE_DGQ<dim> q2(2);
  FE_DGQ<dim> q3(3);
  FE_DGQ<dim> q4(4);

  FE_DGP<dim> p0(0);
  FE_DGP<dim> p1(1);
  FE_DGP<dim> p2(2);
  FE_DGP<dim> p3(3);
  FE_DGP<dim> p4(4);

  test_embedding(q0);
  test_embedding(q1);
  test_embedding(q2);
  test_embedding(q3);
  test_embedding(p1);
  test_embedding(p2);
  test_embedding(p3);

  test_projection(p1,p0, out);
  test_projection(p0,p1, out);
  test_projection(p2,p1, out);
  test_projection(p1,p2, out);
  test_projection(p2,p0, out);
  test_projection(p0,p2, out);
  test_projection(p3,p2, out);
  test_projection(p2,p3, out);
  test_projection(p4,p3, out);
  test_projection(p3,p4, out);

  test_projection(q1,q0, out);
  test_projection(q2,q0, out);
  test_projection(q3,q0, out);
  test_projection(q4,q0, out);
  test_projection(q2,q1, out);
  test_projection(q1,q2, out);
  test_projection(q3,q2, out);
  test_projection(q4,q3, out);
}

int main()
{
  std::ofstream logfile("fe_tools/output");
  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog.threshold_double(1.e-8);

  test_projection<2>(logfile);
  test_projection<3>(logfile);
}
<commit_msg>Fix roundoff problems.<commit_after>//--------------------------------------------------------------------------
//    fe_tools.cc,v 1.1 2003/11/28 15:03:26 guido Exp
//    Version:
//
//    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2009 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//--------------------------------------------------------------------------

// Test the cell matrices generated in FETools and the local renumbering vector.

#include "../tests.h"
#include <iostream>
#include <iomanip>
#include <fstream>

#include <base/logstream.h>

#include <fe/fe_q.h>
#include <fe/fe_dgq.h>
#include <fe/fe_dgp.h>
#include <fe/fe_raviart_thomas.h>
#include <fe/fe_system.h>

#include <fe/fe_tools.h>

template <typename number>
void print_formatted (const FullMatrix<number> &A,
		      const unsigned int        precision,
		      const unsigned int        width)
{
  for (unsigned int i=0; i<A.m(); ++i)
    {
      for (unsigned int j=0; j<A.n(); ++j)
	{
	  if (A(i,j) != 0)
	    deallog << std::setw(width) << std::setprecision(precision)
		    << A(i,j);
	  else
	    deallog << std::setw(width) << std::setprecision(precision)
		    << "~";
	  deallog << ' ';
	};
      deallog << std::endl;
    };
}

template<int dim>
void test_embedding (const FiniteElement<dim>& fe)
{
  const std::string refine_case_names[8]=
    {"no_refinement",
     "cut_x",
     "cut_y",
     "cut_xy",
     "cut_z",
     "cut_xz",
     "cut_yz",
     "cut_xyz"};

  const unsigned int n = fe.dofs_per_cell;

  std::vector<std::vector<FullMatrix<double> > > P;
  P.resize(RefinementCase<dim>::isotropic_refinement);
  for (unsigned int ref_case=RefinementCase<dim>::cut_x;
       ref_case<RefinementCase<dim>::isotropic_refinement+1;
       ++ref_case)
    for (unsigned int c=0;
	 c<GeometryInfo<dim>::n_children(RefinementCase<dim>(ref_case));
	 ++c)
      P[ref_case-1].push_back(FullMatrix<double>(n));

  FETools::compute_embedding_matrices(fe, P);

  for (unsigned int ref_case=RefinementCase<dim>::cut_x;
       ref_case<RefinementCase<dim>::isotropic_refinement+1;
       ++ref_case)
    for (unsigned int c=0;
	 c<GeometryInfo<dim>::n_children(RefinementCase<dim>(ref_case));
	 ++c)
      {
	deallog << fe.get_name() << " embedding, RefinementCase<dim>:: " << refine_case_names[ref_case] << ", child " << c << std::endl;
	print_formatted(P[ref_case-1][c], 4, 6);
      }
}

template<int dim>
void test_projection (const FiniteElement<dim>& fe1,
		      const FiniteElement<dim>& fe2,
		      std::ostream& out)
{
  out << fe1.get_name() << " -> "
      << fe2.get_name() << std::endl;

  const unsigned int n1 = fe1.dofs_per_cell;
  const unsigned int n2 = fe2.dofs_per_cell;

  FullMatrix<double> P(n2,n1);

  FETools::get_projection_matrix(fe1, fe2, P);
  P.print_formatted(out, 3, false, 5);
}

template<int dim>
void test_projection (std::ostream& out)
{
  FE_DGQ<dim> q0(0);
  FE_DGQ<dim> q1(1);
  FE_DGQ<dim> q2(2);
  FE_DGQ<dim> q3(3);
  FE_DGQ<dim> q4(4);

  FE_DGP<dim> p0(0);
  FE_DGP<dim> p1(1);
  FE_DGP<dim> p2(2);
  FE_DGP<dim> p3(3);
  FE_DGP<dim> p4(4);

  test_embedding(q0);
  test_embedding(q1);
  test_embedding(q2);
  test_embedding(q3);
  test_embedding(p1);
  test_embedding(p2);
  test_embedding(p3);

  test_projection(p1,p0, out);
  test_projection(p0,p1, out);
  test_projection(p2,p1, out);
  test_projection(p1,p2, out);
  test_projection(p2,p0, out);
  test_projection(p0,p2, out);
  test_projection(p3,p2, out);
  test_projection(p2,p3, out);
  test_projection(p4,p3, out);
  test_projection(p3,p4, out);

  test_projection(q1,q0, out);
  test_projection(q2,q0, out);
  test_projection(q3,q0, out);
  test_projection(q4,q0, out);
  test_projection(q2,q1, out);
  test_projection(q1,q2, out);
  test_projection(q3,q2, out);
  test_projection(q4,q3, out);
}

int main()
{
  std::ofstream logfile("fe_tools/output");
  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog << std::setprecision(5);
  deallog.threshold_double(1.e-8);

  test_projection<2>(logfile);
  test_projection<3>(logfile);
}
<|endoftext|>


<commit_before>#include "cfg.h"
#include "hg.h"
#include "cfg_format.h"
#include "cfg_binarize.h"
#include "hash.h"
#include "batched_append.h"
#include <limits>
#include "fast_lexical_cast.hpp"

using namespace std;

namespace {
CFG::BinRhs nullrhs(std::numeric_limits<int>::min(),std::numeric_limits<int>::min());
}

WordID CFG::BinName(BinRhs const& b)
{
  return TD::Convert(lexical_cast<string>(b.first)+"+"+lexical_cast<string>(b.second));
}

void CFG::Binarize(CFGBinarize const& b) {
  if (!b.Binarizing()) return;
  if (!b.bin_l2r) {
    assert(b.bin_l2r);
    return;
  }
  // l2r only so far:
  cerr << "Binarizing "<<b<<endl;
  HASH_MAP<BinRhs,NTHandle,boost::hash<BinRhs> > bin2lhs; // we're going to hash cons rather than build an explicit trie from right to left.
  HASH_MAP_EMPTY(bin2lhs,nullrhs);
  int rhsmin=b.bin_unary?0:1;
  // iterate using indices and not iterators because we'll be adding to both nodes and edge list.  we could instead pessimistically reserve space for both, but this is simpler.  also: store original end of nts since we won't need to reprocess newly added ones.
  NTs new_nts; // these will be appended at the end, so we don't have to worry about iterator invalidation
  Rules new_rules;
  //TODO: this could be factored easily into in-place (append to new_* like below) and functional (nondestructive copy) versions (copy orig to target and append to target)
  int newnt=nts.size();
  int newruleid=rules.size();
  BinRhs bin;
  for (NTs::const_iterator n=nts.begin(),nn=nts.end();n!=nn;++n) {
    NT const& nt=*n;
    for (Ruleids::const_iterator ir=nt.ruleids.begin(),er=nt.ruleids.end();ir!=er;++ir) {
      RHS &rhs=rules[*ir].rhs; // we're going to binarize this while adding newly created rules to new_...
      if (rhs.empty()) continue;
      bin.second=rhs.back();
      for (int r=rhs.size()-2;r>=rhsmin;--r) { // pairs from right to left (normally we leave the last pair alone)
        rhs.pop_back();
        bin.first=rhs[r];
        if (newnt==(bin.second=(get_default(bin2lhs,bin,newnt)))) {
          new_nts.push_back(NT());
          new_nts.back().ruleids.push_back(newruleid);
          new_rules.push_back(Rule(newnt,bin));
          if (b.bin_name_nts)
            new_nts.back().from.nt=BinName(bin);
          ++newnt;++newruleid;
        }
      }
    }
  }
  batched_append_swap(nts,new_nts);
  batched_append_swap(rules,new_rules);
}

namespace {
inline int nt_index(int nvar,Hypergraph::TailNodeVector const& t,bool target_side,int w) {
  assert(w<0 || (target_side&&w==0));
  return t[target_side?-w:nvar];
}
}

void CFG::Init(Hypergraph const& hg,bool target_side,bool copy_features,bool push_weights) {
  uninit=false;
  hg_=&hg;
  Hypergraph::NodeProbs np;
  goal_inside=hg.ComputeNodeViterbi(&np);
  pushed_inside=push_weights ? goal_inside : prob_t(1);
  int nn=hg.nodes_.size(),ne=hg.edges_.size();
  nts.resize(nn);
  goal_nt=nn-1;
  rules.resize(ne);
  for (int i=0;i<nn;++i) {
    nts[i].ruleids=hg.nodes_[i].in_edges_;
    hg.SetNodeOrigin(i,nts[i].from);
  }
  for (int i=0;i<ne;++i) {
    Rule &cfgr=rules[i];
    Hypergraph::Edge const& e=hg.edges_[i];
    prob_t &crp=cfgr.p;
    crp=e.edge_prob_;
    cfgr.lhs=e.head_node_;
#if CFG_DEBUG
    cfgr.rule=e.rule_;
#endif
    if (copy_features) cfgr.f=e.feature_values_;
    if (push_weights) crp /=np[e.head_node_];
    TRule const& er=*e.rule_;
    vector<WordID> const& rule_rhs=target_side?er.e():er.f();
    int nr=rule_rhs.size();
    RHS &rhs_out=cfgr.rhs;
    rhs_out.resize(nr);
    Hypergraph::TailNodeVector const& tails=e.tail_nodes_;
    int nvar=0;
    //split out into separate target_side, source_side loops?
    for (int j=0;j<nr;++j) {
      WordID w=rule_rhs[j];
      if (w>0)
        rhs_out[j]=w;
      else {
        int n=nt_index(nvar,tails,target_side,w);
        ++nvar;
        if (push_weights) crp*=np[n];
        rhs_out[j]=-n;
      }
    }
    assert(nvar==er.Arity());
    assert(nvar==tails.size());
  }
}

void CFG::Clear() {
  rules.clear();
  nts.clear();
  goal_nt=-1;
  hg_=0;
}

void CFG::PrintRule(std::ostream &o,RuleHandle rulei,CFGFormat const& f) const {
  Rule const& r=rules[rulei];
  f.print_lhs(o,*this,r.lhs);
  f.print_rhs(o,*this,r.rhs.begin(),r.rhs.end());
  f.print_features(o,r.p,r.f);
#if CFG_DEBUG
  o<<f.partsep<<*r.rule;
#endif
}

void CFG::Print(std::ostream &o,CFGFormat const& f) const {
  assert(!uninit);
  if (!f.goal_nt_name.empty()) {
    o << '['<<f.goal_nt_name <<']';
    WordID rhs=-goal_nt;
    f.print_rhs(o,*this,&rhs,&rhs+1);
    if (pushed_inside!=1)
      f.print_features(o,pushed_inside);
    o<<'\n';
  }
  for (int i=0;i<nts.size();++i) {
    Ruleids const& ntr=nts[i].ruleids;
    for (Ruleids::const_iterator j=ntr.begin(),jj=ntr.end();j!=jj;++j) {
      PrintRule(o,*j,f);
      o<<'\n';
    }
  }
}
<commit_msg>do not print NULL binarized cfg TRule<commit_after>#include "cfg.h"
#include "hg.h"
#include "cfg_format.h"
#include "cfg_binarize.h"
#include "hash.h"
#include "batched_append.h"
#include <limits>
#include "fast_lexical_cast.hpp"

using namespace std;

namespace {
CFG::BinRhs nullrhs(std::numeric_limits<int>::min(),std::numeric_limits<int>::min());
}

WordID CFG::BinName(BinRhs const& b)
{
  return TD::Convert(lexical_cast<string>(b.first)+"+"+lexical_cast<string>(b.second));
}

void CFG::Binarize(CFGBinarize const& b) {
  if (!b.Binarizing()) return;
  if (!b.bin_l2r) {
    assert(b.bin_l2r);
    return;
  }
  // l2r only so far:
  cerr << "Binarizing "<<b<<endl;
  HASH_MAP<BinRhs,NTHandle,boost::hash<BinRhs> > bin2lhs; // we're going to hash cons rather than build an explicit trie from right to left.
  HASH_MAP_EMPTY(bin2lhs,nullrhs);
  int rhsmin=b.bin_unary?0:1;
  // iterate using indices and not iterators because we'll be adding to both nodes and edge list.  we could instead pessimistically reserve space for both, but this is simpler.  also: store original end of nts since we won't need to reprocess newly added ones.
  NTs new_nts; // these will be appended at the end, so we don't have to worry about iterator invalidation
  Rules new_rules;
  //TODO: this could be factored easily into in-place (append to new_* like below) and functional (nondestructive copy) versions (copy orig to target and append to target)
  int newnt=nts.size();
  int newruleid=rules.size();
  BinRhs bin;
  for (NTs::const_iterator n=nts.begin(),nn=nts.end();n!=nn;++n) {
    NT const& nt=*n;
    for (Ruleids::const_iterator ir=nt.ruleids.begin(),er=nt.ruleids.end();ir!=er;++ir) {
      RHS &rhs=rules[*ir].rhs; // we're going to binarize this while adding newly created rules to new_...
      if (rhs.empty()) continue;
      bin.second=rhs.back();
      for (int r=rhs.size()-2;r>=rhsmin;--r) { // pairs from right to left (normally we leave the last pair alone)
        rhs.pop_back();
        bin.first=rhs[r];
        if (newnt==(bin.second=(get_default(bin2lhs,bin,newnt)))) {
          new_nts.push_back(NT());
          new_nts.back().ruleids.push_back(newruleid);
          new_rules.push_back(Rule(newnt,bin));
          if (b.bin_name_nts)
            new_nts.back().from.nt=BinName(bin);
          ++newnt;++newruleid;
        }
      }
    }
  }
  batched_append_swap(nts,new_nts);
  batched_append_swap(rules,new_rules);
}

namespace {
inline int nt_index(int nvar,Hypergraph::TailNodeVector const& t,bool target_side,int w) {
  assert(w<0 || (target_side&&w==0));
  return t[target_side?-w:nvar];
}
}

void CFG::Init(Hypergraph const& hg,bool target_side,bool copy_features,bool push_weights) {
  uninit=false;
  hg_=&hg;
  Hypergraph::NodeProbs np;
  goal_inside=hg.ComputeNodeViterbi(&np);
  pushed_inside=push_weights ? goal_inside : prob_t(1);
  int nn=hg.nodes_.size(),ne=hg.edges_.size();
  nts.resize(nn);
  goal_nt=nn-1;
  rules.resize(ne);
  for (int i=0;i<nn;++i) {
    nts[i].ruleids=hg.nodes_[i].in_edges_;
    hg.SetNodeOrigin(i,nts[i].from);
  }
  for (int i=0;i<ne;++i) {
    Rule &cfgr=rules[i];
    Hypergraph::Edge const& e=hg.edges_[i];
    prob_t &crp=cfgr.p;
    crp=e.edge_prob_;
    cfgr.lhs=e.head_node_;
#if CFG_DEBUG
    cfgr.rule=e.rule_;
#endif
    if (copy_features) cfgr.f=e.feature_values_;
    if (push_weights) crp /=np[e.head_node_];
    TRule const& er=*e.rule_;
    vector<WordID> const& rule_rhs=target_side?er.e():er.f();
    int nr=rule_rhs.size();
    RHS &rhs_out=cfgr.rhs;
    rhs_out.resize(nr);
    Hypergraph::TailNodeVector const& tails=e.tail_nodes_;
    int nvar=0;
    //split out into separate target_side, source_side loops?
    for (int j=0;j<nr;++j) {
      WordID w=rule_rhs[j];
      if (w>0)
        rhs_out[j]=w;
      else {
        int n=nt_index(nvar,tails,target_side,w);
        ++nvar;
        if (push_weights) crp*=np[n];
        rhs_out[j]=-n;
      }
    }
    assert(nvar==er.Arity());
    assert(nvar==tails.size());
  }
}

void CFG::Clear() {
  rules.clear();
  nts.clear();
  goal_nt=-1;
  hg_=0;
}

void CFG::PrintRule(std::ostream &o,RuleHandle rulei,CFGFormat const& f) const {
  Rule const& r=rules[rulei];
  f.print_lhs(o,*this,r.lhs);
  f.print_rhs(o,*this,r.rhs.begin(),r.rhs.end());
  f.print_features(o,r.p,r.f);
#if CFG_DEBUG
  if (r.rule) o<<f.partsep<<*r.rule;
#endif
}

void CFG::Print(std::ostream &o,CFGFormat const& f) const {
  assert(!uninit);
  if (!f.goal_nt_name.empty()) {
    o << '['<<f.goal_nt_name <<']';
    WordID rhs=-goal_nt;
    f.print_rhs(o,*this,&rhs,&rhs+1);
    if (pushed_inside!=1)
      f.print_features(o,pushed_inside);
    o<<'\n';
  }
  for (int i=0;i<nts.size();++i) {
    Ruleids const& ntr=nts[i].ruleids;
    for (Ruleids::const_iterator j=ntr.begin(),jj=ntr.end();j!=jj;++j) {
      PrintRule(o,*j,f);
      o<<'\n';
    }
  }
}
<|endoftext|>


<commit_before>#include "path.h"

#ifdef _WIN32
#include <windows.h>
#elif __linux__
#include <limits.h>
#include <stdlib.h>
#else
#warning file::path is incompatible with target OS!
#endif

namespace z
{
	namespace file
	{
		core::string<utf8> path(const core::string<utf8>& filePath)
		{
#			ifdef _WIN32
			TCHAR fullPath[MAX_PATH];
			GetFullPathName(_T((const char*)filePath.cstring()), MAX_PATH, fullPath, NULL);
#			elif __linux__
			char fullPath[PATH_MAX];
			realpath((const char*)filePath.cstring(), fullPath);
#			else
			auto fullPath = filePath;
#			endif

			return fullPath;
		}
	}
}
<commit_msg>Fixed missing header for windows<commit_after>#include "path.h"

#ifdef _WIN32
#include <Tchar.h>
#include <windows.h>
#elif __linux__
#include <limits.h>
#include <stdlib.h>
#else
#warning file::path is incompatible with target OS!
#endif

namespace z
{
	namespace file
	{
		core::string<utf8> path(const core::string<utf8>& filePath)
		{
#			ifdef _WIN32
			TCHAR fullPath[MAX_PATH];
			GetFullPathName(_T((const char*)filePath.cstring()), MAX_PATH, fullPath, NULL);
#			elif __linux__
			char fullPath[PATH_MAX];
			realpath((const char*)filePath.cstring(), fullPath);
#			else
			auto fullPath = filePath;
#			endif

			return fullPath;
		}
	}
}
<|endoftext|>


<commit_before>#include <QGLWidget>
#include <QMatrix4x4>
#include <QVector3D>

#include <qmath.h>

#include "Logo.hpp"

static const qreal bar_thickness = 0.10;
static const qreal logo_depth = 0.10;
static const qreal nleg1_height = 0.55; //the left | in N
static const qreal nleg2_height = 0.58; //the right | in N
static const qreal nbar_height = 0.6;  //the \ in N
static const qreal tbar_width = 0.7; //the top of the T

struct Geometry
{
    QVector<GLushort> faces;
    QVector<QVector3D> vertices;
    QVector<QVector3D> normals;
    void appendSmooth(const QVector3D &a, const QVector3D &n, int from);
    void appendFaceted(const QVector3D &a, const QVector3D &n);
    void finalize();
    void loadArrays() const;
};

class Patch
{
public:
    enum Smoothing { Faceted, Smooth };
    Patch(Geometry *);
    void setSmoothing(Smoothing s) { sm = s; }
    void translate(const QVector3D &t);
    void rotate(qreal deg, QVector3D axis);
    void draw() const;
    void addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n);
    void addQuad(const QVector3D &a, const QVector3D &b,  const QVector3D &c, const QVector3D &d);

    GLushort start;
    GLushort count;
    GLushort initv;

    GLfloat faceColor[4];
    QMatrix4x4 mat;
    Smoothing sm;
    Geometry *geom;
};

static inline void qSetColor(float colorVec[], QColor c)
{
    colorVec[0] = c.redF();
    colorVec[1] = c.greenF();
    colorVec[2] = c.blueF();
    colorVec[3] = c.alphaF();
}

void Geometry::loadArrays() const
{
    glVertexPointer(3, GL_FLOAT, 0, vertices.constData());
    glNormalPointer(GL_FLOAT, 0, normals.constData());
}

void Geometry::finalize()
{
    // TODO: add vertex buffer uploading here

    // Finish smoothing normals by ensuring accumulated normals are returned
    // to length 1.0.
    for (int i = 0; i < normals.count(); ++i)
        normals[i].normalize();
}

void Geometry::appendSmooth(const QVector3D &a, const QVector3D &n, int from)
{
    // Smooth normals are achieved by averaging the normals for faces meeting
    // at a point.  First find the point in geometry already generated
    // (working backwards, since most often the points shared are between faces
    // recently added).
    int v = vertices.count() - 1;
    for ( ; v >= from; --v)
        if (qFuzzyCompare(vertices[v], a))
            break;
    if (v < from)
    {
        // The vert was not found so add it as a new one, and initialize
        // its corresponding normal
        v = vertices.count();
        vertices.append(a);
        normals.append(n);
    }
    else
    {
        // Vert found, accumulate normals into corresponding normal slot.
        // Must call finalize once finished accumulating normals
        normals[v] += n;
    }
    // In both cases (found or not) reference the vert via its index
    faces.append(v);
}

void Geometry::appendFaceted(const QVector3D &a, const QVector3D &n)
{
    // Faceted normals are achieved by duplicating the vert for every
    // normal, so that faces meeting at a vert get a sharp edge.
    int v = vertices.count();
    vertices.append(a);
    normals.append(n);
    faces.append(v);
}

Patch::Patch(Geometry *g)
   : start(g->faces.count())
   , count(0)
   , initv(g->vertices.count())
   , sm(Patch::Smooth)
   , geom(g)
{
    qSetColor(faceColor, QColor(Qt::darkGray));
}

void Patch::rotate(qreal deg, QVector3D axis)
{
    mat.rotate(deg, axis);
}

void Patch::translate(const QVector3D &t)
{
    mat.translate(t);
}

static inline void qMultMatrix(const QMatrix4x4 &mat)
{
    if (sizeof(qreal) == sizeof(GLfloat))
        glMultMatrixf((GLfloat*)mat.constData());
#ifndef QT_OPENGL_ES
    else if (sizeof(qreal) == sizeof(GLdouble))
        glMultMatrixd((GLdouble*)mat.constData());
#endif
    else
    {
        GLfloat fmat[16];
        qreal const *r = mat.constData();
        for (int i = 0; i < 16; ++i)
            fmat[i] = r[i];
        glMultMatrixf(fmat);
    }
}

void Patch::draw() const
{
    glPushMatrix();
    qMultMatrix(mat);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, faceColor);

    const GLushort *indices = geom->faces.constData();
    glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, indices + start);
    glPopMatrix();
}

void Patch::addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n)
{
    QVector3D norm = n.isNull() ? QVector3D::normal(a, b, c) : n;
    if (sm == Smooth)
    {
        geom->appendSmooth(a, norm, initv);
        geom->appendSmooth(b, norm, initv);
        geom->appendSmooth(c, norm, initv);
    }
    else
    {
        geom->appendFaceted(a, norm);
        geom->appendFaceted(b, norm);
        geom->appendFaceted(c, norm);
    }
    count += 3;
}

void Patch::addQuad(const QVector3D &a, const QVector3D &b,  const QVector3D &c, const QVector3D &d)
{
    QVector3D norm = QVector3D::normal(a, b, c);
    if (sm == Smooth)
    {
        addTri(a, b, c, norm);
        addTri(a, c, d, norm);
    }
    else
    {
        // If faceted share the two common verts
        addTri(a, b, c, norm);
        int k = geom->vertices.count();
        geom->appendSmooth(a, norm, k);
        geom->appendSmooth(c, norm, k);
        geom->appendFaceted(d, norm);
        count += 3;
    }
}

static inline QVector<QVector3D> extrude(const QVector<QVector3D> &verts, qreal depth)
{
    QVector<QVector3D> extr = verts;
    for (int v = 0; v < extr.count(); ++v)
        extr[v].setZ(extr[v].z() - depth);
    return extr;
}

class Rectoid
{
public:
    void translate(const QVector3D &t)
    {
        for (int i = 0; i < parts.count(); ++i)
            parts[i]->translate(t);
    }
    void rotate(qreal deg, QVector3D axis)
    {
        for (int i = 0; i < parts.count(); ++i)
            parts[i]->rotate(deg, axis);
    }

    // No special Rectoid destructor - the parts are fetched out of this member
    // variable, and destroyed by the new owner
    QList<Patch*> parts;
};

class RectPrism : public Rectoid
{
public:
    RectPrism(Geometry *g, qreal width, qreal height, qreal depth);
};

RectPrism::RectPrism(Geometry *g, qreal width, qreal height, qreal depth)
{
    enum { bl, br, tr, tl };
    Patch *fb = new Patch(g);
    fb->setSmoothing(Patch::Faceted);

    // front face
    QVector<QVector3D> r(4);
    r[br].setX(width);
    r[tr].setX(width);
    r[tr].setY(height);
    r[tl].setY(height);
    QVector3D adjToCenter(-width / 2.0, -height / 2.0, depth / 2.0);
    for (int i = 0; i < 4; ++i)
        r[i] += adjToCenter;
    fb->addQuad(r[bl], r[br], r[tr], r[tl]);

    // back face
    QVector<QVector3D> s = extrude(r, depth);
    fb->addQuad(s[tl], s[tr], s[br], s[bl]);

    // side faces
    Patch *sides = new Patch(g);
    sides->setSmoothing(Patch::Faceted);
    sides->addQuad(s[bl], s[br], r[br], r[bl]);
    sides->addQuad(s[br], s[tr], r[tr], r[br]);
    sides->addQuad(s[tr], s[tl], r[tl], r[tr]);
    sides->addQuad(s[tl], s[bl], r[bl], r[tl]);

    parts << fb << sides;
}

class RectTorus : public Rectoid
{
public:
    RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int numSectors);
};

RectTorus::RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int k)
{
    QVector<QVector3D> inside;
    QVector<QVector3D> outside;
    for (int i = 0; i < k; ++i) {
        qreal angle = (i * 2 * M_PI) / k;
        inside << QVector3D(iRad * qSin(angle), iRad * qCos(angle), depth / 2.0);
        outside << QVector3D(oRad * qSin(angle), oRad * qCos(angle), depth / 2.0);
    }
    inside << QVector3D(0.0, iRad, 0.0);
    outside << QVector3D(0.0, oRad, 0.0);
    QVector<QVector3D> in_back = extrude(inside, depth);
    QVector<QVector3D> out_back = extrude(outside, depth);

    // Create front, back and sides as separate patches so that smooth normals
    // are generated for the curving sides, but a faceted edge is created between
    // sides and front/back
    Patch *front = new Patch(g);
    for (int i = 0; i < k; ++i)
        front->addQuad(outside[i], inside[i],
                       inside[(i + 1) % k], outside[(i + 1) % k]);
    Patch *back = new Patch(g);
    for (int i = 0; i < k; ++i)
        back->addQuad(in_back[i], out_back[i],
                      out_back[(i + 1) % k], in_back[(i + 1) % k]);
    Patch *is = new Patch(g);
    for (int i = 0; i < k; ++i)
        is->addQuad(in_back[i], in_back[(i + 1) % k],
                    inside[(i + 1) % k], inside[i]);
    Patch *os = new Patch(g);
    for (int i = 0; i < k; ++i)
        os->addQuad(out_back[(i + 1) % k], out_back[i],
                    outside[i], outside[(i + 1) % k]);
    parts << front << back << is << os;
}

Logo::Logo(QObject *parent, qreal scale)
    : QObject(parent)
    , geom(new Geometry())
{
    buildGeometry(scale);
}

Logo::~Logo()
{
    qDeleteAll(parts);
    delete geom;
}

void Logo::setColor(QColor c)
{
    for (int i = 0; i < parts.count(); ++i)
        qSetColor(parts[i]->faceColor, c);
}

void Logo::buildGeometry(qreal scale)
{
    qreal ld = logo_depth * scale;
    qreal bt = bar_thickness * scale;
    qreal n1h = nleg1_height * scale;
    qreal n2h = nleg2_height * scale;
    qreal nb = nbar_height * scale;
    qreal tb = tbar_width * scale;

    QVector3D z(0.0, 0.0, 1.0);
    //qreal stem_downshift = (th + bt) / 2.0;
    //stem.translate(QVector3D(0.0, -stem_downshift, 0.0));
    RectPrism leg1(geom, bt, n1h, ld);
    RectPrism leg2(geom, bt, n2h, ld);
    RectPrism nbar(geom, bt, nb, ld);
    RectPrism tbar(geom, tb, bt, ld);
    double rnbar = 35;
    double partsciencepartguess = sin(rnbar*3.14/180)*nb*0.5-0.05;

    leg1.translate(QVector3D(-0.24,0.0,0.0));
    leg2.translate(QVector3D(partsciencepartguess,(n2h-n1h)/2,0.0));
    nbar.rotate(rnbar,z);
    nbar.translate(QVector3D(-.05,0.035,0.0));

    tbar.translate(QVector3D(partsciencepartguess,(n2h+bt)/2 , 0.0));

    parts << leg1.parts << leg2.parts << nbar.parts << tbar.parts;

    geom->finalize();
}

void Logo::draw() const
{
    geom->loadArrays();

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    for (int i = 0; i < parts.count(); ++i)
        parts[i]->draw();

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
}

// vim: set ts=4 sw=4 et! :
<commit_msg>Use float for constData, not qreal<commit_after>#include <QGLWidget>
#include <QMatrix4x4>
#include <QVector3D>

#include <qmath.h>

#include "Logo.hpp"

static const qreal bar_thickness = 0.10;
static const qreal logo_depth = 0.10;
static const qreal nleg1_height = 0.55; //the left | in N
static const qreal nleg2_height = 0.58; //the right | in N
static const qreal nbar_height = 0.6;  //the \ in N
static const qreal tbar_width = 0.7; //the top of the T

struct Geometry
{
    QVector<GLushort> faces;
    QVector<QVector3D> vertices;
    QVector<QVector3D> normals;
    void appendSmooth(const QVector3D &a, const QVector3D &n, int from);
    void appendFaceted(const QVector3D &a, const QVector3D &n);
    void finalize();
    void loadArrays() const;
};

class Patch
{
public:
    enum Smoothing { Faceted, Smooth };
    Patch(Geometry *);
    void setSmoothing(Smoothing s) { sm = s; }
    void translate(const QVector3D &t);
    void rotate(qreal deg, QVector3D axis);
    void draw() const;
    void addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n);
    void addQuad(const QVector3D &a, const QVector3D &b,  const QVector3D &c, const QVector3D &d);

    GLushort start;
    GLushort count;
    GLushort initv;

    GLfloat faceColor[4];
    QMatrix4x4 mat;
    Smoothing sm;
    Geometry *geom;
};

static inline void qSetColor(float colorVec[], QColor c)
{
    colorVec[0] = c.redF();
    colorVec[1] = c.greenF();
    colorVec[2] = c.blueF();
    colorVec[3] = c.alphaF();
}

void Geometry::loadArrays() const
{
    glVertexPointer(3, GL_FLOAT, 0, vertices.constData());
    glNormalPointer(GL_FLOAT, 0, normals.constData());
}

void Geometry::finalize()
{
    // TODO: add vertex buffer uploading here

    // Finish smoothing normals by ensuring accumulated normals are returned
    // to length 1.0.
    for (int i = 0; i < normals.count(); ++i)
        normals[i].normalize();
}

void Geometry::appendSmooth(const QVector3D &a, const QVector3D &n, int from)
{
    // Smooth normals are achieved by averaging the normals for faces meeting
    // at a point.  First find the point in geometry already generated
    // (working backwards, since most often the points shared are between faces
    // recently added).
    int v = vertices.count() - 1;
    for ( ; v >= from; --v)
        if (qFuzzyCompare(vertices[v], a))
            break;
    if (v < from)
    {
        // The vert was not found so add it as a new one, and initialize
        // its corresponding normal
        v = vertices.count();
        vertices.append(a);
        normals.append(n);
    }
    else
    {
        // Vert found, accumulate normals into corresponding normal slot.
        // Must call finalize once finished accumulating normals
        normals[v] += n;
    }
    // In both cases (found or not) reference the vert via its index
    faces.append(v);
}

void Geometry::appendFaceted(const QVector3D &a, const QVector3D &n)
{
    // Faceted normals are achieved by duplicating the vert for every
    // normal, so that faces meeting at a vert get a sharp edge.
    int v = vertices.count();
    vertices.append(a);
    normals.append(n);
    faces.append(v);
}

Patch::Patch(Geometry *g)
   : start(g->faces.count())
   , count(0)
   , initv(g->vertices.count())
   , sm(Patch::Smooth)
   , geom(g)
{
    qSetColor(faceColor, QColor(Qt::darkGray));
}

void Patch::rotate(qreal deg, QVector3D axis)
{
    mat.rotate(deg, axis);
}

void Patch::translate(const QVector3D &t)
{
    mat.translate(t);
}

static inline void qMultMatrix(const QMatrix4x4 &mat)
{
    if (sizeof(qreal) == sizeof(GLfloat))
        glMultMatrixf((GLfloat*)mat.constData());
#ifndef QT_OPENGL_ES
    else if (sizeof(qreal) == sizeof(GLdouble))
        glMultMatrixd((GLdouble*)mat.constData());
#endif
    else
    {
        GLfloat fmat[16];
        float const *r = mat.constData();
        for (int i = 0; i < 16; ++i)
            fmat[i] = r[i];
        glMultMatrixf(fmat);
    }
}

void Patch::draw() const
{
    glPushMatrix();
    qMultMatrix(mat);
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, faceColor);

    const GLushort *indices = geom->faces.constData();
    glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, indices + start);
    glPopMatrix();
}

void Patch::addTri(const QVector3D &a, const QVector3D &b, const QVector3D &c, const QVector3D &n)
{
    QVector3D norm = n.isNull() ? QVector3D::normal(a, b, c) : n;
    if (sm == Smooth)
    {
        geom->appendSmooth(a, norm, initv);
        geom->appendSmooth(b, norm, initv);
        geom->appendSmooth(c, norm, initv);
    }
    else
    {
        geom->appendFaceted(a, norm);
        geom->appendFaceted(b, norm);
        geom->appendFaceted(c, norm);
    }
    count += 3;
}

void Patch::addQuad(const QVector3D &a, const QVector3D &b,  const QVector3D &c, const QVector3D &d)
{
    QVector3D norm = QVector3D::normal(a, b, c);
    if (sm == Smooth)
    {
        addTri(a, b, c, norm);
        addTri(a, c, d, norm);
    }
    else
    {
        // If faceted share the two common verts
        addTri(a, b, c, norm);
        int k = geom->vertices.count();
        geom->appendSmooth(a, norm, k);
        geom->appendSmooth(c, norm, k);
        geom->appendFaceted(d, norm);
        count += 3;
    }
}

static inline QVector<QVector3D> extrude(const QVector<QVector3D> &verts, qreal depth)
{
    QVector<QVector3D> extr = verts;
    for (int v = 0; v < extr.count(); ++v)
        extr[v].setZ(extr[v].z() - depth);
    return extr;
}

class Rectoid
{
public:
    void translate(const QVector3D &t)
    {
        for (int i = 0; i < parts.count(); ++i)
            parts[i]->translate(t);
    }
    void rotate(qreal deg, QVector3D axis)
    {
        for (int i = 0; i < parts.count(); ++i)
            parts[i]->rotate(deg, axis);
    }

    // No special Rectoid destructor - the parts are fetched out of this member
    // variable, and destroyed by the new owner
    QList<Patch*> parts;
};

class RectPrism : public Rectoid
{
public:
    RectPrism(Geometry *g, qreal width, qreal height, qreal depth);
};

RectPrism::RectPrism(Geometry *g, qreal width, qreal height, qreal depth)
{
    enum { bl, br, tr, tl };
    Patch *fb = new Patch(g);
    fb->setSmoothing(Patch::Faceted);

    // front face
    QVector<QVector3D> r(4);
    r[br].setX(width);
    r[tr].setX(width);
    r[tr].setY(height);
    r[tl].setY(height);
    QVector3D adjToCenter(-width / 2.0, -height / 2.0, depth / 2.0);
    for (int i = 0; i < 4; ++i)
        r[i] += adjToCenter;
    fb->addQuad(r[bl], r[br], r[tr], r[tl]);

    // back face
    QVector<QVector3D> s = extrude(r, depth);
    fb->addQuad(s[tl], s[tr], s[br], s[bl]);

    // side faces
    Patch *sides = new Patch(g);
    sides->setSmoothing(Patch::Faceted);
    sides->addQuad(s[bl], s[br], r[br], r[bl]);
    sides->addQuad(s[br], s[tr], r[tr], r[br]);
    sides->addQuad(s[tr], s[tl], r[tl], r[tr]);
    sides->addQuad(s[tl], s[bl], r[bl], r[tl]);

    parts << fb << sides;
}

class RectTorus : public Rectoid
{
public:
    RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int numSectors);
};

RectTorus::RectTorus(Geometry *g, qreal iRad, qreal oRad, qreal depth, int k)
{
    QVector<QVector3D> inside;
    QVector<QVector3D> outside;
    for (int i = 0; i < k; ++i) {
        qreal angle = (i * 2 * M_PI) / k;
        inside << QVector3D(iRad * qSin(angle), iRad * qCos(angle), depth / 2.0);
        outside << QVector3D(oRad * qSin(angle), oRad * qCos(angle), depth / 2.0);
    }
    inside << QVector3D(0.0, iRad, 0.0);
    outside << QVector3D(0.0, oRad, 0.0);
    QVector<QVector3D> in_back = extrude(inside, depth);
    QVector<QVector3D> out_back = extrude(outside, depth);

    // Create front, back and sides as separate patches so that smooth normals
    // are generated for the curving sides, but a faceted edge is created between
    // sides and front/back
    Patch *front = new Patch(g);
    for (int i = 0; i < k; ++i)
        front->addQuad(outside[i], inside[i],
                       inside[(i + 1) % k], outside[(i + 1) % k]);
    Patch *back = new Patch(g);
    for (int i = 0; i < k; ++i)
        back->addQuad(in_back[i], out_back[i],
                      out_back[(i + 1) % k], in_back[(i + 1) % k]);
    Patch *is = new Patch(g);
    for (int i = 0; i < k; ++i)
        is->addQuad(in_back[i], in_back[(i + 1) % k],
                    inside[(i + 1) % k], inside[i]);
    Patch *os = new Patch(g);
    for (int i = 0; i < k; ++i)
        os->addQuad(out_back[(i + 1) % k], out_back[i],
                    outside[i], outside[(i + 1) % k]);
    parts << front << back << is << os;
}

Logo::Logo(QObject *parent, qreal scale)
    : QObject(parent)
    , geom(new Geometry())
{
    buildGeometry(scale);
}

Logo::~Logo()
{
    qDeleteAll(parts);
    delete geom;
}

void Logo::setColor(QColor c)
{
    for (int i = 0; i < parts.count(); ++i)
        qSetColor(parts[i]->faceColor, c);
}

void Logo::buildGeometry(qreal scale)
{
    qreal ld = logo_depth * scale;
    qreal bt = bar_thickness * scale;
    qreal n1h = nleg1_height * scale;
    qreal n2h = nleg2_height * scale;
    qreal nb = nbar_height * scale;
    qreal tb = tbar_width * scale;

    QVector3D z(0.0, 0.0, 1.0);
    //qreal stem_downshift = (th + bt) / 2.0;
    //stem.translate(QVector3D(0.0, -stem_downshift, 0.0));
    RectPrism leg1(geom, bt, n1h, ld);
    RectPrism leg2(geom, bt, n2h, ld);
    RectPrism nbar(geom, bt, nb, ld);
    RectPrism tbar(geom, tb, bt, ld);
    double rnbar = 35;
    double partsciencepartguess = sin(rnbar*3.14/180)*nb*0.5-0.05;

    leg1.translate(QVector3D(-0.24,0.0,0.0));
    leg2.translate(QVector3D(partsciencepartguess,(n2h-n1h)/2,0.0));
    nbar.rotate(rnbar,z);
    nbar.translate(QVector3D(-.05,0.035,0.0));

    tbar.translate(QVector3D(partsciencepartguess,(n2h+bt)/2 , 0.0));

    parts << leg1.parts << leg2.parts << nbar.parts << tbar.parts;

    geom->finalize();
}

void Logo::draw() const
{
    geom->loadArrays();

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_NORMAL_ARRAY);

    for (int i = 0; i < parts.count(); ++i)
        parts[i]->draw();

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
}

// vim: set ts=4 sw=4 et! :
<|endoftext|>