<commit_before>#include <string>
#include <map>
#include "lexer.h"

using namespace std;

ostream &operator<<(ostream &out, Location loc) {
    out << "(" << loc.line << ", " << loc.column << ")";
    return out;
}

map<string, Token::Kind> str_to_tok = {
    {"MODULE",      Token::MODULE},
    {"IMPORT",      Token::IMPORT},
    {"BEGIN",       Token::BEGIN},
    {"END",         Token::END},
    {"EXTERN",      Token::EXTERN},
    {"PROCEDURE",   Token::PROCEDURE},
    {"EXIT",        Token::EXIT},
    {"RETURN",      Token::RETURN},
    {"VAR",         Token::VAR},
    {"CONST",       Token::CONST},
    {"TYPE",        Token::TYPE},
    {"NIL",         Token::NIL},
    {"ARRAY",       Token::ARRAY},
    {"RECORD",      Token::RECORD},
    {"POINTER",     Token::POINTER},
    {"OF",          Token::OF},
    {"TO",          Token::TO},
    {"IF",          Token::IF},
    {"THEN",        Token::THEN},
    {"ELSIF",       Token::ELSIF},
    {"ELSE",        Token::ELSE},
    {"CASE",        Token::CASE},
    {"WITH",        Token::WITH},
    {"REPEAT",      Token::REPEAT},
    {"UNTIL",       Token::UNTIL},
    {"WHILE",       Token::WHILE},
    {"DO",          Token::DO},
    {"FOR",         Token::FOR},
    {"BY",          Token::BY},
    {"LOOP",        Token::LOOP}
};

map<Token::Kind, string> tok_to_str = {
    {Token::INTLITERAL,     "INTLITERAL"},
    {Token::FLOATLITERAL,   "FLOATLITERAL"},
    {Token::STRLITERAL,     "STRLITERAL"},
    {Token::CHARLITERAL,    "CHARLITERAL"},
    {Token::IDENTIFIER,     "IDENTIFIER"},
    {Token::RELATION,       "RELATION"},
    {Token::OPERATOR,       "OPERATOR"},
    {Token::DOT,            "DOT"},
    {Token::RANGE,          "RANGE"},
    {Token::COMMA,          "COMMA"},
    {Token::COLON,          "COLON"},
    {Token::SEMICOLON,      "SEMICOLON"},
    {Token::ASSIGNMENT,     "ASSIGNMENT"},
    {Token::PIPE,           "PIPE"},
    {Token::CARET,          "CARET"},
    {Token::LPAREN,         "LPAREN"},
    {Token::RPAREN,         "RPAREN"},
    {Token::LCURLY,         "LCURLY"},
    {Token::RCURLY,         "RCURLY"},
    {Token::LSQUARE,        "LSQUARE"},
    {Token::RSQUARE,        "RSQUARE"},
    {Token::END_OF_FILE,    "END_OF_FILE"}
};

string kind_to_string(Token::Kind kind) {
    for (auto &entry : str_to_tok) {
        if (entry.second == kind) {
            return entry.first;
        }
    }
    return tok_to_str[kind];
}

Token::Token(Kind _kind, Location _loc, string _text) : kind(_kind), loc(_loc), text(_text) {
    if (_kind == IDENTIFIER) {
        // uppercase each character in-place
        for (auto &c : _text) {
            c = toupper(c);
        }
        for (auto &entry : str_to_tok) {
            if (entry.first == _text) {
                kind = entry.second;
                return;
            }
        }
        if (_text == "OR" || _text == "DIV" || _text == "MOD") {
            kind = OPERATOR;
            text = _text;
        } else if (_text == "IN" || _text == "IS") {
            kind = RELATION;
            text = _text;
        } else if (_text == "FALSE") {
            kind = BOOLLITERAL;
            boolval = false;
        } else if (_text == "TRUE") {
            kind = BOOLLITERAL;
            boolval = true;
        }
    }
}

void Lexer::take(void) {
    if (lastChar == '\n') {
        loc.line++;
        loc.column = 1;
    } else {
        loc.column++;
    }
    lastChar = in->get();
}

static bool isoper(char c) {
    switch (c) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '&':
    case '~':
    case '\\':
        return true;
    default:
        return false;
    }
}

static bool isrelation(char c) {
    switch (c) {
    case '<':
    case '>':
    case '#':
    case '=':
        return true;
    default:
        return false;
    }
}

shared_ptr<Token> Lexer::nextToken() {
    while (isspace(lastChar)) {
        take();
    }

    if (lastChar == EOF) {
        return make_shared<Token>(Token::END_OF_FILE, loc);
    }

    if (isalpha(lastChar)) {
        string text;
        do {
            text.push_back(lastChar);
            take();
        } while (isalnum(lastChar));
        return make_shared<Token>(Token::IDENTIFIER, loc, text);
    } else if (isdigit(lastChar)) {
        string numStr;
        bool hasDot;
        do {
            if (lastChar == '.') hasDot = true;
            numStr.push_back(lastChar);
            take();
        } while (isdigit(lastChar) || (lastChar == '.' && !hasDot));
        if (hasDot) {
            return make_shared<Token>(Token::FLOATLITERAL, loc, stof(numStr.c_str()));
        } else {
            return make_shared<Token>(Token::INTLITERAL, loc, stoi(numStr.c_str()));
        }
    } else if (isoper(lastChar)) {
        string opStr;
        do {
            opStr.push_back(lastChar);
            take();
        } while (isoper(lastChar));
        return make_shared<Token>(Token::OPERATOR, loc, opStr);
    } else if (isrelation(lastChar)) {
        string relStr;
        do {
            relStr.push_back(lastChar);
            take();
        } while (isrelation(lastChar));
        return make_shared<Token>(Token::RELATION, loc, relStr);
    } else {
        switch (lastChar) {
            case ',':
                take();
                return make_shared<Token>(Token::COMMA, loc);
            case '~':
                take();
                return make_shared<Token>(Token::TILDE, loc);
            case '^':
                take();
                return make_shared<Token>(Token::CARET, loc);
            case '|':
                take();
                return make_shared<Token>(Token::PIPE, loc);
            case ';':
                take();
                return make_shared<Token>(Token::SEMICOLON, loc);
            case ':':
                take();
                if (lastChar == '=') {
                    take();
                    return make_shared<Token>(Token::ASSIGNMENT, loc);
                } else {
                    return make_shared<Token>(Token::COLON, loc);
                }
            case '"': {
                take(); // open "
                string str;
                while (lastChar != '"') {
                    str.push_back(lastChar);
                    take();
                }
                take(); // close "
                if (str.length() == 1) {
                    return make_shared<Token>(Token::CHARLITERAL, loc, str[0]);
                } else {
                    return make_shared<Token>(Token::STRLITERAL, loc, str);
                }
            }
            case '\'': {
                take(); // open '
                string str;
                while (lastChar != '\'') {
                    str.push_back(lastChar);
                    take();
                }
                take(); // close '
                if (str.length() == 1) {
                    return make_shared<Token>(Token::CHARLITERAL, loc, str[0]);
                } else {
                    return make_shared<Token>(Token::STRLITERAL, loc, str);
                }
            }
            case '(': {
                take(); // open (
                // it's a comment
                if (lastChar == '*') {
                    char beforeLast = 0;
                    int depth = 1;
                    while(1) {
                        take();
                        if (beforeLast == '(' && lastChar == '*') {
                            depth++;
                        } else if (beforeLast == '*' && lastChar == ')') {
                            if (depth == 1) break;
                            depth--;
                        }
                        beforeLast = lastChar;
                    }
                    take(); // close )
                    return nextToken();
                } else {
                    return make_shared<Token>(Token::LPAREN, loc);
                }
            }
            case ')':
                take();
                return make_shared<Token>(Token::RPAREN, loc);
            case '{':
                take();
                return make_shared<Token>(Token::LCURLY, loc);
            case '}':
                take();
                return make_shared<Token>(Token::RCURLY, loc);
            case '[':
                take();
                return make_shared<Token>(Token::LSQUARE, loc);
            case ']':
                take();
                return make_shared<Token>(Token::RSQUARE, loc);
            case '.':
                take();
                if (lastChar == '.') {
                    take();
                    return make_shared<Token>(Token::RANGE, loc);
                } else {
                    return make_shared<Token>(Token::DOT, loc);
                }
            default:
                take();
        }
    }

    return make_shared<Token>(Token::OTHER, loc);
}
<commit_msg>hex-digits and scaled-reals<commit_after>#include <string>
#include <map>
#include "lexer.h"

using namespace std;

ostream &operator<<(ostream &out, Location loc) {
    out << "(" << loc.line << ", " << loc.column << ")";
    return out;
}

map<string, Token::Kind> str_to_tok = {
    {"MODULE",      Token::MODULE},
    {"IMPORT",      Token::IMPORT},
    {"BEGIN",       Token::BEGIN},
    {"END",         Token::END},
    {"EXTERN",      Token::EXTERN},
    {"PROCEDURE",   Token::PROCEDURE},
    {"EXIT",        Token::EXIT},
    {"RETURN",      Token::RETURN},
    {"VAR",         Token::VAR},
    {"CONST",       Token::CONST},
    {"TYPE",        Token::TYPE},
    {"NIL",         Token::NIL},
    {"ARRAY",       Token::ARRAY},
    {"RECORD",      Token::RECORD},
    {"POINTER",     Token::POINTER},
    {"OF",          Token::OF},
    {"TO",          Token::TO},
    {"IF",          Token::IF},
    {"THEN",        Token::THEN},
    {"ELSIF",       Token::ELSIF},
    {"ELSE",        Token::ELSE},
    {"CASE",        Token::CASE},
    {"WITH",        Token::WITH},
    {"REPEAT",      Token::REPEAT},
    {"UNTIL",       Token::UNTIL},
    {"WHILE",       Token::WHILE},
    {"DO",          Token::DO},
    {"FOR",         Token::FOR},
    {"BY",          Token::BY},
    {"LOOP",        Token::LOOP}
};

map<Token::Kind, string> tok_to_str = {
    {Token::INTLITERAL,     "INTLITERAL"},
    {Token::FLOATLITERAL,   "FLOATLITERAL"},
    {Token::STRLITERAL,     "STRLITERAL"},
    {Token::CHARLITERAL,    "CHARLITERAL"},
    {Token::IDENTIFIER,     "IDENTIFIER"},
    {Token::RELATION,       "RELATION"},
    {Token::OPERATOR,       "OPERATOR"},
    {Token::DOT,            "DOT"},
    {Token::RANGE,          "RANGE"},
    {Token::COMMA,          "COMMA"},
    {Token::COLON,          "COLON"},
    {Token::SEMICOLON,      "SEMICOLON"},
    {Token::ASSIGNMENT,     "ASSIGNMENT"},
    {Token::PIPE,           "PIPE"},
    {Token::CARET,          "CARET"},
    {Token::LPAREN,         "LPAREN"},
    {Token::RPAREN,         "RPAREN"},
    {Token::LCURLY,         "LCURLY"},
    {Token::RCURLY,         "RCURLY"},
    {Token::LSQUARE,        "LSQUARE"},
    {Token::RSQUARE,        "RSQUARE"},
    {Token::END_OF_FILE,    "END_OF_FILE"}
};

string kind_to_string(Token::Kind kind) {
    for (auto &entry : str_to_tok) {
        if (entry.second == kind) {
            return entry.first;
        }
    }
    return tok_to_str[kind];
}

Token::Token(Kind _kind, Location _loc, string _text) : kind(_kind), loc(_loc), text(_text) {
    if (_kind == IDENTIFIER) {
        // uppercase each character in-place
        for (auto &c : _text) {
            c = toupper(c);
        }
        for (auto &entry : str_to_tok) {
            if (entry.first == _text) {
                kind = entry.second;
                return;
            }
        }
        if (_text == "OR" || _text == "DIV" || _text == "MOD") {
            kind = OPERATOR;
            text = _text;
        } else if (_text == "IN" || _text == "IS") {
            kind = RELATION;
            text = _text;
        } else if (_text == "FALSE") {
            kind = BOOLLITERAL;
            boolval = false;
        } else if (_text == "TRUE") {
            kind = BOOLLITERAL;
            boolval = true;
        }
    }
}

void Lexer::take(void) {
    if (lastChar == '\n') {
        loc.line++;
        loc.column = 1;
    } else {
        loc.column++;
    }
    lastChar = in->get();
}

static bool isoper(char c) {
    switch (c) {
    case '+':
    case '-':
    case '*':
    case '/':
    case '&':
    case '~':
    case '\\':
        return true;
    default:
        return false;
    }
}

static bool isrelation(char c) {
    switch (c) {
    case '<':
    case '>':
    case '#':
    case '=':
        return true;
    default:
        return false;
    }
}

shared_ptr<Token> Lexer::nextToken() {
    while (isspace(lastChar)) {
        take();
    }

    if (lastChar == EOF) {
        return make_shared<Token>(Token::END_OF_FILE, loc);
    }

    if (isalpha(lastChar)) {
        string text;
        do {
            text.push_back(lastChar);
            take();
        } while (isalnum(lastChar));
        return make_shared<Token>(Token::IDENTIFIER, loc, text);
    } else if (isdigit(lastChar)) {
        string numStr;
        bool isReal = false, isHex = false, isScaled = false, isSigned = false, isDone = false;
        while (!isDone) {
            switch (lastChar) {
            case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
                numStr.push_back(lastChar);
                take();
                break;
            case 'A': case 'B': case 'C': case 'F':
                isHex = true;
                numStr.push_back(lastChar);
                take();
                break;
            case 'D': case 'E':
                if (isScaled) {
                    isDone = true;
                } else if (isReal) {
                    isScaled = true;
                    numStr.push_back(lastChar);
                    take();
                } else {
                    isHex = true;
                    numStr.push_back(lastChar);
                    take();
                }
                break;
            case '+': case '-':
                if (isSigned) {
                    isDone = true;
                } else if (isScaled) {
                    isSigned = true;
                    numStr.push_back(lastChar);
                    take();
                }
                break;
            case '.':
                if (isReal) {
                    isDone = true;
                } else {
                    isReal = true;
                    numStr.push_back(lastChar);
                    take();
                }
                break;
            case 'H':
                isHex = true;
                isDone = true;
                numStr.push_back(lastChar);
                take();
                break;
            default:
                isDone = true;
            }
        }
        if (isReal) {
            return make_shared<Token>(Token::FLOATLITERAL, loc, stof(numStr.c_str()));
        } else {
            return make_shared<Token>(Token::INTLITERAL, loc, stoi(numStr.c_str()));
        }
    } else if (isoper(lastChar)) {
        string opStr;
        do {
            opStr.push_back(lastChar);
            take();
        } while (isoper(lastChar));
        return make_shared<Token>(Token::OPERATOR, loc, opStr);
    } else if (isrelation(lastChar)) {
        string relStr;
        do {
            relStr.push_back(lastChar);
            take();
        } while (isrelation(lastChar));
        return make_shared<Token>(Token::RELATION, loc, relStr);
    } else {
        switch (lastChar) {
            case ',':
                take();
                return make_shared<Token>(Token::COMMA, loc);
            case '~':
                take();
                return make_shared<Token>(Token::TILDE, loc);
            case '^':
                take();
                return make_shared<Token>(Token::CARET, loc);
            case '|':
                take();
                return make_shared<Token>(Token::PIPE, loc);
            case ';':
                take();
                return make_shared<Token>(Token::SEMICOLON, loc);
            case ':':
                take();
                if (lastChar == '=') {
                    take();
                    return make_shared<Token>(Token::ASSIGNMENT, loc);
                } else {
                    return make_shared<Token>(Token::COLON, loc);
                }
            case '"': {
                take(); // open "
                string str;
                while (lastChar != '"') {
                    str.push_back(lastChar);
                    take();
                }
                take(); // close "
                if (str.length() == 1) {
                    return make_shared<Token>(Token::CHARLITERAL, loc, str[0]);
                } else {
                    return make_shared<Token>(Token::STRLITERAL, loc, str);
                }
            }
            case '\'': {
                take(); // open '
                string str;
                while (lastChar != '\'') {
                    str.push_back(lastChar);
                    take();
                }
                take(); // close '
                if (str.length() == 1) {
                    return make_shared<Token>(Token::CHARLITERAL, loc, str[0]);
                } else {
                    return make_shared<Token>(Token::STRLITERAL, loc, str);
                }
            }
            case '(': {
                take(); // open (
                // it's a comment
                if (lastChar == '*') {
                    char beforeLast = 0;
                    int depth = 1;
                    while(1) {
                        take();
                        if (beforeLast == '(' && lastChar == '*') {
                            depth++;
                        } else if (beforeLast == '*' && lastChar == ')') {
                            if (depth == 1) break;
                            depth--;
                        }
                        beforeLast = lastChar;
                    }
                    take(); // close )
                    return nextToken();
                } else {
                    return make_shared<Token>(Token::LPAREN, loc);
                }
            }
            case ')':
                take();
                return make_shared<Token>(Token::RPAREN, loc);
            case '{':
                take();
                return make_shared<Token>(Token::LCURLY, loc);
            case '}':
                take();
                return make_shared<Token>(Token::RCURLY, loc);
            case '[':
                take();
                return make_shared<Token>(Token::LSQUARE, loc);
            case ']':
                take();
                return make_shared<Token>(Token::RSQUARE, loc);
            case '.':
                take();
                if (lastChar == '.') {
                    take();
                    return make_shared<Token>(Token::RANGE, loc);
                } else {
                    return make_shared<Token>(Token::DOT, loc);
                }
            default:
                take();
        }
    }

    return make_shared<Token>(Token::OTHER, loc);
}
<|endoftext|>


<commit_before>#ifndef RUNNER_INTERPRETER_VISIT_HXX
# define RUNNER_INTERPRETER_VISIT_HXX

# include <boost/bind.hpp>

# include <libport/compiler.hh>
# include <libport/finally.hh>
# include <libport/foreach.hh>

# include <ast/all.hh>
# include <ast/print.hh>

# include <kernel/exception.hh>
# include <kernel/uconnection.hh>

# include <object/code-class.hh>
# include <object/global-class.hh>
# include <object/list-class.hh>
# include <object/tag-class.hh>
# include <object/symbols.hh>

# include <runner/interpreter.hh>

# include <libport/compilation.hh>

/// Job echo.
#define JECHO(Title, Content)                           \
  ECHO ("job " << ME << ", " Title ": " << Content)

/// Job & astecho.
#define JAECHO(Title, Ast)                      \
  JECHO (Title, AST(Ast))

/* Yield and trace. */
#define YIELD()                                 \
  do                                            \
  {                                             \
    ECHO ("job " << ME << " yielding on AST: "  \
	  << &e << ' ' << AST(e));              \
    yield ();                                   \
  } while (0)

namespace runner
{
  using boost::bind;
  using libport::Finally;

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::And* e)
  {
    // Collect all subrunners
    scheduler::jobs_type jobs;

    // Create separate runners for every child but the first
    foreach (const ast::rConstExp& child,
             boost::make_iterator_range(e->children_get(), 1, 0))
    {
      Interpreter* job = new Interpreter(*this, eval(child));
      // Propagate errors from subrunners.
      link(job);
      jobs.push_back(job);
      job->start_job();
    }

    // Evaluate the first child in this runner
    rRoutine code = eval(e->children_get().front()).unsafe_cast<object::Code>();
    assert(code);
    // This is a closure, it won't use its 'this'
    object::objects_type args;
    args.push_back(rObject());
    apply_urbi(code, SYMBOL(), args, 0);

    // Wait for all other jobs to terminate
    yield_until_terminated(jobs);

    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Assignment* e)
  {
    rObject val = eval(e->value_get());
    stacks_.set(e, val);
    return val;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Call* e)
  {
    // The invoked slot (probably a function).
    const ast::rConstExp& ast_tgt = e->target_get();
    rObject tgt = ast_tgt->implicit() ? stacks_.self() : eval(ast_tgt);
    return apply(tgt, e->name_get(), e->arguments_get(), e->location_get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::CallMsg*)
  {
    return stacks_.call();
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Declaration* d)
  {
    rObject val = eval(d->value_get());
    stacks_.def(d, val);
    return val;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Float* e)
  {
    return new object::Float(e->value_get());
  }

  LIBPORT_SPEED_INLINE object::rObject Interpreter::visit(const ast::Routine* e, bool closure)
  {
    rRoutine res = make_routine(e);

    // Capture variables
    foreach (const ast::rDeclaration& dec, *e->captured_variables_get())
    {
      ast::rLocal local = dec->value_get().unsafe_cast<ast::Local>();
      assert(local);
      res->captures_get().push_back(stacks_.rget(local));
    }

    // Capture 'this' and 'call' in closures
    if (closure)
    {
      res->self_get() = stacks_.self();
      res->call_get() = stacks_.call();
    }

    return res;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Function* e)
  {
    return visit(e, false);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Closure* e)
  {
    return visit(e, true);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::If* e)
  {
    // Evaluate the test.
    JAECHO ("test", e->test_get ());
    rObject cond = operator()(e->test_get().get());

    if (object::is_true(cond, SYMBOL(if)))
    {
      JAECHO ("then", e->thenclause_get());
      return e->thenclause_get()->eval(*this);
    }
    else
    {
      JAECHO ("else", e->elseclause_get());
      return e->elseclause_get()->eval(*this);
    }
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::List* e)
  {
    object::List::value_type res;
    // Evaluate every expression in the list
    foreach (const ast::rConstExp& c, e->value_get())
    {
      rObject v = eval(c);
      // Refuse void in literal lists
      if (v == object::void_class)
      {
        object::WrongArgumentType e(SYMBOL(new));
        e.location_set(c->location_get());
        throw e;
      }
      res.push_back(v);
    }
    return new object::List(res);
    //ECHO ("result: " << *result_);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Lazy* e)
  {
    return operator()(e->strict_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Local* e)
  {
    const rObject& value = stacks_.get(e);

    passert("Local variable read before being set", value);

    if (e->arguments_get())
      // FIXME: Register in the call stack
      return apply(stacks_.self(), value,
                   e->name_get(), e->arguments_get(),
                   e->location_get());
    else
      return value;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Message* e)
  {
    send_message(e->tag_get(), e->text_get());
    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Nary* e)
  {
    // List of runners for Stmt flavored by a comma.
    scheduler::jobs_type runners;

    // In case we're empty, {} evaluates to void.
    rObject res = object::void_class;

    bool tail = false;
    foreach (const ast::rConstExp& c, e->children_get())
    {
      // Allow some time to pass before we execute what follows.  If
      // we don't do this, the ;-operator would act almost like the
      // |-operator because it would always start to execute its RHS
      // immediately. However, we don't want to do it before the first
      // statement or if we only have one statement in the scope.
      if (tail++)
	YIELD();

      JAECHO("child", c);

      if (c.unsafe_cast<const ast::Stmt>() &&
	  c.unsafe_cast<const ast::Stmt>()->flavor_get() == ast::flavor_comma)
      {
	// The new runners are attached to the same tags as we are.
	Interpreter* subrunner = new Interpreter(*this, eval(c));
	// If the subrunner throws an exception, propagate it here ASAP, unless
	// we are at the top level.
	if (!e->toplevel_get())
	  link(subrunner);
	runners.push_back(subrunner);
	subrunner->start_job ();
      }
      else
      {
	// If at toplevel, print errors and continue, else rethrow them
	try
	{
          // We do not use operator() to avoid duplicating the catch
          // of UrbiExceptions
          res = c->eval(*this);
	  // We need to keep checking for void here because it can not be passed
	  // to the << function
	  if (e->toplevel_get()
              && res.get() // FIXME: What's that for?
              && res != object::void_class)
	  {
	    try
	    {
	      assertion(res);
	      ECHO("toplevel: returning a result to the connection.");

	      // Display the value using the topLevel channel.
	      // If it is not (yet) defined, do nothing, unless the environment
	      // variable TOPLEVEL_DEBUG is set.

	      static bool toplevel_debug = getenv("TOPLEVEL_DEBUG");
	      if (rObject topLevel =
	        object::global_class->slot_locate(SYMBOL(topLevel), false,
		  true))
	      {
		rObject e = topLevel->slot_get(SYMBOL(LT_LT));
                objects_type args;
                args.push_back(topLevel);
                args.push_back(res);
		apply(e, SYMBOL(topLevel), args);
	      }
	      else if (toplevel_debug)
		lobby_->value_get().connection.new_result(res);
	    }
	    catch (std::exception &ke)
	    {
	      std::cerr << "Exception when printing result: "
                        << ke.what() << std::endl;
	    }
	    catch (object::UrbiException& e)
	    {
	      throw;
	    }
	    catch (...)
	    {
	      std::cerr << "Unknown exception when printing result"
                        << std::endl;
	    }
	  }
	}
	catch (object::UrbiException& ue)
	{
          propagate_error_(ue, c->location_get());
	  if (e->toplevel_get())
	    show_error_(ue);
	  else
	    throw;
	}
      }
    }

    // If the Nary is not the toplevel one, all subrunners must be finished when
    // the runner exits the Nary node. However, it we have a scopeTag, we must
    // issue a "stop" which may interrupt subrunners.
    if (!e->toplevel_get() && !runners.empty())
    {
      const scheduler::rTag& tag = scope_tag_get();
      if (tag)
	tag->stop(scheduler_get(), object::void_class);
      yield_until_terminated(runners);
    }

    return res;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Noop*)
  {
    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Pipe* e)
  {
    // lhs
    JAECHO ("lhs", e->lhs_get ());
    operator() (e->lhs_get().get());

    // rhs:  start the execution immediately.
    JAECHO ("rhs", e->rhs_get ());
    return operator() (e->rhs_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Scope* e)
  {
    libport::Finally finally;
    create_scope_tag();
    finally << boost::bind(&Interpreter::cleanup_scope_tag, this);
    finally << libport::restore(non_interruptible_);
    return operator()(e->body_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Do* e)
  {
    Finally finally;

    rObject tgt = eval(e->target_get());

    finally << stacks_.switch_self(tgt);

    const ast::Scope* scope = reinterpret_cast<const ast::Scope*>(e);
    visit(scope);
    // This is arguable. Do, just like Scope, should maybe return
    // their last inner value.
    return tgt;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Stmt* e)
  {
    JAECHO ("expression", e->expression_get());
    return operator()(e->expression_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::String* e)
  {
    return new object::String(libport::Symbol(e->value_get()));
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Tag* t)
  {
    return eval_tag(t->exp_get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::TaggedStmt* t)
  {
    int result_depth = tags_get().size();
    try
    {
      // FIXME: might be simplified after type checking code is moved
      // to Object
      object::rObject unchecked_tag = eval(t->tag_get());
      object::type_check<object::Tag>(unchecked_tag, SYMBOL(tagged_stmt));
      const object::rTag& urbi_tag = unchecked_tag->as<object::Tag>();
      const scheduler::rTag& tag = urbi_tag->value_get();

      // If tag is blocked, do not start and ignore the
      // statement completely but use the provided payload.
      if (tag->blocked())
	return boost::any_cast<rObject>(tag->payload_get());

      push_tag (tag);
      Finally finally(bind(&Interpreter::pop_tag, this));
      // If the latest tag causes us to be frozen, let the
      // scheduler handle this properly to avoid duplicating the
      // logic.
      if (tag->frozen())
	yield();
      urbi_tag->triggerEnter(*this);
      rObject res = eval (t->exp_get());
      urbi_tag->triggerLeave(*this);
      return res;
    }
    catch (scheduler::StopException& e)
    {
      // Rewind up to the appropriate depth.
      if (e.depth_get() < result_depth)
	throw;
      // Extract the value from the exception.
      return boost::any_cast<rObject>(e.payload_get());
      // If we are frozen, reenter the scheduler for a while.
      if (frozen())
	yield();
    }
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::This*)
  {
    return stacks_.self();
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::While* e)
  {
    const bool must_yield = e->flavor_get() == ast::flavor_semicolon;
    bool tail = false;
    // Evaluate the test.
    while (true)
    {
      if (must_yield && tail++)
	YIELD();
      JAECHO ("while test", e->test_get());
      rObject cond = operator()(e->test_get().get());
      if (!object::is_true(cond, SYMBOL(while)))
	break;

      JAECHO ("while body", e->body_get());

      e->body_get()->eval(*this);
    }
    return object::void_class;
  }

  // Invalid nodes
#define INVALID(Node)                                      \
  LIBPORT_SPEED_INLINE object::rObject                                   \
  Interpreter::visit(const ast::Node* n)                   \
  {                                                        \
    static_cast<void>(n);                                  \
    pabort("Invalid node in the Interpreter: " << *n);     \
  }                                                        \

  INVALID(Binding);
  INVALID(Break);
  INVALID(Continue);
  INVALID(Foreach);
  INVALID(Implicit);
  INVALID(MetaExp);
  INVALID(Return);

#undef INVALID

}

#endif
<commit_msg>Use static_cast instead of reinterpret_cast.<commit_after>#ifndef RUNNER_INTERPRETER_VISIT_HXX
# define RUNNER_INTERPRETER_VISIT_HXX

# include <boost/bind.hpp>

# include <libport/compiler.hh>
# include <libport/finally.hh>
# include <libport/foreach.hh>

# include <ast/all.hh>
# include <ast/print.hh>

# include <kernel/exception.hh>
# include <kernel/uconnection.hh>

# include <object/code-class.hh>
# include <object/global-class.hh>
# include <object/list-class.hh>
# include <object/tag-class.hh>
# include <object/symbols.hh>

# include <runner/interpreter.hh>

# include <libport/compilation.hh>

/// Job echo.
#define JECHO(Title, Content)                           \
  ECHO ("job " << ME << ", " Title ": " << Content)

/// Job & astecho.
#define JAECHO(Title, Ast)                      \
  JECHO (Title, AST(Ast))

/* Yield and trace. */
#define YIELD()                                 \
  do                                            \
  {                                             \
    ECHO ("job " << ME << " yielding on AST: "  \
	  << &e << ' ' << AST(e));              \
    yield ();                                   \
  } while (0)

namespace runner
{
  using boost::bind;
  using libport::Finally;

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::And* e)
  {
    // Collect all subrunners
    scheduler::jobs_type jobs;

    // Create separate runners for every child but the first
    foreach (const ast::rConstExp& child,
             boost::make_iterator_range(e->children_get(), 1, 0))
    {
      Interpreter* job = new Interpreter(*this, eval(child));
      // Propagate errors from subrunners.
      link(job);
      jobs.push_back(job);
      job->start_job();
    }

    // Evaluate the first child in this runner
    rRoutine code = eval(e->children_get().front()).unsafe_cast<object::Code>();
    assert(code);
    // This is a closure, it won't use its 'this'
    object::objects_type args;
    args.push_back(rObject());
    apply_urbi(code, SYMBOL(), args, 0);

    // Wait for all other jobs to terminate
    yield_until_terminated(jobs);

    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Assignment* e)
  {
    rObject val = eval(e->value_get());
    stacks_.set(e, val);
    return val;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Call* e)
  {
    // The invoked slot (probably a function).
    const ast::rConstExp& ast_tgt = e->target_get();
    rObject tgt = ast_tgt->implicit() ? stacks_.self() : eval(ast_tgt);
    return apply(tgt, e->name_get(), e->arguments_get(), e->location_get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::CallMsg*)
  {
    return stacks_.call();
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Declaration* d)
  {
    rObject val = eval(d->value_get());
    stacks_.def(d, val);
    return val;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Float* e)
  {
    return new object::Float(e->value_get());
  }

  LIBPORT_SPEED_INLINE object::rObject Interpreter::visit(const ast::Routine* e, bool closure)
  {
    rRoutine res = make_routine(e);

    // Capture variables
    foreach (const ast::rDeclaration& dec, *e->captured_variables_get())
    {
      ast::rLocal local = dec->value_get().unsafe_cast<ast::Local>();
      assert(local);
      res->captures_get().push_back(stacks_.rget(local));
    }

    // Capture 'this' and 'call' in closures
    if (closure)
    {
      res->self_get() = stacks_.self();
      res->call_get() = stacks_.call();
    }

    return res;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Function* e)
  {
    return visit(e, false);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Closure* e)
  {
    return visit(e, true);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::If* e)
  {
    // Evaluate the test.
    JAECHO ("test", e->test_get ());
    rObject cond = operator()(e->test_get().get());

    if (object::is_true(cond, SYMBOL(if)))
    {
      JAECHO ("then", e->thenclause_get());
      return e->thenclause_get()->eval(*this);
    }
    else
    {
      JAECHO ("else", e->elseclause_get());
      return e->elseclause_get()->eval(*this);
    }
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::List* e)
  {
    object::List::value_type res;
    // Evaluate every expression in the list
    foreach (const ast::rConstExp& c, e->value_get())
    {
      rObject v = eval(c);
      // Refuse void in literal lists
      if (v == object::void_class)
      {
        object::WrongArgumentType e(SYMBOL(new));
        e.location_set(c->location_get());
        throw e;
      }
      res.push_back(v);
    }
    return new object::List(res);
    //ECHO ("result: " << *result_);
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Lazy* e)
  {
    return operator()(e->strict_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Local* e)
  {
    const rObject& value = stacks_.get(e);

    passert("Local variable read before being set", value);

    if (e->arguments_get())
      // FIXME: Register in the call stack
      return apply(stacks_.self(), value,
                   e->name_get(), e->arguments_get(),
                   e->location_get());
    else
      return value;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Message* e)
  {
    send_message(e->tag_get(), e->text_get());
    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Nary* e)
  {
    // List of runners for Stmt flavored by a comma.
    scheduler::jobs_type runners;

    // In case we're empty, {} evaluates to void.
    rObject res = object::void_class;

    bool tail = false;
    foreach (const ast::rConstExp& c, e->children_get())
    {
      // Allow some time to pass before we execute what follows.  If
      // we don't do this, the ;-operator would act almost like the
      // |-operator because it would always start to execute its RHS
      // immediately. However, we don't want to do it before the first
      // statement or if we only have one statement in the scope.
      if (tail++)
	YIELD();

      JAECHO("child", c);

      if (c.unsafe_cast<const ast::Stmt>() &&
	  c.unsafe_cast<const ast::Stmt>()->flavor_get() == ast::flavor_comma)
      {
	// The new runners are attached to the same tags as we are.
	Interpreter* subrunner = new Interpreter(*this, eval(c));
	// If the subrunner throws an exception, propagate it here ASAP, unless
	// we are at the top level.
	if (!e->toplevel_get())
	  link(subrunner);
	runners.push_back(subrunner);
	subrunner->start_job ();
      }
      else
      {
	// If at toplevel, print errors and continue, else rethrow them
	try
	{
          // We do not use operator() to avoid duplicating the catch
          // of UrbiExceptions
          res = c->eval(*this);
	  // We need to keep checking for void here because it can not be passed
	  // to the << function
	  if (e->toplevel_get()
              && res.get() // FIXME: What's that for?
              && res != object::void_class)
	  {
	    try
	    {
	      assertion(res);
	      ECHO("toplevel: returning a result to the connection.");

	      // Display the value using the topLevel channel.
	      // If it is not (yet) defined, do nothing, unless the environment
	      // variable TOPLEVEL_DEBUG is set.

	      static bool toplevel_debug = getenv("TOPLEVEL_DEBUG");
	      if (rObject topLevel =
	        object::global_class->slot_locate(SYMBOL(topLevel), false,
		  true))
	      {
		rObject e = topLevel->slot_get(SYMBOL(LT_LT));
                objects_type args;
                args.push_back(topLevel);
                args.push_back(res);
		apply(e, SYMBOL(topLevel), args);
	      }
	      else if (toplevel_debug)
		lobby_->value_get().connection.new_result(res);
	    }
	    catch (std::exception &ke)
	    {
	      std::cerr << "Exception when printing result: "
                        << ke.what() << std::endl;
	    }
	    catch (object::UrbiException& e)
	    {
	      throw;
	    }
	    catch (...)
	    {
	      std::cerr << "Unknown exception when printing result"
                        << std::endl;
	    }
	  }
	}
	catch (object::UrbiException& ue)
	{
          propagate_error_(ue, c->location_get());
	  if (e->toplevel_get())
	    show_error_(ue);
	  else
	    throw;
	}
      }
    }

    // If the Nary is not the toplevel one, all subrunners must be finished when
    // the runner exits the Nary node. However, it we have a scopeTag, we must
    // issue a "stop" which may interrupt subrunners.
    if (!e->toplevel_get() && !runners.empty())
    {
      const scheduler::rTag& tag = scope_tag_get();
      if (tag)
	tag->stop(scheduler_get(), object::void_class);
      yield_until_terminated(runners);
    }

    return res;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Noop*)
  {
    return object::void_class;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Pipe* e)
  {
    // lhs
    JAECHO ("lhs", e->lhs_get ());
    operator() (e->lhs_get().get());

    // rhs:  start the execution immediately.
    JAECHO ("rhs", e->rhs_get ());
    return operator() (e->rhs_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Scope* e)
  {
    libport::Finally finally;
    create_scope_tag();
    finally << boost::bind(&Interpreter::cleanup_scope_tag, this);
    finally << libport::restore(non_interruptible_);
    return operator()(e->body_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Do* e)
  {
    Finally finally;

    rObject tgt = eval(e->target_get());

    finally << stacks_.switch_self(tgt);

    visit(static_cast<const ast::Scope*>(e));
    // This is arguable. Do, just like Scope, should maybe return
    // their last inner value.
    return tgt;
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Stmt* e)
  {
    JAECHO ("expression", e->expression_get());
    return operator()(e->expression_get().get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::String* e)
  {
    return new object::String(libport::Symbol(e->value_get()));
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::Tag* t)
  {
    return eval_tag(t->exp_get());
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::TaggedStmt* t)
  {
    int result_depth = tags_get().size();
    try
    {
      // FIXME: might be simplified after type checking code is moved
      // to Object
      object::rObject unchecked_tag = eval(t->tag_get());
      object::type_check<object::Tag>(unchecked_tag, SYMBOL(tagged_stmt));
      const object::rTag& urbi_tag = unchecked_tag->as<object::Tag>();
      const scheduler::rTag& tag = urbi_tag->value_get();

      // If tag is blocked, do not start and ignore the
      // statement completely but use the provided payload.
      if (tag->blocked())
	return boost::any_cast<rObject>(tag->payload_get());

      push_tag (tag);
      Finally finally(bind(&Interpreter::pop_tag, this));
      // If the latest tag causes us to be frozen, let the
      // scheduler handle this properly to avoid duplicating the
      // logic.
      if (tag->frozen())
	yield();
      urbi_tag->triggerEnter(*this);
      rObject res = eval (t->exp_get());
      urbi_tag->triggerLeave(*this);
      return res;
    }
    catch (scheduler::StopException& e)
    {
      // Rewind up to the appropriate depth.
      if (e.depth_get() < result_depth)
	throw;
      // Extract the value from the exception.
      return boost::any_cast<rObject>(e.payload_get());
      // If we are frozen, reenter the scheduler for a while.
      if (frozen())
	yield();
    }
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::This*)
  {
    return stacks_.self();
  }

  LIBPORT_SPEED_INLINE object::rObject
  Interpreter::visit(const ast::While* e)
  {
    const bool must_yield = e->flavor_get() == ast::flavor_semicolon;
    bool tail = false;
    // Evaluate the test.
    while (true)
    {
      if (must_yield && tail++)
	YIELD();
      JAECHO ("while test", e->test_get());
      rObject cond = operator()(e->test_get().get());
      if (!object::is_true(cond, SYMBOL(while)))
	break;

      JAECHO ("while body", e->body_get());

      e->body_get()->eval(*this);
    }
    return object::void_class;
  }

  // Invalid nodes
#define INVALID(Node)                                      \
  LIBPORT_SPEED_INLINE object::rObject                                   \
  Interpreter::visit(const ast::Node* n)                   \
  {                                                        \
    static_cast<void>(n);                                  \
    pabort("Invalid node in the Interpreter: " << *n);     \
  }                                                        \

  INVALID(Binding);
  INVALID(Break);
  INVALID(Continue);
  INVALID(Foreach);
  INVALID(Implicit);
  INVALID(MetaExp);
  INVALID(Return);

#undef INVALID

}

#endif
<|endoftext|>


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

#include "scenegraph_window.hpp"

#include <lens.h>
#include <material.h>
#include <geomNode.h>
#include <paramNodePath.h>

#include <fmt/format.h>

#include <render_pipeline/rppanda/showbase/showbase.hpp>
#include <render_pipeline/rppanda/showbase/loader.hpp>
#include <render_pipeline/rppanda/actor/actor.hpp>
#include <render_pipeline/rpcore/globals.hpp>
#include <render_pipeline/rpcore/util/rpgeomnode.hpp>
#include <render_pipeline/rpcore/util/primitives.hpp>

#include "ImGuizmo/ImGuizmo.h"
#include "imgui/imgui_stl.h"

#include "rpplugins/rpstat/plugin.hpp"
#include "material_window.hpp"
#include "texture_window.hpp"
#include "import_model_dialog.hpp"

namespace rpplugins {

static constexpr const char* SHOW_MATERIAL_WINDOW_TEXT = "Show Material Window";
static constexpr const char* SHOW_TEXTURE_WINDOW_TEXT = "Show Texture Window";

ScenegraphWindow::ScenegraphWindow(RPStatPlugin& plugin, rpcore::RenderPipeline& pipeline) : WindowInterface(plugin, pipeline, "Scenegraph", "###Scenegraph")
{
    window_flags_ |= ImGuiWindowFlags_MenuBar;

    root_ = rpcore::Globals::render.attach_new_node("imgui-ScenegraphWindow-root");

    message_dialog_ = std::make_unique<MessageDialog>("Continue?");
    import_model_dialog_ = std::make_unique<ImportModelDialog>(plugin_, FileDialog::OperationFlag::open, "Importing Model ...");
    import_actor_dialog_ = std::make_unique<ImportModelDialog>(plugin_, FileDialog::OperationFlag::open, "Importing Actor ...");

    accept(
        CHANGE_SELECTED_NODE_EVENT_NAME,
        [this](const Event* ev) { change_selected_nodepath(DCAST(ParamNodePath, ev->get_parameter(0).get_ptr())->get_value()); }
    );
}

ScenegraphWindow::~ScenegraphWindow() = default;

void ScenegraphWindow::draw()
{
    if (!is_open_ && selected_np_)
        selected_np_.clear();

    WindowInterface::draw();
}

void ScenegraphWindow::draw_contents()
{
    enum class MenuID : int
    {
        None,
        File_Import_Model,
    };
    static MenuID menu_selected = MenuID::None;

    if (ImGui::BeginMenuBar())
    {
        if (ImGui::BeginMenu("File"))
        {
            if (ImGui::MenuItem("Import Model"))
                import_model_dialog_->open();

            if (ImGui::MenuItem("Import Actor"))
                import_actor_dialog_->open();

            ImGui::EndMenu();
        }

        if (ImGui::BeginMenu("Tools"))
        {
            if (ImGui::MenuItem("Create Empty Node"))
                rpcore::Globals::render.attach_new_node(new PandaNode("New-Node"));

            if (ImGui::MenuItem("Create Plane"))
                rpcore::create_plane("Plane").reparent_to(rpcore::Globals::render);

            if (ImGui::MenuItem("Create Cube"))
                rpcore::create_cube("Cube").reparent_to(rpcore::Globals::render);

            if (ImGui::MenuItem("Create Sphere"))
                rpcore::create_sphere("Sphere", 36, 72).reparent_to(rpcore::Globals::render);

            ImGui::EndMenu();
        }

        ImGui::EndMenuBar();
    }

    draw_import_model();
    draw_import_actor();

    // gizmo buttons
    ImGui::RadioButton("Translate", &gizmo_op_, 0); ImGui::SameLine();
    ImGui::RadioButton("Rotate", &gizmo_op_, 1); ImGui::SameLine();
    ImGui::RadioButton("Scale", &gizmo_op_, 2);

    // scenegraph
    ImGui::BeginChild("child_scenegraph", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);

    draw_nodepath(rpcore::Globals::base->get_render());

    ImGui::EndChild();

    // gizmo
    if (selected_np_)
        draw_gizmo();

    if (will_remove_np_)
    {
        will_remove_np_.remove_node();
        will_remove_np_.clear();
    }
}

void ScenegraphWindow::change_selected_nodepath(NodePath np)
{
    selected_np_ = np;
    selected_geom_ = nullptr;

    throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(selected_np_)));
}

rppanda::Actor* ScenegraphWindow::get_actor(NodePath actor) const
{
    auto found = actor_map_.find(actor);
    if (found == actor_map_.end())
        return nullptr;
    else
        return found->second;
}

bool ScenegraphWindow::is_actor(NodePath actor) const
{
    return actor_map_.find(actor) != actor_map_.end();
}

void ScenegraphWindow::add_actor(PT(rppanda::Actor) actor)
{
    actor_map_.emplace(*actor, actor);
}

void ScenegraphWindow::remove_actor(rppanda::Actor* actor)
{
    actor_map_.erase(*actor);
}

void ScenegraphWindow::remove_actor(NodePath actor)
{
    actor_map_.erase(actor);
}

void ScenegraphWindow::draw_nodepath(NodePath np)
{
    if (!np || np == root_)
        return;

    bool node_open = false;
    ImGuiTreeNodeFlags flags =
        ImGuiTreeNodeFlags_OpenOnArrow |
        ImGuiTreeNodeFlags_OpenOnDoubleClick |
        (selected_np_ == np ? ImGuiTreeNodeFlags_Selected : 0);

    // leaf or not.
    if (np.get_num_children() == 0 && !np.node()->is_geom_node())
    {
        flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
        ImGui::TreeNodeEx(np.node(), flags, np.node()->get_name().c_str());
    }
    else
    {
        node_open = ImGui::TreeNodeEx(np.node(), flags, np.node()->get_name().c_str());
    }

    // drag & drop source
    if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
    {
        payload_np_ = np;
        ImGui::SetDragDropPayload("NodePath", &payload_np_, sizeof(payload_np_));
        ImGui::Text("NodePath: %s", payload_np_.node()->get_name().c_str());
        ImGui::EndDragDropSource();
    }
    else if (ImGui::IsMouseReleased(0) && ImGui::IsItemHovered(ImGuiHoveredFlags_None))     // IsItemClicked is performed before drag and drop
    {
        change_selected_nodepath(np);
    }

    // drag & drop target
    {
        static NodePath dropped_np;
        static NodePath dragged_np;
        if (ImGui::BeginDragDropTarget())
        {
            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("NodePath"))
            {
                dropped_np = np;
                dragged_np = *reinterpret_cast<NodePath*>(ImGui::GetDragDropPayload()->Data);
                ImGui::OpenPopup("drop_nodepath");
            }
            ImGui::EndDragDropTarget();
        }

        if (dropped_np == np && ImGui::BeginPopup("drop_nodepath"))
        {
            if (ImGui::Selectable("Reparent"))
            {
                dragged_np.reparent_to(dropped_np);
                dragged_np.clear();
                dropped_np.clear();
            }

            if (ImGui::Selectable("Reparent (wrt)"))
            {
                dragged_np.wrt_reparent_to(dropped_np);
                dragged_np.clear();
                dropped_np.clear();
            }

            ImGui::EndPopup();
        }
    }

    draw_nodepath_context_menu(np);
    if (dialog_np_ == np)
    {
        const auto& accepted = message_dialog_->draw();
        if (accepted && *accepted)
            will_remove_np_ = np;
    }

    if (node_open)
    {
        for (int k = 0, k_end = np.get_num_children(); k < k_end; ++k)
            draw_nodepath(np.get_child(k));

        if (np.node()->is_geom_node())
            draw_geomnode(DCAST(GeomNode, np.node()));

        ImGui::TreePop();
    }
}

void ScenegraphWindow::draw_nodepath_context_menu(NodePath np)
{
    if (!ImGui::BeginPopupContextItem())
        return;

    if (ImGui::Selectable("Show NodePath Window"))
    {
        send_show_event("###NodePath");
        throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    ;
    if (ImGui::Selectable("Show Actor Window", false,
        actor_map_.find(np) == actor_map_.end() ? ImGuiSelectableFlags_Disabled : ImGuiSelectableFlags_None))
    {
        send_show_event("###Actor");
        throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    if (ImGui::Selectable(SHOW_MATERIAL_WINDOW_TEXT, false,
        np.has_material() ? ImGuiSelectableFlags_None : ImGuiSelectableFlags_Disabled))
    {
        send_show_event("###Material");
        throw_event(MaterialWindow::MATERIAL_SELECTED_EVENT_NAME, EventParameter(np.get_material()));
    }

    if (ImGui::Selectable(SHOW_TEXTURE_WINDOW_TEXT, false,
        np.has_texture() ? ImGuiSelectableFlags_None : ImGuiSelectableFlags_Disabled))
    {
        //    send_show_event("###Texture");
        //    throw_event(TextureWindow::TEXTURE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    if (ImGui::Selectable("Copy"))
        plugin_.set_copied_nodepath(np);

    if (ImGui::Selectable("Remove"))
    {
        dialog_np_ = np;
        message_dialog_->set_message("Do you want to remove the node?");
        message_dialog_->open();
    }

    ImGui::EndPopup();
}

void ScenegraphWindow::draw_geomnode(GeomNode* node)
{
    rpcore::RPGeomNode gn(node);
    for (int k = 0, k_end = gn.get_num_geoms(); k < k_end; ++k)
    {
        const Geom* geom = gn->get_geom(k);
        const auto& state = gn.get_state(k);

        ImGuiTreeNodeFlags flags =
            ImGuiTreeNodeFlags_OpenOnArrow |
            ImGuiTreeNodeFlags_OpenOnDoubleClick |
            (selected_geom_ == geom ? ImGuiTreeNodeFlags_Selected : 0);

        bool node_open = ImGui::TreeNodeEx(geom, flags, "Geom %d", k);

        if (ImGui::IsItemClicked())
        {
            selected_np_.clear();
            selected_geom_ = geom;
        }

        if (ImGui::BeginPopupContextItem())
        {
            if (state.has_material())
            {
                if (ImGui::Selectable(SHOW_MATERIAL_WINDOW_TEXT))
                {
                    send_show_event("###Material");
                    throw_event(MaterialWindow::MATERIAL_SELECTED_EVENT_NAME, EventParameter(state.get_material().get_material()));
                }
            }
            else
            {
                ImGui::TextDisabled(SHOW_MATERIAL_WINDOW_TEXT);
            }

            if (state.has_texture())
            {
                if (ImGui::Selectable(SHOW_TEXTURE_WINDOW_TEXT))
                {
                //    send_show_event("###Texture");
                //    throw_event(TextureWindow::TEXTURE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
                }
            }
            else
            {
                ImGui::TextDisabled(SHOW_TEXTURE_WINDOW_TEXT);
            }
            ImGui::EndPopup();
        }

        if (node_open)
        {
            ImGui::TreePop();
        }
    }
}

void ScenegraphWindow::draw_gizmo()
{
    ImGuizmo::BeginFrame();

    ImGuiIO& io = ImGui::GetIO();
    ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);

    static LMatrix4f view_mat;
    static LMatrix4f proj_mat;
    static LMatrix4f obj_mat;

    view_mat = rpcore::Globals::render.get_mat(rpcore::Globals::base->get_cam(0));
    proj_mat = rpcore::Globals::base->get_cam_lens(0)->get_projection_mat();
    obj_mat = selected_np_.get_mat(rpcore::Globals::render);

    ImGuizmo::Manipulate(&view_mat(0, 0), &proj_mat(0, 0), ImGuizmo::OPERATION(gizmo_op_), ImGuizmo::LOCAL, &obj_mat(0, 0), NULL, NULL, NULL, NULL);

    selected_np_.set_mat(rpcore::Globals::render, obj_mat);
}

void ScenegraphWindow::draw_import_model()
{
    const auto& accepted = import_model_dialog_->draw();
    if (!(accepted && *accepted))
        return;

    const auto& fname = import_model_dialog_->get_filename();
    if (!fname.empty())
    {
        NodePath np;
        try
        {
            np = rpcore::Globals::base->get_loader()->load_model(fname);
        }
        catch (const std::runtime_error& err)
        {
            plugin_.error(err.what());
        }

        if (np)
        {
            np.reparent_to(rpcore::Globals::render);
            throw_event(ScenegraphWindow::CHANGE_SELECTED_NODE_EVENT_NAME, EventParameter(new ParamNodePath(np)));
        }
    }
}

void ScenegraphWindow::draw_import_actor()
{
    const auto& accepted = import_actor_dialog_->draw();
    if (!(accepted && *accepted))
        return;

    const auto& fname = import_actor_dialog_->get_filename();
    if (!fname.empty())
    {
        PT(rppanda::Actor) actor;
        try
        {
            actor = new rppanda::Actor(rppanda::Actor::ModelsType{ fname });
        }
        catch (const std::runtime_error& err)
        {
            plugin_.error(err.what());
        }

        if (actor)
        {
            add_actor(actor);
            actor->reparent_to(rpcore::Globals::render);
            throw_event(ScenegraphWindow::CHANGE_SELECTED_NODE_EVENT_NAME, EventParameter(new ParamNodePath(NodePath(*actor))));
        }
    }
}

}
<commit_msg>Fix matrix is set when guizmo is not used<commit_after>/**
 * MIT License
 *
 * Copyright (c) 2018 Younguk Kim (bluekyu)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "scenegraph_window.hpp"

#include <lens.h>
#include <material.h>
#include <geomNode.h>
#include <paramNodePath.h>

#include <fmt/format.h>

#include <render_pipeline/rppanda/showbase/showbase.hpp>
#include <render_pipeline/rppanda/showbase/loader.hpp>
#include <render_pipeline/rppanda/actor/actor.hpp>
#include <render_pipeline/rpcore/globals.hpp>
#include <render_pipeline/rpcore/util/rpgeomnode.hpp>
#include <render_pipeline/rpcore/util/primitives.hpp>

#include "ImGuizmo/ImGuizmo.h"
#include "imgui/imgui_stl.h"

#include "rpplugins/rpstat/plugin.hpp"
#include "material_window.hpp"
#include "texture_window.hpp"
#include "import_model_dialog.hpp"

namespace rpplugins {

static constexpr const char* SHOW_MATERIAL_WINDOW_TEXT = "Show Material Window";
static constexpr const char* SHOW_TEXTURE_WINDOW_TEXT = "Show Texture Window";

ScenegraphWindow::ScenegraphWindow(RPStatPlugin& plugin, rpcore::RenderPipeline& pipeline) : WindowInterface(plugin, pipeline, "Scenegraph", "###Scenegraph")
{
    window_flags_ |= ImGuiWindowFlags_MenuBar;

    root_ = rpcore::Globals::render.attach_new_node("imgui-ScenegraphWindow-root");

    message_dialog_ = std::make_unique<MessageDialog>("Continue?");
    import_model_dialog_ = std::make_unique<ImportModelDialog>(plugin_, FileDialog::OperationFlag::open, "Importing Model ...");
    import_actor_dialog_ = std::make_unique<ImportModelDialog>(plugin_, FileDialog::OperationFlag::open, "Importing Actor ...");

    accept(
        CHANGE_SELECTED_NODE_EVENT_NAME,
        [this](const Event* ev) { change_selected_nodepath(DCAST(ParamNodePath, ev->get_parameter(0).get_ptr())->get_value()); }
    );
}

ScenegraphWindow::~ScenegraphWindow() = default;

void ScenegraphWindow::draw()
{
    if (!is_open_ && selected_np_)
        selected_np_.clear();

    WindowInterface::draw();
}

void ScenegraphWindow::draw_contents()
{
    enum class MenuID : int
    {
        None,
        File_Import_Model,
    };
    static MenuID menu_selected = MenuID::None;

    if (ImGui::BeginMenuBar())
    {
        if (ImGui::BeginMenu("File"))
        {
            if (ImGui::MenuItem("Import Model"))
                import_model_dialog_->open();

            if (ImGui::MenuItem("Import Actor"))
                import_actor_dialog_->open();

            ImGui::EndMenu();
        }

        if (ImGui::BeginMenu("Tools"))
        {
            if (ImGui::MenuItem("Create Empty Node"))
                rpcore::Globals::render.attach_new_node(new PandaNode("New-Node"));

            if (ImGui::MenuItem("Create Plane"))
                rpcore::create_plane("Plane").reparent_to(rpcore::Globals::render);

            if (ImGui::MenuItem("Create Cube"))
                rpcore::create_cube("Cube").reparent_to(rpcore::Globals::render);

            if (ImGui::MenuItem("Create Sphere"))
                rpcore::create_sphere("Sphere", 36, 72).reparent_to(rpcore::Globals::render);

            ImGui::EndMenu();
        }

        ImGui::EndMenuBar();
    }

    draw_import_model();
    draw_import_actor();

    // gizmo buttons
    ImGui::RadioButton("Translate", &gizmo_op_, 0); ImGui::SameLine();
    ImGui::RadioButton("Rotate", &gizmo_op_, 1); ImGui::SameLine();
    ImGui::RadioButton("Scale", &gizmo_op_, 2);

    // scenegraph
    ImGui::BeginChild("child_scenegraph", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar);

    draw_nodepath(rpcore::Globals::base->get_render());

    ImGui::EndChild();

    // gizmo
    if (selected_np_)
        draw_gizmo();

    if (will_remove_np_)
    {
        will_remove_np_.remove_node();
        will_remove_np_.clear();
    }
}

void ScenegraphWindow::change_selected_nodepath(NodePath np)
{
    selected_np_ = np;
    selected_geom_ = nullptr;

    throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(selected_np_)));
}

rppanda::Actor* ScenegraphWindow::get_actor(NodePath actor) const
{
    auto found = actor_map_.find(actor);
    if (found == actor_map_.end())
        return nullptr;
    else
        return found->second;
}

bool ScenegraphWindow::is_actor(NodePath actor) const
{
    return actor_map_.find(actor) != actor_map_.end();
}

void ScenegraphWindow::add_actor(PT(rppanda::Actor) actor)
{
    actor_map_.emplace(*actor, actor);
}

void ScenegraphWindow::remove_actor(rppanda::Actor* actor)
{
    actor_map_.erase(*actor);
}

void ScenegraphWindow::remove_actor(NodePath actor)
{
    actor_map_.erase(actor);
}

void ScenegraphWindow::draw_nodepath(NodePath np)
{
    if (!np || np == root_)
        return;

    bool node_open = false;
    ImGuiTreeNodeFlags flags =
        ImGuiTreeNodeFlags_OpenOnArrow |
        ImGuiTreeNodeFlags_OpenOnDoubleClick |
        (selected_np_ == np ? ImGuiTreeNodeFlags_Selected : 0);

    // leaf or not.
    if (np.get_num_children() == 0 && !np.node()->is_geom_node())
    {
        flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen;
        ImGui::TreeNodeEx(np.node(), flags, np.node()->get_name().c_str());
    }
    else
    {
        node_open = ImGui::TreeNodeEx(np.node(), flags, np.node()->get_name().c_str());
    }

    // drag & drop source
    if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
    {
        payload_np_ = np;
        ImGui::SetDragDropPayload("NodePath", &payload_np_, sizeof(payload_np_));
        ImGui::Text("NodePath: %s", payload_np_.node()->get_name().c_str());
        ImGui::EndDragDropSource();
    }
    else if (ImGui::IsMouseReleased(0) && ImGui::IsItemHovered(ImGuiHoveredFlags_None))     // IsItemClicked is performed before drag and drop
    {
        change_selected_nodepath(np);
    }

    // drag & drop target
    {
        static NodePath dropped_np;
        static NodePath dragged_np;
        if (ImGui::BeginDragDropTarget())
        {
            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("NodePath"))
            {
                dropped_np = np;
                dragged_np = *reinterpret_cast<NodePath*>(ImGui::GetDragDropPayload()->Data);
                ImGui::OpenPopup("drop_nodepath");
            }
            ImGui::EndDragDropTarget();
        }

        if (dropped_np == np && ImGui::BeginPopup("drop_nodepath"))
        {
            if (ImGui::Selectable("Reparent"))
            {
                dragged_np.reparent_to(dropped_np);
                dragged_np.clear();
                dropped_np.clear();
            }

            if (ImGui::Selectable("Reparent (wrt)"))
            {
                dragged_np.wrt_reparent_to(dropped_np);
                dragged_np.clear();
                dropped_np.clear();
            }

            ImGui::EndPopup();
        }
    }

    draw_nodepath_context_menu(np);
    if (dialog_np_ == np)
    {
        const auto& accepted = message_dialog_->draw();
        if (accepted && *accepted)
            will_remove_np_ = np;
    }

    if (node_open)
    {
        for (int k = 0, k_end = np.get_num_children(); k < k_end; ++k)
            draw_nodepath(np.get_child(k));

        if (np.node()->is_geom_node())
            draw_geomnode(DCAST(GeomNode, np.node()));

        ImGui::TreePop();
    }
}

void ScenegraphWindow::draw_nodepath_context_menu(NodePath np)
{
    if (!ImGui::BeginPopupContextItem())
        return;

    if (ImGui::Selectable("Show NodePath Window"))
    {
        send_show_event("###NodePath");
        throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    ;
    if (ImGui::Selectable("Show Actor Window", false,
        actor_map_.find(np) == actor_map_.end() ? ImGuiSelectableFlags_Disabled : ImGuiSelectableFlags_None))
    {
        send_show_event("###Actor");
        throw_event(NODE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    if (ImGui::Selectable(SHOW_MATERIAL_WINDOW_TEXT, false,
        np.has_material() ? ImGuiSelectableFlags_None : ImGuiSelectableFlags_Disabled))
    {
        send_show_event("###Material");
        throw_event(MaterialWindow::MATERIAL_SELECTED_EVENT_NAME, EventParameter(np.get_material()));
    }

    if (ImGui::Selectable(SHOW_TEXTURE_WINDOW_TEXT, false,
        np.has_texture() ? ImGuiSelectableFlags_None : ImGuiSelectableFlags_Disabled))
    {
        //    send_show_event("###Texture");
        //    throw_event(TextureWindow::TEXTURE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
    }

    if (ImGui::Selectable("Copy"))
        plugin_.set_copied_nodepath(np);

    if (ImGui::Selectable("Remove"))
    {
        dialog_np_ = np;
        message_dialog_->set_message("Do you want to remove the node?");
        message_dialog_->open();
    }

    ImGui::EndPopup();
}

void ScenegraphWindow::draw_geomnode(GeomNode* node)
{
    rpcore::RPGeomNode gn(node);
    for (int k = 0, k_end = gn.get_num_geoms(); k < k_end; ++k)
    {
        const Geom* geom = gn->get_geom(k);
        const auto& state = gn.get_state(k);

        ImGuiTreeNodeFlags flags =
            ImGuiTreeNodeFlags_OpenOnArrow |
            ImGuiTreeNodeFlags_OpenOnDoubleClick |
            (selected_geom_ == geom ? ImGuiTreeNodeFlags_Selected : 0);

        bool node_open = ImGui::TreeNodeEx(geom, flags, "Geom %d", k);

        if (ImGui::IsItemClicked())
        {
            selected_np_.clear();
            selected_geom_ = geom;
        }

        if (ImGui::BeginPopupContextItem())
        {
            if (state.has_material())
            {
                if (ImGui::Selectable(SHOW_MATERIAL_WINDOW_TEXT))
                {
                    send_show_event("###Material");
                    throw_event(MaterialWindow::MATERIAL_SELECTED_EVENT_NAME, EventParameter(state.get_material().get_material()));
                }
            }
            else
            {
                ImGui::TextDisabled(SHOW_MATERIAL_WINDOW_TEXT);
            }

            if (state.has_texture())
            {
                if (ImGui::Selectable(SHOW_TEXTURE_WINDOW_TEXT))
                {
                //    send_show_event("###Texture");
                //    throw_event(TextureWindow::TEXTURE_SELECTED_EVENT_NAME, EventParameter(new ParamNodePath(np)));
                }
            }
            else
            {
                ImGui::TextDisabled(SHOW_TEXTURE_WINDOW_TEXT);
            }
            ImGui::EndPopup();
        }

        if (node_open)
        {
            ImGui::TreePop();
        }
    }
}

void ScenegraphWindow::draw_gizmo()
{
    ImGuizmo::BeginFrame();

    ImGuiIO& io = ImGui::GetIO();
    ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);

    static LMatrix4f view_mat;
    static LMatrix4f proj_mat;
    static LMatrix4f obj_mat;

    view_mat = rpcore::Globals::render.get_mat(rpcore::Globals::base->get_cam(0));
    proj_mat = rpcore::Globals::base->get_cam_lens(0)->get_projection_mat();
    obj_mat = selected_np_.get_mat(rpcore::Globals::render);

    const bool guizmo_using = ImGuizmo::IsUsing();

    ImGuizmo::Manipulate(&view_mat(0, 0), &proj_mat(0, 0), ImGuizmo::OPERATION(gizmo_op_), ImGuizmo::LOCAL, &obj_mat(0, 0), NULL, NULL, NULL, NULL);

    if (guizmo_using)
        selected_np_.set_mat(rpcore::Globals::render, obj_mat);
}

void ScenegraphWindow::draw_import_model()
{
    const auto& accepted = import_model_dialog_->draw();
    if (!(accepted && *accepted))
        return;

    const auto& fname = import_model_dialog_->get_filename();
    if (!fname.empty())
    {
        NodePath np;
        try
        {
            np = rpcore::Globals::base->get_loader()->load_model(fname);
        }
        catch (const std::runtime_error& err)
        {
            plugin_.error(err.what());
        }

        if (np)
        {
            np.reparent_to(rpcore::Globals::render);
            throw_event(ScenegraphWindow::CHANGE_SELECTED_NODE_EVENT_NAME, EventParameter(new ParamNodePath(np)));
        }
    }
}

void ScenegraphWindow::draw_import_actor()
{
    const auto& accepted = import_actor_dialog_->draw();
    if (!(accepted && *accepted))
        return;

    const auto& fname = import_actor_dialog_->get_filename();
    if (!fname.empty())
    {
        PT(rppanda::Actor) actor;
        try
        {
            actor = new rppanda::Actor(rppanda::Actor::ModelsType{ fname });
        }
        catch (const std::runtime_error& err)
        {
            plugin_.error(err.what());
        }

        if (actor)
        {
            add_actor(actor);
            actor->reparent_to(rpcore::Globals::render);
            throw_event(ScenegraphWindow::CHANGE_SELECTED_NODE_EVENT_NAME, EventParameter(new ParamNodePath(NodePath(*actor))));
        }
    }
}

}
<|endoftext|>