<commit_before>//! \file
/*
**  Copyright (C) - Triton
**
**  This program is under the terms of the Apache License 2.0.
*/

#include <string>

#include <triton/astContext.hpp>
#include <triton/exceptions.hpp>
#include <triton/solverModel.hpp>
#include <triton/symbolicVariable.hpp>
#include <triton/tritonToZ3Ast.hpp>
#include <triton/tritonTypes.hpp>
#include <triton/z3Solver.hpp>
#include <triton/z3ToTritonAst.hpp>

namespace triton {
  namespace engines {
    namespace solver {

      //! Wrapper to handle variadict number of arguments or'd togethers
      z3::expr mk_or(z3::expr_vector args) {
        std::vector<Z3_ast> array;

        for (triton::uint32 i = 0; i < args.size(); i++)
          array.push_back(args[i]);

        return to_expr(args.ctx(), Z3_mk_or(args.ctx(), static_cast<triton::uint32>(array.size()), &(array[0])));
      }

      Z3Solver::Z3Solver() {
        this->timeout = 0;
        this->memoryLimit = 0;
      }

      std::vector<std::unordered_map<triton::usize, SolverModel>> Z3Solver::getModels(const triton::ast::SharedAbstractNode& node, triton::uint32 limit, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        std::vector<std::unordered_map<triton::usize, SolverModel>> ret;
        triton::ast::SharedAbstractNode onode = node;
        triton::ast::TritonToZ3Ast z3Ast{false};

        try {
          if (onode == nullptr)
            throw triton::exceptions::SolverEngine("Z3Solver::getModels(): node cannot be null.");

          /* Z3 does not need an assert() as root node */
          if (node->getType() == triton::ast::ASSERT_NODE)
            onode = node->getChildren()[0];

          if (onode->isLogical() == false)
            throw triton::exceptions::SolverEngine("Z3Solver::getModels(): Must be a logical node.");

          z3::expr      expr = z3Ast.convert(onode);
          z3::context&  ctx  = expr.ctx();
          z3::solver    solver(ctx);

          /* Create a solver and add the expression */
          solver.add(expr);

          z3::params p(ctx);

          /* Define the timeout */
          if (timeout) {
            p.set(":timeout", timeout);
          }
          else if (this->timeout) {
            p.set(":timeout", this->timeout);
          }

          /* Define memory limit */
          if (this->memoryLimit) {
            p.set(":max_memory", this->memoryLimit);
          }

          solver.set(p);

          /* Get first model */
          z3::check_result res = solver.check();

          /* Write back the status code of the first constraint */
          this->writeBackStatus(solver, res, status);

          /* Check if it is sat */
          while (res == z3::sat && limit >= 1) {
            /* Get model */
            z3::model m = solver.get_model();

            /* Traversing the model */
            std::unordered_map<triton::usize, SolverModel> smodel;
            z3::expr_vector args(ctx);
            for (triton::uint32 i = 0; i < m.size(); i++) {

              /* Get the z3 variable */
              z3::func_decl z3Variable = m[i];

              /* Get the name as std::string from a z3 variable */
              std::string varName = z3Variable.name().str();

              /* Get z3 expr */
              z3::expr exp = m.get_const_interp(z3Variable);

              /* Get the size of a z3 expr */
              triton::uint32 bvSize = exp.get_sort().bv_size();

              /* Get the value of a z3 expr */
              std::string svalue = Z3_get_numeral_string(ctx, exp);

              /* Convert a string value to a integer value */
              triton::uint512 value = triton::uint512(svalue);

              /* Create a triton model */
              SolverModel trionModel = SolverModel(z3Ast.variables[varName], value);

              /* Map the result */
              smodel[trionModel.getId()] = trionModel;

              /* Uniq result */
              if (exp.get_sort().is_bv())
                args.push_back(ctx.bv_const(varName.c_str(), bvSize) != ctx.bv_val(svalue.c_str(), bvSize));
            }

            /* Check that model is available */
            if (smodel.empty())
              break;

            /* Push model */
            ret.push_back(smodel);

            if (--limit) {
              /* Escape last models */
              solver.add(triton::engines::solver::mk_or(args));

              /* Get next model */
              res = solver.check();
            }
          }
        }
        catch (const z3::exception& e) {
          if (!strcmp(e.msg(), "max. memory exceeded")) {
            if (status) {
              *status = triton::engines::solver::OUTOFMEM;
            }
            return {};
          }
          throw triton::exceptions::SolverEngine(std::string("Z3Solver::getModels(): ") + e.msg());
        }

        return ret;
      }

      bool Z3Solver::isSat(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        triton::ast::TritonToZ3Ast z3Ast{false};

        if (node == nullptr)
          throw triton::exceptions::SolverEngine("Z3Solver::isSat(): node cannot be null.");

        if (node->isLogical() == false)
          throw triton::exceptions::SolverEngine("Z3Solver::isSat(): Must be a logical node.");

        try {
          z3::expr      expr = z3Ast.convert(node);
          z3::context&  ctx  = expr.ctx();
          z3::solver    solver(ctx);

          /* Create a solver and add the expression */
          solver.add(expr);

          z3::params p(ctx);

          /* Define the timeout */
          if (timeout) {
            p.set(":timeout", timeout);
          }
          else if (this->timeout) {
            p.set(":timeout", this->timeout);
          }

          /* Define memory limit */
          if (this->memoryLimit) {
            p.set(":max_memory", this->memoryLimit);
          }

          solver.set(p);

          z3::check_result res = solver.check();
          this->writeBackStatus(solver, res, status);
          return res == z3::sat;
        }
        catch (const z3::exception& e) {
          if (!strcmp(e.msg(), "max. memory exceeded")) {
            if (status) {
              *status = triton::engines::solver::OUTOFMEM;
            }
            return {};
          }
          throw triton::exceptions::SolverEngine(std::string("Z3Solver::isSat(): ") + e.msg());
        }
      }

      std::unordered_map<triton::usize, SolverModel> Z3Solver::getModel(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        std::unordered_map<triton::usize, SolverModel> ret;
        std::vector<std::unordered_map<triton::usize, SolverModel>> allModels;

        allModels = this->getModels(node, 1, status, timeout);
        if (allModels.size() > 0)
          ret = allModels.front();

        return ret;
      }

      triton::ast::SharedAbstractNode Z3Solver::simplify(const triton::ast::SharedAbstractNode& node) const {
        if (node == nullptr)
          throw triton::exceptions::AstTranslations("Z3Solver::simplify(): node cannot be null.");

        try {
          triton::ast::TritonToZ3Ast z3Ast{false};
          triton::ast::Z3ToTritonAst tritonAst{node->getContext()};

          /* From Triton to Z3 */
          z3::expr expr = z3Ast.convert(node);

          /* Simplify and back to Triton's AST */
          auto snode = tritonAst.convert(expr.simplify());

          return snode;
        }
        catch (const z3::exception& e) {
          throw triton::exceptions::AstTranslations(std::string("Z3Solver::evaluate(): ") + e.msg());
        }
      }

      triton::uint512 Z3Solver::evaluate(const triton::ast::SharedAbstractNode& node) const {
        if (node == nullptr)
          throw triton::exceptions::AstTranslations("Z3Solver::simplify(): node cannot be null.");

        try {
          triton::ast::TritonToZ3Ast z3ast{true};

          /* From Triton to Z3 */
          z3::expr expr = z3ast.convert(node);

          /* Simplify the expression to get a constant */
          expr = expr.simplify();

          triton::uint512 res = 0;
          if (expr.get_sort().is_bool())
            res = Z3_get_bool_value(expr.ctx(), expr) == Z3_L_TRUE ? true : false;
          else
            res = triton::uint512{Z3_get_numeral_string(expr.ctx(), expr)};

          return res;
        }
        catch (const z3::exception& e) {
          throw triton::exceptions::AstTranslations(std::string("Z3Solver::evaluate(): ") + e.msg());
        }
      }

      void Z3Solver::writeBackStatus(z3::solver& solver, z3::check_result res, triton::engines::solver::status_e* status) const {
        if (status != nullptr) {
          switch (res) {
            case z3::sat:
              *status = triton::engines::solver::SAT;
              break;

            case z3::unsat:
              *status = triton::engines::solver::UNSAT;
              break;

            case z3::unknown:
              if (solver.reason_unknown() == "timeout") {
                *status = triton::engines::solver::TIMEOUT;
              }
              else if (solver.reason_unknown() == "max. memory exceeded")
              {
                *status = triton::engines::solver::OUTOFMEM;
              }
              else {
                *status = triton::engines::solver::UNKNOWN;
              }
              break;
          }
        }
      }

      std::string Z3Solver::getName(void) const {
        return "z3";
      }

      void Z3Solver::setTimeout(triton::uint32 ms) {
        this->timeout = ms;
      }

      void Z3Solver::setMemoryLimit(triton::uint32 limit) {
        this->memoryLimit = limit;
      }

    };
  };
};
<commit_msg>Check that vector for mk_or is not empty<commit_after>//! \file
/*
**  Copyright (C) - Triton
**
**  This program is under the terms of the Apache License 2.0.
*/

#include <string>

#include <triton/astContext.hpp>
#include <triton/exceptions.hpp>
#include <triton/solverModel.hpp>
#include <triton/symbolicVariable.hpp>
#include <triton/tritonToZ3Ast.hpp>
#include <triton/tritonTypes.hpp>
#include <triton/z3Solver.hpp>
#include <triton/z3ToTritonAst.hpp>

namespace triton {
  namespace engines {
    namespace solver {

      //! Wrapper to handle variadict number of arguments or'd togethers
      z3::expr mk_or(z3::expr_vector args) {
        std::vector<Z3_ast> array;

        for (triton::uint32 i = 0; i < args.size(); i++)
          array.push_back(args[i]);

        return to_expr(args.ctx(), Z3_mk_or(args.ctx(), static_cast<triton::uint32>(array.size()), &(array[0])));
      }

      Z3Solver::Z3Solver() {
        this->timeout = 0;
        this->memoryLimit = 0;
      }

      std::vector<std::unordered_map<triton::usize, SolverModel>> Z3Solver::getModels(const triton::ast::SharedAbstractNode& node, triton::uint32 limit, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        std::vector<std::unordered_map<triton::usize, SolverModel>> ret;
        triton::ast::SharedAbstractNode onode = node;
        triton::ast::TritonToZ3Ast z3Ast{false};

        try {
          if (onode == nullptr)
            throw triton::exceptions::SolverEngine("Z3Solver::getModels(): node cannot be null.");

          /* Z3 does not need an assert() as root node */
          if (node->getType() == triton::ast::ASSERT_NODE)
            onode = node->getChildren()[0];

          if (onode->isLogical() == false)
            throw triton::exceptions::SolverEngine("Z3Solver::getModels(): Must be a logical node.");

          z3::expr      expr = z3Ast.convert(onode);
          z3::context&  ctx  = expr.ctx();
          z3::solver    solver(ctx);

          /* Create a solver and add the expression */
          solver.add(expr);

          z3::params p(ctx);

          /* Define the timeout */
          if (timeout) {
            p.set(":timeout", timeout);
          }
          else if (this->timeout) {
            p.set(":timeout", this->timeout);
          }

          /* Define memory limit */
          if (this->memoryLimit) {
            p.set(":max_memory", this->memoryLimit);
          }

          solver.set(p);

          /* Get first model */
          z3::check_result res = solver.check();

          /* Write back the status code of the first constraint */
          this->writeBackStatus(solver, res, status);

          /* Check if it is sat */
          while (res == z3::sat && limit >= 1) {
            /* Get model */
            z3::model m = solver.get_model();

            /* Traversing the model */
            std::unordered_map<triton::usize, SolverModel> smodel;
            z3::expr_vector args(ctx);
            for (triton::uint32 i = 0; i < m.size(); i++) {

              /* Get the z3 variable */
              z3::func_decl z3Variable = m[i];

              /* Get the name as std::string from a z3 variable */
              std::string varName = z3Variable.name().str();

              /* Get z3 expr */
              z3::expr exp = m.get_const_interp(z3Variable);

              /* Get the size of a z3 expr */
              triton::uint32 bvSize = exp.get_sort().bv_size();

              /* Get the value of a z3 expr */
              std::string svalue = Z3_get_numeral_string(ctx, exp);

              /* Convert a string value to a integer value */
              triton::uint512 value = triton::uint512(svalue);

              /* Create a triton model */
              SolverModel trionModel = SolverModel(z3Ast.variables[varName], value);

              /* Map the result */
              smodel[trionModel.getId()] = trionModel;

              /* Uniq result */
              if (exp.get_sort().is_bv())
                args.push_back(ctx.bv_const(varName.c_str(), bvSize) != ctx.bv_val(svalue.c_str(), bvSize));
            }

            /* Check that model is available */
            if (smodel.empty())
              break;

            /* Push model */
            ret.push_back(smodel);

            if (--limit) {
              /* Escape last models */
              if (!args.empty()) {
                solver.add(triton::engines::solver::mk_or(args));
              }

              /* Get next model */
              res = solver.check();
            }
          }
        }
        catch (const z3::exception& e) {
          if (!strcmp(e.msg(), "max. memory exceeded")) {
            if (status) {
              *status = triton::engines::solver::OUTOFMEM;
            }
            return {};
          }
          throw triton::exceptions::SolverEngine(std::string("Z3Solver::getModels(): ") + e.msg());
        }

        return ret;
      }

      bool Z3Solver::isSat(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        triton::ast::TritonToZ3Ast z3Ast{false};

        if (node == nullptr)
          throw triton::exceptions::SolverEngine("Z3Solver::isSat(): node cannot be null.");

        if (node->isLogical() == false)
          throw triton::exceptions::SolverEngine("Z3Solver::isSat(): Must be a logical node.");

        try {
          z3::expr      expr = z3Ast.convert(node);
          z3::context&  ctx  = expr.ctx();
          z3::solver    solver(ctx);

          /* Create a solver and add the expression */
          solver.add(expr);

          z3::params p(ctx);

          /* Define the timeout */
          if (timeout) {
            p.set(":timeout", timeout);
          }
          else if (this->timeout) {
            p.set(":timeout", this->timeout);
          }

          /* Define memory limit */
          if (this->memoryLimit) {
            p.set(":max_memory", this->memoryLimit);
          }

          solver.set(p);

          z3::check_result res = solver.check();
          this->writeBackStatus(solver, res, status);
          return res == z3::sat;
        }
        catch (const z3::exception& e) {
          if (!strcmp(e.msg(), "max. memory exceeded")) {
            if (status) {
              *status = triton::engines::solver::OUTOFMEM;
            }
            return {};
          }
          throw triton::exceptions::SolverEngine(std::string("Z3Solver::isSat(): ") + e.msg());
        }
      }

      std::unordered_map<triton::usize, SolverModel> Z3Solver::getModel(const triton::ast::SharedAbstractNode& node, triton::engines::solver::status_e* status, triton::uint32 timeout) const {
        std::unordered_map<triton::usize, SolverModel> ret;
        std::vector<std::unordered_map<triton::usize, SolverModel>> allModels;

        allModels = this->getModels(node, 1, status, timeout);
        if (allModels.size() > 0)
          ret = allModels.front();

        return ret;
      }

      triton::ast::SharedAbstractNode Z3Solver::simplify(const triton::ast::SharedAbstractNode& node) const {
        if (node == nullptr)
          throw triton::exceptions::AstTranslations("Z3Solver::simplify(): node cannot be null.");

        try {
          triton::ast::TritonToZ3Ast z3Ast{false};
          triton::ast::Z3ToTritonAst tritonAst{node->getContext()};

          /* From Triton to Z3 */
          z3::expr expr = z3Ast.convert(node);

          /* Simplify and back to Triton's AST */
          auto snode = tritonAst.convert(expr.simplify());

          return snode;
        }
        catch (const z3::exception& e) {
          throw triton::exceptions::AstTranslations(std::string("Z3Solver::evaluate(): ") + e.msg());
        }
      }

      triton::uint512 Z3Solver::evaluate(const triton::ast::SharedAbstractNode& node) const {
        if (node == nullptr)
          throw triton::exceptions::AstTranslations("Z3Solver::simplify(): node cannot be null.");

        try {
          triton::ast::TritonToZ3Ast z3ast{true};

          /* From Triton to Z3 */
          z3::expr expr = z3ast.convert(node);

          /* Simplify the expression to get a constant */
          expr = expr.simplify();

          triton::uint512 res = 0;
          if (expr.get_sort().is_bool())
            res = Z3_get_bool_value(expr.ctx(), expr) == Z3_L_TRUE ? true : false;
          else
            res = triton::uint512{Z3_get_numeral_string(expr.ctx(), expr)};

          return res;
        }
        catch (const z3::exception& e) {
          throw triton::exceptions::AstTranslations(std::string("Z3Solver::evaluate(): ") + e.msg());
        }
      }

      void Z3Solver::writeBackStatus(z3::solver& solver, z3::check_result res, triton::engines::solver::status_e* status) const {
        if (status != nullptr) {
          switch (res) {
            case z3::sat:
              *status = triton::engines::solver::SAT;
              break;

            case z3::unsat:
              *status = triton::engines::solver::UNSAT;
              break;

            case z3::unknown:
              if (solver.reason_unknown() == "timeout") {
                *status = triton::engines::solver::TIMEOUT;
              }
              else if (solver.reason_unknown() == "max. memory exceeded")
              {
                *status = triton::engines::solver::OUTOFMEM;
              }
              else {
                *status = triton::engines::solver::UNKNOWN;
              }
              break;
          }
        }
      }

      std::string Z3Solver::getName(void) const {
        return "z3";
      }

      void Z3Solver::setTimeout(triton::uint32 ms) {
        this->timeout = ms;
      }

      void Z3Solver::setMemoryLimit(triton::uint32 limit) {
        this->memoryLimit = limit;
      }

    };
  };
};
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/java_bridge_dispatcher.h"

#include "content/common/child_process.h"
#include "content/common/java_bridge_messages.h"
#include "content/common/npobject_util.h"  // For CreateNPVariant()
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/java_bridge_channel.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"

JavaBridgeDispatcher::JavaBridgeDispatcher(
    content::RenderView* render_view,
    const IPC::ChannelHandle& channel_handle)
    : RenderViewObserver(render_view) {
  channel_.reset(JavaBridgeChannel::GetJavaBridgeChannel(
      channel_handle, ChildProcess::current()->io_message_loop_proxy()));
}

JavaBridgeDispatcher::~JavaBridgeDispatcher() {
  for (ObjectMap::const_iterator iter = objects_.begin();
      iter != objects_.end(); ++iter) {
    WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  }
}

bool JavaBridgeDispatcher::OnMessageReceived(const IPC::Message& msg) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(JavaBridgeDispatcher, msg)
    IPC_MESSAGE_HANDLER(JavaBridgeMsg_AddNamedObject, OnAddNamedObject)
    IPC_MESSAGE_HANDLER(JavaBridgeMsg_RemoveNamedObject,
                        OnRemoveNamedObject)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void JavaBridgeDispatcher::DidClearWindowObject(WebKit::WebFrame* web_frame) {
  // We only inject objects into the main frame.
  if (web_frame != render_view()->GetWebView()->mainFrame())
    return;

  // Note that we have to (re)bind all objects, as they will have been unbound
  // when the window object was cleared.
  for (ObjectMap::const_iterator iter = objects_.begin();
      iter != objects_.end(); ++iter) {
    // This refs the NPObject. This reference is dropped when either the window
    // object is later cleared, or the object is GC'ed. So the object may be
    // deleted at any time after OnRemoveNamedObject() is called.
    web_frame->bindToWindowObject(iter->first,
        NPVARIANT_TO_OBJECT(iter->second));
  }
}

void JavaBridgeDispatcher::OnAddNamedObject(
    const string16& name,
    const NPVariant_Param& variant_param) {
  DCHECK_EQ(variant_param.type, NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID);
  // This creates an NPObject, wrapped as an NPVariant. We don't need the
  // containing window or the page URL, as we don't do re-entrant sync IPC.
  NPVariant variant;
  bool created =
      CreateNPVariant(variant_param, channel_.get(), &variant, NULL, GURL());
  DCHECK(created);
  DCHECK_EQ(variant.type, NPVariantType_Object);

  // The NPObject is created with a ref count of one, which we remove when
  // OnRemoveNamedObject() is called for that object.
  ObjectMap::iterator iter = objects_.find(name);
  if (iter != objects_.end()) {
    WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  }
  objects_[name] = variant;
}

void JavaBridgeDispatcher::OnRemoveNamedObject(const string16& name) {
  // Removing an object does not unbind it from JavaScript until the window
  // object is next cleared. Note that the browser checks that the named object
  // is present.
  ObjectMap::iterator iter = objects_.find(name);
  DCHECK(iter != objects_.end());
  WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  objects_.erase(iter);
}
<commit_msg>Use 0 instead of NULL to make GCC 4.6 for Chrome OS happy.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/java_bridge_dispatcher.h"

#include "content/common/child_process.h"
#include "content/common/java_bridge_messages.h"
#include "content/common/npobject_util.h"  // For CreateNPVariant()
#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/render_view.h"
#include "content/renderer/java_bridge_channel.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"

JavaBridgeDispatcher::JavaBridgeDispatcher(
    content::RenderView* render_view,
    const IPC::ChannelHandle& channel_handle)
    : RenderViewObserver(render_view) {
  channel_.reset(JavaBridgeChannel::GetJavaBridgeChannel(
      channel_handle, ChildProcess::current()->io_message_loop_proxy()));
}

JavaBridgeDispatcher::~JavaBridgeDispatcher() {
  for (ObjectMap::const_iterator iter = objects_.begin();
      iter != objects_.end(); ++iter) {
    WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  }
}

bool JavaBridgeDispatcher::OnMessageReceived(const IPC::Message& msg) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(JavaBridgeDispatcher, msg)
    IPC_MESSAGE_HANDLER(JavaBridgeMsg_AddNamedObject, OnAddNamedObject)
    IPC_MESSAGE_HANDLER(JavaBridgeMsg_RemoveNamedObject,
                        OnRemoveNamedObject)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void JavaBridgeDispatcher::DidClearWindowObject(WebKit::WebFrame* web_frame) {
  // We only inject objects into the main frame.
  if (web_frame != render_view()->GetWebView()->mainFrame())
    return;

  // Note that we have to (re)bind all objects, as they will have been unbound
  // when the window object was cleared.
  for (ObjectMap::const_iterator iter = objects_.begin();
      iter != objects_.end(); ++iter) {
    // This refs the NPObject. This reference is dropped when either the window
    // object is later cleared, or the object is GC'ed. So the object may be
    // deleted at any time after OnRemoveNamedObject() is called.
    web_frame->bindToWindowObject(iter->first,
        NPVARIANT_TO_OBJECT(iter->second));
  }
}

void JavaBridgeDispatcher::OnAddNamedObject(
    const string16& name,
    const NPVariant_Param& variant_param) {
  DCHECK_EQ(variant_param.type, NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID);
  // This creates an NPObject, wrapped as an NPVariant. We don't need the
  // containing window or the page URL, as we don't do re-entrant sync IPC.
  NPVariant variant;
  bool created =
      CreateNPVariant(variant_param, channel_.get(), &variant, 0, GURL());
  DCHECK(created);
  DCHECK_EQ(variant.type, NPVariantType_Object);

  // The NPObject is created with a ref count of one, which we remove when
  // OnRemoveNamedObject() is called for that object.
  ObjectMap::iterator iter = objects_.find(name);
  if (iter != objects_.end()) {
    WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  }
  objects_[name] = variant;
}

void JavaBridgeDispatcher::OnRemoveNamedObject(const string16& name) {
  // Removing an object does not unbind it from JavaScript until the window
  // object is next cleared. Note that the browser checks that the named object
  // is present.
  ObjectMap::iterator iter = objects_.find(name);
  DCHECK(iter != objects_.end());
  WebKit::WebBindings::releaseObject(NPVARIANT_TO_OBJECT(iter->second));
  objects_.erase(iter);
}
<|endoftext|>


<commit_before>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// 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 <string.h>

#include <functional>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "rcutils/allocator.h"
#include "rcutils/error_handling.h"
#include "rcutils/logging_macros.h"
#include "rcutils/strdup.h"
#include "rcutils/types.h"

#include "rmw/allocators.h"
#include "rmw/convert_rcutils_ret_to_rmw_ret.h"
#include "rmw/error_handling.h"
#include "rmw/get_topic_names_and_types.h"
#include "rmw/impl/cpp/macros.hpp"
#include "rmw/impl/cpp/key_value.hpp"
#include "rmw/names_and_types.h"
#include "rmw/rmw.h"

#include "rmw_connext_shared_cpp/node_info_and_types.hpp"
#include "rmw_connext_shared_cpp/types.hpp"
#include "rmw_connext_shared_cpp/names_and_types_helpers.hpp"
#include "rmw_connext_shared_cpp/ndds_include.hpp"
#include "rmw_connext_shared_cpp/guid_helper.hpp"

/**
 * Check to see if a node name and namespace match the user data QoS policy
 * of a node.
 *
 * @param user_data_qos to inspect
 * @param node_name to match
 * @param node_namespace to match
 * @return true if match
 */
bool
__is_node_match(
  DDS::UserDataQosPolicy & user_data_qos,
  const char * node_name,
  const char * node_namespace)
{
  uint8_t * buf = user_data_qos.value.get_contiguous_buffer();
  if (buf) {
    std::vector<uint8_t> kv(buf, buf + user_data_qos.value.length());
    auto map = rmw::impl::cpp::parse_key_value(kv);
    auto name_found = map.find("name");
    auto ns_found = map.find("namespace");

    if (name_found != map.end() && ns_found != map.end()) {
      std::string name(name_found->second.begin(), name_found->second.end());
      std::string ns(ns_found->second.begin(), ns_found->second.end());
      return (name == node_name) && (ns == node_namespace);
    }
  }
  return false;
}

/**
 * Get a DDS GUID key for the discovered participant which matches the
 * node_name and node_namepace supplied.
 *
 * @param node_info to discover nodes
 * @param node_name to match
 * @param node_namespace to match
 * @param key [out] guid key that matches the node name and namespace
 *
 * @return RMW_RET_OK if success, ERROR otherwise
 */
rmw_ret_t
__get_key(
  ConnextNodeInfo * node_info,
  const char * node_name,
  const char * node_namespace,
  DDS::GUID_t & key)
{
  auto participant = node_info->participant;
  RMW_CHECK_FOR_NULL_WITH_MSG(participant, "participant handle is null", return RMW_RET_ERROR);

  DDS::DomainParticipantQos dpqos;
  auto dds_ret = participant->get_qos(dpqos);
  if (dds_ret == DDS::RETCODE_OK && __is_node_match(dpqos.user_data, node_name, node_namespace)) {
    DDS_InstanceHandle_to_GUID(&key, participant->get_instance_handle());
    return RMW_RET_OK;
  }

  DDS::InstanceHandleSeq handles;
  if (participant->get_discovered_participants(handles) != DDS::RETCODE_OK) {
    RMW_SET_ERROR_MSG("unable to fetch discovered participants.");
    return RMW_RET_ERROR;
  }

  for (DDS::Long i = 0; i < handles.length(); ++i) {
    DDS::ParticipantBuiltinTopicData pbtd;
    auto dds_ret = participant->get_discovered_participant_data(pbtd, handles[i]);
    if (dds_ret == DDS::RETCODE_OK) {
      uint8_t * buf = pbtd.user_data.value.get_contiguous_buffer();
      if (buf) {
        std::vector<uint8_t> kv(buf, buf + pbtd.user_data.value.length());
        auto map = rmw::impl::cpp::parse_key_value(kv);
        auto name_found = map.find("name");
        auto ns_found = map.find("namespace");

        if (name_found != map.end() && ns_found != map.end()) {
          std::string name(name_found->second.begin(), name_found->second.end());
          std::string ns(ns_found->second.begin(), ns_found->second.end());
          if ((name == node_name) && (ns == node_namespace)) {
            DDS_BuiltinTopicKey_to_GUID(&key, pbtd.key);
            return RMW_RET_OK;
          }
        }
      }
    } else {
      RMW_SET_ERROR_MSG("unable to fetch discovered participants data.");
      return RMW_RET_ERROR;
    }
  }
  RMW_SET_ERROR_MSG("unable to match node_name/namespace with discovered nodes.");
  return RMW_RET_ERROR;
}

rmw_ret_t
validate_names_and_namespace(
  const char * node_name,
  const char * node_namespace)
{
  if (!node_name) {
    RMW_SET_ERROR_MSG("null node name");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (!node_namespace) {
    RMW_SET_ERROR_MSG("null node namespace");
    return RMW_RET_INVALID_ARGUMENT;
  }
  return RMW_RET_OK;
}

rmw_ret_t
get_subscriber_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  bool no_demangle,
  rmw_names_and_types_t * topic_names_and_types)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(topic_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }
  ret = validate_names_and_namespace(node_name, node_namespace);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> topics;
  node_info->subscriber_listener->fill_topic_names_and_types_by_guid(no_demangle, topics, key);

  return copy_topics_names_and_types(topics, allocator, no_demangle, topic_names_and_types);
}

rmw_ret_t
get_publisher_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  bool no_demangle,
  rmw_names_and_types_t * topic_names_and_types)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(topic_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }
  ret = validate_names_and_namespace(node_name, node_namespace);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> topics;
  node_info->publisher_listener->fill_topic_names_and_types_by_guid(no_demangle, topics, key);

  return copy_topics_names_and_types(topics, allocator, no_demangle, topic_names_and_types);
}

static
rmw_ret_t
__get_service_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types,
  const char * suffix)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(service_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> services;
  node_info->subscriber_listener->fill_service_names_and_types_by_guid(services, key, suffix);

  rmw_ret_t rmw_ret =
    copy_services_to_names_and_types(services, allocator, service_names_and_types);
  if (rmw_ret != RMW_RET_OK) {
    return rmw_ret;
  }

  return RMW_RET_OK;
}

rmw_ret_t
get_service_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types)
{
  return __get_service_names_and_types_by_node(
    implementation_identifier,
    node,
    allocator,
    node_name,
    node_namespace,
    service_names_and_types,
    "Request");
}

rmw_ret_t
get_client_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types)
{
  return __get_service_names_and_types_by_node(
    implementation_identifier,
    node,
    allocator,
    node_name,
    node_namespace,
    service_names_and_types,
    "Reply");
}
<commit_msg>Return specific error when not finding a node name (#365)<commit_after>// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// 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 <string.h>

#include <functional>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "rcutils/allocator.h"
#include "rcutils/error_handling.h"
#include "rcutils/logging_macros.h"
#include "rcutils/strdup.h"
#include "rcutils/types.h"

#include "rmw/allocators.h"
#include "rmw/convert_rcutils_ret_to_rmw_ret.h"
#include "rmw/error_handling.h"
#include "rmw/get_topic_names_and_types.h"
#include "rmw/impl/cpp/macros.hpp"
#include "rmw/impl/cpp/key_value.hpp"
#include "rmw/names_and_types.h"
#include "rmw/rmw.h"

#include "rmw_connext_shared_cpp/node_info_and_types.hpp"
#include "rmw_connext_shared_cpp/types.hpp"
#include "rmw_connext_shared_cpp/names_and_types_helpers.hpp"
#include "rmw_connext_shared_cpp/ndds_include.hpp"
#include "rmw_connext_shared_cpp/guid_helper.hpp"

/**
 * Check to see if a node name and namespace match the user data QoS policy
 * of a node.
 *
 * @param user_data_qos to inspect
 * @param node_name to match
 * @param node_namespace to match
 * @return true if match
 */
bool
__is_node_match(
  DDS::UserDataQosPolicy & user_data_qos,
  const char * node_name,
  const char * node_namespace)
{
  uint8_t * buf = user_data_qos.value.get_contiguous_buffer();
  if (buf) {
    std::vector<uint8_t> kv(buf, buf + user_data_qos.value.length());
    auto map = rmw::impl::cpp::parse_key_value(kv);
    auto name_found = map.find("name");
    auto ns_found = map.find("namespace");

    if (name_found != map.end() && ns_found != map.end()) {
      std::string name(name_found->second.begin(), name_found->second.end());
      std::string ns(ns_found->second.begin(), ns_found->second.end());
      return (name == node_name) && (ns == node_namespace);
    }
  }
  return false;
}

/**
 * Get a DDS GUID key for the discovered participant which matches the
 * node_name and node_namepace supplied.
 *
 * @param node_info to discover nodes
 * @param node_name to match
 * @param node_namespace to match
 * @param key [out] guid key that matches the node name and namespace
 *
 * @return RMW_RET_OK if success, ERROR otherwise
 */
rmw_ret_t
__get_key(
  ConnextNodeInfo * node_info,
  const char * node_name,
  const char * node_namespace,
  DDS::GUID_t & key)
{
  auto participant = node_info->participant;
  RMW_CHECK_FOR_NULL_WITH_MSG(participant, "participant handle is null", return RMW_RET_ERROR);

  DDS::DomainParticipantQos dpqos;
  auto dds_ret = participant->get_qos(dpqos);
  if (dds_ret == DDS::RETCODE_OK && __is_node_match(dpqos.user_data, node_name, node_namespace)) {
    DDS_InstanceHandle_to_GUID(&key, participant->get_instance_handle());
    return RMW_RET_OK;
  }

  DDS::InstanceHandleSeq handles;
  if (participant->get_discovered_participants(handles) != DDS::RETCODE_OK) {
    RMW_SET_ERROR_MSG("unable to fetch discovered participants.");
    return RMW_RET_ERROR;
  }

  for (DDS::Long i = 0; i < handles.length(); ++i) {
    DDS::ParticipantBuiltinTopicData pbtd;
    auto dds_ret = participant->get_discovered_participant_data(pbtd, handles[i]);
    if (dds_ret == DDS::RETCODE_OK) {
      uint8_t * buf = pbtd.user_data.value.get_contiguous_buffer();
      if (buf) {
        std::vector<uint8_t> kv(buf, buf + pbtd.user_data.value.length());
        auto map = rmw::impl::cpp::parse_key_value(kv);
        auto name_found = map.find("name");
        auto ns_found = map.find("namespace");

        if (name_found != map.end() && ns_found != map.end()) {
          std::string name(name_found->second.begin(), name_found->second.end());
          std::string ns(ns_found->second.begin(), ns_found->second.end());
          if ((name == node_name) && (ns == node_namespace)) {
            DDS_BuiltinTopicKey_to_GUID(&key, pbtd.key);
            return RMW_RET_OK;
          }
        }
      }
    } else {
      RMW_SET_ERROR_MSG("unable to fetch discovered participants data.");
      return RMW_RET_ERROR;
    }
  }
  RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
    "Node name not found: ns='%s', name='%s",
    node_namespace,
    node_name
  );
  return RMW_RET_NODE_NAME_NON_EXISTENT;
}

rmw_ret_t
validate_names_and_namespace(
  const char * node_name,
  const char * node_namespace)
{
  if (!node_name) {
    RMW_SET_ERROR_MSG("null node name");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (!node_namespace) {
    RMW_SET_ERROR_MSG("null node namespace");
    return RMW_RET_INVALID_ARGUMENT;
  }
  return RMW_RET_OK;
}

rmw_ret_t
get_subscriber_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  bool no_demangle,
  rmw_names_and_types_t * topic_names_and_types)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(topic_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }
  ret = validate_names_and_namespace(node_name, node_namespace);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> topics;
  node_info->subscriber_listener->fill_topic_names_and_types_by_guid(no_demangle, topics, key);

  return copy_topics_names_and_types(topics, allocator, no_demangle, topic_names_and_types);
}

rmw_ret_t
get_publisher_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  bool no_demangle,
  rmw_names_and_types_t * topic_names_and_types)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(topic_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }
  ret = validate_names_and_namespace(node_name, node_namespace);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> topics;
  node_info->publisher_listener->fill_topic_names_and_types_by_guid(no_demangle, topics, key);

  return copy_topics_names_and_types(topics, allocator, no_demangle, topic_names_and_types);
}

static
rmw_ret_t
__get_service_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types,
  const char * suffix)
{
  if (!node) {
    RMW_SET_ERROR_MSG("null node handle");
    return RMW_RET_INVALID_ARGUMENT;
  }
  if (node->implementation_identifier != implementation_identifier) {
    RMW_SET_ERROR_MSG("node handle is not from this rmw implementation");
    return RMW_RET_ERROR;
  }

  rmw_ret_t ret = rmw_names_and_types_check_zero(service_names_and_types);
  if (ret != RMW_RET_OK) {
    return ret;
  }

  auto node_info = static_cast<ConnextNodeInfo *>(node->data);
  if (!node_info) {
    RMW_SET_ERROR_MSG("node info handle is null");
    return RMW_RET_ERROR;
  }

  DDS::GUID_t key;
  auto get_guid_err = __get_key(node_info, node_name, node_namespace, key);
  if (get_guid_err != RMW_RET_OK) {
    return get_guid_err;
  }

  // combine publisher and subscriber information
  std::map<std::string, std::set<std::string>> services;
  node_info->subscriber_listener->fill_service_names_and_types_by_guid(services, key, suffix);

  rmw_ret_t rmw_ret =
    copy_services_to_names_and_types(services, allocator, service_names_and_types);
  if (rmw_ret != RMW_RET_OK) {
    return rmw_ret;
  }

  return RMW_RET_OK;
}

rmw_ret_t
get_service_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types)
{
  return __get_service_names_and_types_by_node(
    implementation_identifier,
    node,
    allocator,
    node_name,
    node_namespace,
    service_names_and_types,
    "Request");
}

rmw_ret_t
get_client_names_and_types_by_node(
  const char * implementation_identifier,
  const rmw_node_t * node,
  rcutils_allocator_t * allocator,
  const char * node_name,
  const char * node_namespace,
  rmw_names_and_types_t * service_names_and_types)
{
  return __get_service_names_and_types_by_node(
    implementation_identifier,
    node,
    allocator,
    node_name,
    node_namespace,
    service_names_and_types,
    "Reply");
}
<|endoftext|>


<commit_before>/*
 * DefaultForwardChainerCB.cc
 *
 * Copyright (C) 2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "DefaultForwardChainerCB.h"
#include "PLNCommons.h"

#include <opencog/query/PatternMatch.h>
#include <opencog/guile/SchemeSmob.h>

DefaultForwardChainerCB::DefaultForwardChainerCB(
        AtomSpace* as, target_selection_mode ts_mode /*=TV_FITNESS_BASED*/) :
        ForwardChainerCallBack(as)
{
    as_ = as;
    fcim_ = new ForwardChainInputMatchCB(as);
    fcpm_ = new ForwardChainPatternMatchCB(as);
    ts_mode_ = ts_mode;
}

DefaultForwardChainerCB::~DefaultForwardChainerCB()
{
    delete fcim_;
    delete fcpm_;
}

/**
 * choose rule based on premises of rule matching the target
 * uses temporary atomspace to limit the search space
 *
 * @param fcmem forward chainer's working memory
 * @return a vector of chosen rules
 */

vector<Rule*> DefaultForwardChainerCB::choose_rule(FCMemory& fcmem)
{
    Handle target = fcmem.get_cur_target();
    if (target == Handle::UNDEFINED or NodeCast(target))
        throw InvalidParamException(TRACE_INFO,
                                    "Needs a target atom of type LINK");
    HandleSeq chosen_bindlinks;

    if (LinkCast(target)) {
        AtomSpace rule_atomspace;
        Handle target_cpy = rule_atomspace.addAtom(target);

        // Copy rules to the temporary atomspace.
        vector<Rule*> rules = fcmem.get_rules();
        for (Rule* r : rules) {
            rule_atomspace.addAtom(r->get_handle());
        }

        // Create bindlink with target as an implicant.
        PLNCommons pc(&rule_atomspace);
        Handle copy = pc.replace_nodes_with_varnode(target_cpy, NODE);
        Handle bind_link = pc.create_bindLink(copy, false);

        // Pattern match.
        DefaultImplicator imp(&rule_atomspace);
        try {
            PatternMatch pm;
            pm.do_bindlink(bind_link, imp);
        } catch (InvalidParamException& e) {
            cout << "VALIDATION FAILED:" << endl << e.what() << endl;
        }

        // Get matched bindLinks.
        HandleSeq matches = imp.result_list;
        if (matches.empty()) {
            logger().debug(
                    "No matching BindLink was found.Returning empty vector");
            return vector<Rule*> { };
        }

        HandleSeq bindlinks;
        for (Handle hm : matches) {
            //get all BindLinks whose part of their premise matches with hm
            HandleSeq hs = get_rootlinks(hm, &rule_atomspace, BIND_LINK);
            for (Handle hi : hs) {
                if (find(bindlinks.begin(), bindlinks.end(), hi) == bindlinks.end()) {
                    bindlinks.push_back(hi);
                }
            }
        }

        // Copy handles to main atomspace.
        for (Handle h : bindlinks) {
            chosen_bindlinks.push_back(as_->addAtom(h));
        }
    }

    // Try to find specialized rules that contain the target node.
    if (NodeCast(target)) {
        chosen_bindlinks = get_rootlinks(target, as_, BIND_LINK);
    }

    // Find the rules containing the bindLink in copied_back.
    vector<Rule*> matched_rules;
    vector<Rule*> rules = fcmem.get_rules();
    for (Rule* r : rules) {
        auto it = find(chosen_bindlinks.begin(), chosen_bindlinks.end(),
                       r->get_handle()); //xxx not matching
        if (it != chosen_bindlinks.end()) {
            matched_rules.push_back(r);
        }
    }

    return matched_rules;
}

/**
 * Gets all top level links of certain types and subclasses that contain @param htarget
 *
 * @param htarget handle whose top level links are to be searched
 * @param as the atomspace in which search is to be done
 * @param link_type the root link types to be searched
 * @param subclasses a flag that tells to look subclasses of @link_type
 */
HandleSeq DefaultForwardChainerCB::get_rootlinks(Handle htarget, AtomSpace* as,
                                                 Type link_type,
                                                 bool subclasses)
{
    auto outgoing = [as](Handle h) {return as->getOutgoing(h);};
    PLNCommons pc(as);
    HandleSeq chosen_roots;
    HandleSeq candidates_roots;
    pc.get_root_links(htarget, candidates_roots);

    for (Handle hr : candidates_roots) {
        bool notexist = find(chosen_roots.begin(), chosen_roots.end(), hr)
                == chosen_roots.end();
        auto type = as->getType(hr);
        bool subtype = (subclasses and classserver().isA(type, link_type));
        if (((type == link_type) or subtype) and notexist) {
            //make sure matches are actually part of the premise list rather than the output of the bindLink
            Handle hpremise = outgoing(outgoing(hr)[1])[0]; //extracting premise from (BindLink((ListLinK..)(ImpLink (premise) (..))))
            if (pc.exists_in(hpremise, htarget)) {
                chosen_roots.push_back(hr);
            }
        }

    }

    return chosen_roots;
}
HandleSeq DefaultForwardChainerCB::choose_input(FCMemory& fcmem)
{
    HandleSeq inputs;
    PLNCommons pc(as_);
    Handle htarget = fcmem.get_cur_target();

    //Get everything associated with the target handle.
    HandleSeq neighbors = as_->getNeighbors(htarget, true, true, LINK, true);

    //Add all root links of atoms in @param neighbors.
    for (auto hn : neighbors) {
        if (hn->getType() != VARIABLE_NODE) {
            HandleSeq roots;
            pc.get_root_links(hn, roots);
            for (auto r : roots) {
                if (find(inputs.begin(), inputs.end(), r) == inputs.end() and r->getType()
                        != BIND_LINK)
                    inputs.push_back(r);
            }
        }
    }

    return inputs;
}

Handle DefaultForwardChainerCB::choose_next_target(FCMemory& fcmem)
{
    HandleSeq tlist = fcmem.get_premise_list();
    map<Handle, float> tournament_elem;
    PLNCommons pc(as_);
    Handle hchosen = Handle::UNDEFINED;

    for (Handle t : tlist) {
        switch (ts_mode_) {
        case TV_FITNESS_BASED: {
            float fitness = pc.target_tv_fitness(t);
            tournament_elem[t] = fitness;
        }
            break;
        case STI_BASED:
            tournament_elem[t] = t->getSTI();
            break;
        default:
            throw RuntimeException(TRACE_INFO,
                                   "Unknown target selection mode.");
            break;
        }
    }

    //!Choose a new target that has never been chosen before.
    //!xxx FIXME since same handle might be chosen multiple times the following
    //!code doesn't guarantee all targets have been exhaustively looked.
    for (size_t i = 0; i < tournament_elem.size(); i++) {
        Handle hselected = pc.tournament_select(tournament_elem);
        if (fcmem.isin_target_list(hselected)) {
            continue;
        } else {
            hchosen = hselected;
            break;
        }
    }

    return hchosen;
}

//TODO applier should check on atoms (Inference.matched_atoms when Inference.Rule =Cur_Rule), for mutex rules
HandleSeq DefaultForwardChainerCB::apply_rule(FCMemory& fcmem)
{
    Rule * cur_rule = fcmem.get_cur_rule();
    fcpm_->set_fcmem(&fcmem);
    PatternMatch pm;
    try {
        pm.do_bindlink(cur_rule->get_handle(), *fcpm_);
    } catch (InvalidParamException& e) {
        cout << "VALIDATION FAILED:" << endl << e.what() << endl;
    }
    return fcpm_->get_products();
}
<commit_msg>Return new inferences only<commit_after>/*
 * DefaultForwardChainerCB.cc
 *
 * Copyright (C) 2015 Misgana Bayetta
 *
 * Author: Misgana Bayetta <misgana.bayetta@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "DefaultForwardChainerCB.h"
#include "PLNCommons.h"

#include <opencog/query/PatternMatch.h>
#include <opencog/guile/SchemeSmob.h>

DefaultForwardChainerCB::DefaultForwardChainerCB(
        AtomSpace* as, target_selection_mode ts_mode /*=TV_FITNESS_BASED*/) :
        ForwardChainerCallBack(as)
{
    as_ = as;
    fcim_ = new ForwardChainInputMatchCB(as);
    fcpm_ = new ForwardChainPatternMatchCB(as);
    ts_mode_ = ts_mode;
}

DefaultForwardChainerCB::~DefaultForwardChainerCB()
{
    delete fcim_;
    delete fcpm_;
}

/**
 * choose rule based on premises of rule matching the target
 * uses temporary atomspace to limit the search space
 *
 * @param fcmem forward chainer's working memory
 * @return a vector of chosen rules
 */

vector<Rule*> DefaultForwardChainerCB::choose_rule(FCMemory& fcmem)
{
    Handle target = fcmem.get_cur_target();
    if (target == Handle::UNDEFINED or NodeCast(target))
        throw InvalidParamException(TRACE_INFO,
                                    "Needs a target atom of type LINK");
    HandleSeq chosen_bindlinks;

    if (LinkCast(target)) {
        AtomSpace rule_atomspace;
        Handle target_cpy = rule_atomspace.addAtom(target);

        // Copy rules to the temporary atomspace.
        vector<Rule*> rules = fcmem.get_rules();
        for (Rule* r : rules) {
            rule_atomspace.addAtom(r->get_handle());
        }

        // Create bindlink with target as an implicant.
        PLNCommons pc(&rule_atomspace);
        Handle copy = pc.replace_nodes_with_varnode(target_cpy, NODE);
        Handle bind_link = pc.create_bindLink(copy, false);

        // Pattern match.
        DefaultImplicator imp(&rule_atomspace);
        try {
            PatternMatch pm;
            pm.do_bindlink(bind_link, imp);
        } catch (InvalidParamException& e) {
            cout << "VALIDATION FAILED:" << endl << e.what() << endl;
        }

        // Get matched bindLinks.
        HandleSeq matches = imp.result_list;
        if (matches.empty()) {
            logger().debug(
                    "No matching BindLink was found.Returning empty vector");
            return vector<Rule*> { };
        }

        HandleSeq bindlinks;
        for (Handle hm : matches) {
            //get all BindLinks whose part of their premise matches with hm
            HandleSeq hs = get_rootlinks(hm, &rule_atomspace, BIND_LINK);
            for (Handle hi : hs) {
                if (find(bindlinks.begin(), bindlinks.end(), hi) == bindlinks.end()) {
                    bindlinks.push_back(hi);
                }
            }
        }

        // Copy handles to main atomspace.
        for (Handle h : bindlinks) {
            chosen_bindlinks.push_back(as_->addAtom(h));
        }
    }

    // Try to find specialized rules that contain the target node.
    if (NodeCast(target)) {
        chosen_bindlinks = get_rootlinks(target, as_, BIND_LINK);
    }

    // Find the rules containing the bindLink in copied_back.
    vector<Rule*> matched_rules;
    vector<Rule*> rules = fcmem.get_rules();
    for (Rule* r : rules) {
        auto it = find(chosen_bindlinks.begin(), chosen_bindlinks.end(),
                       r->get_handle()); //xxx not matching
        if (it != chosen_bindlinks.end()) {
            matched_rules.push_back(r);
        }
    }

    return matched_rules;
}

/**
 * Gets all top level links of certain types and subclasses that contain @param htarget
 *
 * @param htarget handle whose top level links are to be searched
 * @param as the atomspace in which search is to be done
 * @param link_type the root link types to be searched
 * @param subclasses a flag that tells to look subclasses of @link_type
 */
HandleSeq DefaultForwardChainerCB::get_rootlinks(Handle htarget, AtomSpace* as,
                                                 Type link_type,
                                                 bool subclasses)
{
    auto outgoing = [as](Handle h) {return as->getOutgoing(h);};
    PLNCommons pc(as);
    HandleSeq chosen_roots;
    HandleSeq candidates_roots;
    pc.get_root_links(htarget, candidates_roots);

    for (Handle hr : candidates_roots) {
        bool notexist = find(chosen_roots.begin(), chosen_roots.end(), hr)
                == chosen_roots.end();
        auto type = as->getType(hr);
        bool subtype = (subclasses and classserver().isA(type, link_type));
        if (((type == link_type) or subtype) and notexist) {
            //make sure matches are actually part of the premise list rather than the output of the bindLink
            Handle hpremise = outgoing(outgoing(hr)[1])[0]; //extracting premise from (BindLink((ListLinK..)(ImpLink (premise) (..))))
            if (pc.exists_in(hpremise, htarget)) {
                chosen_roots.push_back(hr);
            }
        }

    }

    return chosen_roots;
}
HandleSeq DefaultForwardChainerCB::choose_input(FCMemory& fcmem)
{
    HandleSeq inputs;
    PLNCommons pc(as_);
    Handle htarget = fcmem.get_cur_target();

    //Get everything associated with the target handle.
    HandleSeq neighbors = as_->getNeighbors(htarget, true, true, LINK, true);

    //Add all root links of atoms in @param neighbors.
    for (auto hn : neighbors) {
        if (hn->getType() != VARIABLE_NODE) {
            HandleSeq roots;
            pc.get_root_links(hn, roots);
            for (auto r : roots) {
                if (find(inputs.begin(), inputs.end(), r) == inputs.end() and r->getType()
                        != BIND_LINK)
                    inputs.push_back(r);
            }
        }
    }

    return inputs;
}

Handle DefaultForwardChainerCB::choose_next_target(FCMemory& fcmem)
{
    HandleSeq tlist = fcmem.get_premise_list();
    map<Handle, float> tournament_elem;
    PLNCommons pc(as_);
    Handle hchosen = Handle::UNDEFINED;

    for (Handle t : tlist) {
        switch (ts_mode_) {
        case TV_FITNESS_BASED: {
            float fitness = pc.target_tv_fitness(t);
            tournament_elem[t] = fitness;
        }
            break;
        case STI_BASED:
            tournament_elem[t] = t->getSTI();
            break;
        default:
            throw RuntimeException(TRACE_INFO,
                                   "Unknown target selection mode.");
            break;
        }
    }

    //!Choose a new target that has never been chosen before.
    //!xxx FIXME since same handle might be chosen multiple times the following
    //!code doesn't guarantee all targets have been exhaustively looked.
    for (size_t i = 0; i < tournament_elem.size(); i++) {
        Handle hselected = pc.tournament_select(tournament_elem);
        if (fcmem.isin_target_list(hselected)) {
            continue;
        } else {
            hchosen = hselected;
            break;
        }
    }

    return hchosen;
}

//TODO applier should check on atoms (Inference.matched_atoms when Inference.Rule =Cur_Rule), for mutex rules
HandleSeq DefaultForwardChainerCB::apply_rule(FCMemory& fcmem)
{
    Rule * cur_rule = fcmem.get_cur_rule();
    fcpm_->set_fcmem(&fcmem);
    PatternMatch pm;
    try {
        pm.do_bindlink(cur_rule->get_handle(), *fcpm_);
    } catch (InvalidParamException& e) {
        cout << "VALIDATION FAILED:" << endl << e.what() << endl;
    }

    HandleSeq product = fcpm_->get_products();

    //! Make sure the inferences made are new.
    HandleSeq new_product;
    for (auto h : product) {
        if (not fcmem.isin_premise_list(h))
            new_product.push_back(h);
    }

    return new_product;
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2008, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <cassert>

#include "boost/format.hpp"

#include "IECoreGL/ToGLMeshConverter.h"
#include "IECoreGL/MeshPrimitive.h"

#include "IECore/MeshPrimitive.h"
#include "IECore/TriangulateOp.h"
#include "IECore/MeshNormalsOp.h"
#include "IECore/DespatchTypedData.h"
#include "IECore/MessageHandler.h"

using namespace IECoreGL;

class ToGLMeshConverter::ToFaceVaryingConverter
{
	public:

		typedef IECore::DataPtr ReturnType;

		ToFaceVaryingConverter( IECore::ConstIntVectorDataPtr vertIds ) : m_vertIds( vertIds )
		{
			assert( m_vertIds );
		}

		template<typename T>
		IECore::DataPtr operator()( typename T::Ptr inData )
		{
			assert( inData );

			const typename T::Ptr outData = new T();
			outData->writable().resize( m_vertIds->readable().size() );

			typename T::ValueType::iterator outIt = outData->writable().begin();

			for ( typename T::ValueType::size_type i = 0; i <  m_vertIds->readable().size(); i++ )
			{
				*outIt++ = inData->readable()[ m_vertIds->readable()[ i ] ];
			}

			return outData;
		}

		IECore::ConstIntVectorDataPtr m_vertIds;
};

ToGLMeshConverter::ToGLMeshConverter( IECore::ConstMeshPrimitivePtr toConvert )
	:	ToGLConverter( staticTypeName(), "Converts IECore::MeshPrimitive objects to IECoreGL::MeshPrimitive objects.", IECore::MeshPrimitiveTypeId )
{
	srcParameter()->setValue( boost::const_pointer_cast<IECore::MeshPrimitive>( toConvert ) );
}

ToGLMeshConverter::~ToGLMeshConverter()
{
}

IECore::RunTimeTypedPtr ToGLMeshConverter::doConversion( IECore::ConstObjectPtr src, IECore::ConstCompoundObjectPtr operands ) const
{
	IECore::MeshPrimitivePtr mesh = boost::static_pointer_cast<IECore::MeshPrimitive>( src->copy() ); // safe because the parameter validated it for us

	IECore::TriangulateOpPtr op = new IECore::TriangulateOp();
	op->inputParameter()->setValue( mesh );
	op->throwExceptionsParameter()->setTypedValue( false ); // it's better to see something than nothing

	mesh = IECore::runTimeCast< IECore::MeshPrimitive > ( op->operate() );
	assert( mesh );

	IECore::ConstV3fVectorDataPtr p = 0;
	IECore::PrimitiveVariableMap::const_iterator pIt = mesh->variables.find( "P" );
	if( pIt!=mesh->variables.end() )
	{
		if( pIt->second.interpolation==IECore::PrimitiveVariable::Vertex )
		{
			p = IECore::runTimeCast<const IECore::V3fVectorData>( pIt->second.data );
		}
	}
	if( !p )
	{
		throw IECore::Exception( "Must specify primitive variable \"P\", of type V3fVectorData and interpolation type Vertex." );
	}

	IECore::ConstV3fVectorDataPtr n = 0;
	IECore::PrimitiveVariableMap::const_iterator nIt = mesh->variables.find( "N" );
	if( nIt != mesh->variables.end() )
	{
		if( nIt->second.interpolation==IECore::PrimitiveVariable::Vertex || nIt->second.interpolation==IECore::PrimitiveVariable::Varying || nIt->second.interpolation==IECore::PrimitiveVariable::FaceVarying )
		{
			n = IECore::runTimeCast<const IECore::V3fVectorData>( nIt->second.data );
		}
		if( !n )
		{
			throw IECore::Exception( "Must specify primitive variable \"N\", of type V3fVectorData" );
		}
	}
	else
	{
		IECore::MeshNormalsOpPtr normOp = new IECore::MeshNormalsOp();
		normOp->inputParameter()->setValue( mesh );

		mesh = IECore::runTimeCast< IECore::MeshPrimitive > ( normOp->operate() );
		assert( mesh );

		nIt = mesh->variables.find( "N" );
		assert( nIt != mesh->variables.end() );
		n = IECore::runTimeCast<const IECore::V3fVectorData>( nIt->second.data );
	}
	assert( n );

	MeshPrimitivePtr glMesh = new MeshPrimitive( mesh->vertexIds(), p );

	ToFaceVaryingConverter primVarConverter( mesh->vertexIds() );

	for ( IECore::PrimitiveVariableMap::iterator pIt = mesh->variables.begin(); pIt != mesh->variables.end(); ++pIt )
	{
		if ( pIt->second.data )
		{
			if ( pIt->second.interpolation==IECore::PrimitiveVariable::Vertex || pIt->second.interpolation==IECore::PrimitiveVariable::Varying )
			{
				IECore::DataPtr newData = IECore::despatchTypedData< ToFaceVaryingConverter, IECore::TypeTraits::IsVectorTypedData >( pIt->second.data, primVarConverter );
				pIt->second.interpolation = IECore::PrimitiveVariable::FaceVarying;
				glMesh->addVertexAttribute( pIt->first, newData );
			}
			else if ( pIt->second.interpolation==IECore::PrimitiveVariable::FaceVarying )
			{
				glMesh->addVertexAttribute( pIt->first, pIt->second.data );
			}
		}
		else
		{
			IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", boost::format( "No data given for primvar \"%s\"" ) % pIt->first );
		}
	}

	IECore::PrimitiveVariableMap::const_iterator sIt = mesh->variables.find( "s" );
	IECore::PrimitiveVariableMap::const_iterator tIt = mesh->variables.find( "t" );
	if ( sIt != mesh->variables.end() && tIt != mesh->variables.end() )
	{
		if ( sIt->second.interpolation == IECore::PrimitiveVariable::FaceVarying
			&&  tIt->second.interpolation == IECore::PrimitiveVariable::FaceVarying )
		{
			IECore::ConstFloatVectorDataPtr s = IECore::runTimeCast< const IECore::FloatVectorData >( sIt->second.data );
			IECore::ConstFloatVectorDataPtr t = IECore::runTimeCast< const IECore::FloatVectorData >( tIt->second.data );

			if ( s && t )
			{
				/// Should hold true if primvarsAreValid
				assert( s->readable().size() == t->readable().size() );

				IECore::V2fVectorDataPtr stData = new IECore::V2fVectorData();
				stData->writable().resize( s->readable().size() );

				for ( unsigned i = 0; i < s->readable().size(); i++ )
				{
					stData->writable()[i] = Imath::V2f( s->readable()[i], t->readable()[i] );
				}

				glMesh->addVertexAttribute( "st", stData );
			}
			else
			{
				IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "If specified, primitive variables \"s\" and \"t\" must be of type FloatVectorData and interpolation type FaceVarying." );
			}
		}
		else
		{
			IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "If specified, primitive variables \"s\" and \"t\" must be of type FloatVectorData and interpolation type FaceVarying." );
		}
	}
	else if ( sIt != mesh->variables.end() || tIt != mesh->variables.end() )
	{
		IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "Primitive variable \"s\" or \"t\" found, but not both." );
	}

	return glMesh;
}
<commit_msg>Fixed a bug which meant that vertex s,t primvars would add invalid data to the gl mesh. They are now converted to facevarying and added.<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2008, Image Engine Design Inc. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the name of Image Engine Design nor the names of any
//       other contributors to this software may be used to endorse or
//       promote products derived from this software without specific prior
//       written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
//  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
//  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
//  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
//  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
//  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////

#include <cassert>

#include "boost/format.hpp"

#include "IECoreGL/ToGLMeshConverter.h"
#include "IECoreGL/MeshPrimitive.h"

#include "IECore/MeshPrimitive.h"
#include "IECore/TriangulateOp.h"
#include "IECore/MeshNormalsOp.h"
#include "IECore/DespatchTypedData.h"
#include "IECore/MessageHandler.h"

using namespace IECoreGL;

/// \todo This should be in a standalone Op, and should cope with more than just Vertex interpolated input.
class ToGLMeshConverter::ToFaceVaryingConverter
{
	public:

		typedef IECore::DataPtr ReturnType;

		ToFaceVaryingConverter( IECore::ConstIntVectorDataPtr vertIds ) : m_vertIds( vertIds )
		{
			assert( m_vertIds );
		}

		template<typename T>
		IECore::DataPtr operator()( typename T::Ptr inData )
		{
			assert( inData );

			const typename T::Ptr outData = new T();
			outData->writable().resize( m_vertIds->readable().size() );

			typename T::ValueType::iterator outIt = outData->writable().begin();

			for ( typename T::ValueType::size_type i = 0; i <  m_vertIds->readable().size(); i++ )
			{
				*outIt++ = inData->readable()[ m_vertIds->readable()[ i ] ];
			}

			return outData;
		}

		IECore::ConstIntVectorDataPtr m_vertIds;
};

ToGLMeshConverter::ToGLMeshConverter( IECore::ConstMeshPrimitivePtr toConvert )
	:	ToGLConverter( staticTypeName(), "Converts IECore::MeshPrimitive objects to IECoreGL::MeshPrimitive objects.", IECore::MeshPrimitiveTypeId )
{
	srcParameter()->setValue( boost::const_pointer_cast<IECore::MeshPrimitive>( toConvert ) );
}

ToGLMeshConverter::~ToGLMeshConverter()
{
}

IECore::RunTimeTypedPtr ToGLMeshConverter::doConversion( IECore::ConstObjectPtr src, IECore::ConstCompoundObjectPtr operands ) const
{
	IECore::MeshPrimitivePtr mesh = boost::static_pointer_cast<IECore::MeshPrimitive>( src->copy() ); // safe because the parameter validated it for us

	IECore::TriangulateOpPtr op = new IECore::TriangulateOp();
	op->inputParameter()->setValue( mesh );
	op->throwExceptionsParameter()->setTypedValue( false ); // it's better to see something than nothing

	mesh = IECore::runTimeCast< IECore::MeshPrimitive > ( op->operate() );
	assert( mesh );

	IECore::ConstV3fVectorDataPtr p = 0;
	IECore::PrimitiveVariableMap::const_iterator pIt = mesh->variables.find( "P" );
	if( pIt!=mesh->variables.end() )
	{
		if( pIt->second.interpolation==IECore::PrimitiveVariable::Vertex )
		{
			p = IECore::runTimeCast<const IECore::V3fVectorData>( pIt->second.data );
		}
	}
	if( !p )
	{
		throw IECore::Exception( "Must specify primitive variable \"P\", of type V3fVectorData and interpolation type Vertex." );
	}

	IECore::ConstV3fVectorDataPtr n = 0;
	IECore::PrimitiveVariableMap::const_iterator nIt = mesh->variables.find( "N" );
	if( nIt != mesh->variables.end() )
	{
		if( nIt->second.interpolation==IECore::PrimitiveVariable::Vertex || nIt->second.interpolation==IECore::PrimitiveVariable::Varying || nIt->second.interpolation==IECore::PrimitiveVariable::FaceVarying )
		{
			n = IECore::runTimeCast<const IECore::V3fVectorData>( nIt->second.data );
		}
		if( !n )
		{
			throw IECore::Exception( "Must specify primitive variable \"N\", of type V3fVectorData" );
		}
	}
	else
	{
		IECore::MeshNormalsOpPtr normOp = new IECore::MeshNormalsOp();
		normOp->inputParameter()->setValue( mesh );

		mesh = IECore::runTimeCast< IECore::MeshPrimitive > ( normOp->operate() );
		assert( mesh );

		nIt = mesh->variables.find( "N" );
		assert( nIt != mesh->variables.end() );
		n = IECore::runTimeCast<const IECore::V3fVectorData>( nIt->second.data );
	}
	assert( n );

	MeshPrimitivePtr glMesh = new MeshPrimitive( mesh->vertexIds(), p );

	ToFaceVaryingConverter primVarConverter( mesh->vertexIds() );

	for ( IECore::PrimitiveVariableMap::iterator pIt = mesh->variables.begin(); pIt != mesh->variables.end(); ++pIt )
	{
		if ( pIt->second.data )
		{
			if ( pIt->second.interpolation==IECore::PrimitiveVariable::Vertex || pIt->second.interpolation==IECore::PrimitiveVariable::Varying )
			{
				// convert to facevarying
				IECore::DataPtr newData = IECore::despatchTypedData< ToFaceVaryingConverter, IECore::TypeTraits::IsVectorTypedData >( pIt->second.data, primVarConverter );
				glMesh->addVertexAttribute( pIt->first, newData );
				// modify the primvar we converted in case it is "s" or "t", then we don't have to do another conversion below.
				pIt->second.interpolation = IECore::PrimitiveVariable::FaceVarying;
				pIt->second.data = newData;
			}
			else if ( pIt->second.interpolation==IECore::PrimitiveVariable::FaceVarying )
			{
				glMesh->addVertexAttribute( pIt->first, pIt->second.data );
			}
		}
		else
		{
			IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", boost::format( "No data given for primvar \"%s\"" ) % pIt->first );
		}
	}

	IECore::PrimitiveVariableMap::const_iterator sIt = mesh->variables.find( "s" );
	IECore::PrimitiveVariableMap::const_iterator tIt = mesh->variables.find( "t" );
	if ( sIt != mesh->variables.end() && tIt != mesh->variables.end() )
	{
		if ( sIt->second.interpolation == IECore::PrimitiveVariable::FaceVarying
			&&  tIt->second.interpolation == IECore::PrimitiveVariable::FaceVarying )
		{
			IECore::ConstFloatVectorDataPtr s = IECore::runTimeCast< const IECore::FloatVectorData >( sIt->second.data );
			IECore::ConstFloatVectorDataPtr t = IECore::runTimeCast< const IECore::FloatVectorData >( tIt->second.data );

			if ( s && t )
			{
				/// Should hold true if primvarsAreValid
				assert( s->readable().size() == t->readable().size() );

				IECore::V2fVectorDataPtr stData = new IECore::V2fVectorData();
				stData->writable().resize( s->readable().size() );

				for ( unsigned i = 0; i < s->readable().size(); i++ )
				{
					stData->writable()[i] = Imath::V2f( s->readable()[i], t->readable()[i] );
				}

				glMesh->addVertexAttribute( "st", stData );
			}
			else
			{
				IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "If specified, primitive variables \"s\" and \"t\" must be of type FloatVectorData and interpolation type FaceVarying." );
			}
		}
		else
		{
			IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "If specified, primitive variables \"s\" and \"t\" must be of type FloatVectorData and interpolation type FaceVarying." );
		}
	}
	else if ( sIt != mesh->variables.end() || tIt != mesh->variables.end() )
	{
		IECore::msg( IECore::Msg::Warning, "ToGLMeshConverter", "Primitive variable \"s\" or \"t\" found, but not both." );
	}

	return glMesh;
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2014, Paul Houx - All rights reserved.
 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

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

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

#include "cinder/app/App.h"
#include "cinder/Log.h"

#include "SMAA.h"
#include "AreaTex.h"
#include "SearchTex.h"

using namespace ci;
using namespace ci::app;
using namespace std;

SMAA::SMAA()
{
	// Load and compile our shaders
	try {
		// Define the format by specifying the vertex and fragment shader files and defining the quality settings.
		auto fmt = gl::GlslProg::Format().vertex( loadAsset( "smaa.vert" ) ).fragment( loadAsset( "smaa.frag" ) ).define( "SMAA_PRESET_ULTRA" ).define( "SMAA_GLSL_3", "1" );
		// Each pass uses the same files, but with a different value for the SMAA_PASS pre-processor directive.
		mGlslFirstPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "1" ) );
		mGlslSecondPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "2" ) );
		mGlslThirdPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "3" ) );
	}
	catch( const std::exception& e ) {
		CI_LOG_EXCEPTION( "exception caught loading smaa shaders", e );
	}

	// Create lookup textures
	gl::Texture2d::Format fmt;
	fmt.setMinFilter( GL_LINEAR );
	fmt.setMagFilter( GL_LINEAR );
	fmt.setWrap( GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE );

	// Search Texture (Grayscale, 8 bits unsigned).
	fmt.setInternalFormat( GL_R8 );
	fmt.setSwizzleMask( GL_RED, GL_RED, GL_RED, GL_ONE );
	fmt.loadTopDown( true );
	mSearchTex = gl::Texture2d::create( searchTexBytes, GL_RED, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, fmt );

	// Area Texture (Red+Green Channels, 8 bits unsigned).
	fmt.setInternalFormat( GL_RG8 );
	fmt.setSwizzleMask( GL_RED, GL_GREEN, GL_ZERO, GL_ONE );
	fmt.loadTopDown( true );
	mAreaTex = gl::Texture2d::create( areaTexBytes, GL_RG, AREATEX_WIDTH, AREATEX_HEIGHT, fmt );

	// Specify the Fbo format.
	gl::Texture2d::Format tfmt;
	tfmt.setMinFilter( GL_LINEAR );
	tfmt.setMagFilter( GL_LINEAR );
	tfmt.setInternalFormat( GL_RGBA8 );

	mFboFormat.setColorTextureFormat( tfmt );
	mFboFormat.enableDepthBuffer( false );
}

void SMAA::apply( const gl::FboRef &destination, const gl::FboRef &source )
{
	gl::ScopedFramebuffer fbo( destination );
	gl::ScopedViewport viewport( 0, 0, destination->getWidth(), destination->getHeight() );
	gl::ScopedMatrices matrices;
	gl::setMatricesWindow( destination->getSize() );

	// Make sure our source is linearly interpolated.
	GLenum minFilter = source->getFormat().getColorTextureFormat().getMinFilter();
	GLenum magFilter = source->getFormat().getColorTextureFormat().getMagFilter();
	bool filterChanged = ( minFilter != GL_LINEAR || magFilter != GL_LINEAR );
	if( filterChanged ) {
		source->getColorTexture()->setMinFilter( GL_LINEAR );
		source->getColorTexture()->setMagFilter( GL_LINEAR );
	}

	// Perform SMAA anti-aliasing.
	gl::clear( ColorA( 0, 0, 0, 0 ) );
	draw( source->getColorTexture(), destination->getBounds() );

	// Restore texture parameters.
	if( filterChanged ) {
		source->getColorTexture()->setMinFilter( minFilter );
		source->getColorTexture()->setMagFilter( magFilter );
	}
}

void SMAA::draw( const gl::Texture2dRef &source, const Area &bounds )
{
	if( ! mGlslFirstPass || ! mGlslSecondPass || ! mGlslThirdPass )
		return;

	// Create or resize buffers.
	const int width = source->getWidth();
	const int height = source->getHeight();
	createBuffers( width, height );

	// Apply first two passes.
	doEdgePass( source );
	doBlendPass();

	// Apply SMAA.
	gl::ScopedTextureBind tex0( source );
	gl::ScopedTextureBind tex1( mFboBlendPass->getColorTexture(), 1 );
	gl::ScopedGlslProg glslScope( mGlslThirdPass );
	mGlslThirdPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslThirdPass->uniform( "uColorTex", 0 );
	mGlslThirdPass->uniform( "uBlendTex", 1 );

	gl::color( Color::white() );
	gl::drawSolidRect( bounds );

	//	gl::draw( mAreaTex );
}

gl::TextureRef SMAA::getEdgePass()
{
	return mFboEdgePass->getColorTexture();
}

gl::TextureRef SMAA::getBlendPass()
{
	return mFboBlendPass->getColorTexture();
}

void SMAA::createBuffers( int width, int height )
{
	// Create or resize frame buffers
	if( !mFboEdgePass || mFboEdgePass->getWidth() != width || mFboEdgePass->getHeight() != height ) {
		mFboEdgePass = gl::Fbo::create( width, height, mFboFormat );
	}

	if( !mFboBlendPass || mFboBlendPass->getWidth() != width || mFboBlendPass->getHeight() != height ) {
		mFboBlendPass = gl::Fbo::create( width, height, mFboFormat );
	}

	mMetrics = vec4( 1.0f / width, 1.0f / height, (float) width, (float) height );
}

void SMAA::doEdgePass( const gl::Texture2dRef &source )
{
	// Enable frame buffer, bind textures and shader.
	gl::ScopedFramebuffer fbo( mFboEdgePass );
	gl::clear( ColorA( 0, 0, 0, 0 ) );

	gl::ScopedTextureBind tex0( source );
	gl::ScopedGlslProg glslScope( mGlslFirstPass );
	mGlslFirstPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslFirstPass->uniform( "uColorTex", 0 );

	// Execute shader by drawing a 'full screen' rectangle.
	gl::color( Color::white() );
	gl::drawSolidRect( mFboEdgePass->getBounds() );
}

void SMAA::doBlendPass()
{
	// Enable frame buffer, bind textures and shader.
	gl::ScopedFramebuffer fbo( mFboBlendPass );
	gl::clear( ColorA( 0, 0, 0, 0 ) );

	gl::ScopedTextureBind tex0( mFboEdgePass->getColorTexture() );
	gl::ScopedTextureBind tex1( mAreaTex, 1 );
	gl::ScopedTextureBind tex2( mSearchTex, 2 );
	gl::ScopedGlslProg glslScope( mGlslSecondPass );
	mGlslSecondPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslSecondPass->uniform( "uEdgesTex", 0 );
	mGlslSecondPass->uniform( "uAreaTex", 1 );
	mGlslSecondPass->uniform( "uSearchTex", 2 );

	// Execute shader by drawing a 'full screen' rectangle.
	gl::color( Color::white() );
	gl::drawSolidRect( mFboBlendPass->getBounds() );
}<commit_msg>Removed 'loadTopDown' as it was ignored and triggered a warning.<commit_after>/*
 Copyright (c) 2014, Paul Houx - All rights reserved.
 This code is intended for use with the Cinder C++ library: http://libcinder.org

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

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

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

#include "cinder/app/App.h"
#include "cinder/Log.h"

#include "SMAA.h"
#include "AreaTex.h"
#include "SearchTex.h"

using namespace ci;
using namespace ci::app;
using namespace std;

SMAA::SMAA()
{
	// Load and compile our shaders
	try {
		// Define the format by specifying the vertex and fragment shader files and defining the quality settings.
		auto fmt = gl::GlslProg::Format().vertex( loadAsset( "smaa.vert" ) ).fragment( loadAsset( "smaa.frag" ) ).define( "SMAA_PRESET_ULTRA" ).define( "SMAA_GLSL_3", "1" );
		// Each pass uses the same files, but with a different value for the SMAA_PASS pre-processor directive.
		mGlslFirstPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "1" ) );
		mGlslSecondPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "2" ) );
		mGlslThirdPass = gl::GlslProg::create( gl::GlslProg::Format( fmt ).define( "SMAA_PASS", "3" ) );
	}
	catch( const std::exception& e ) {
		CI_LOG_EXCEPTION( "exception caught loading smaa shaders", e );
	}

	// Create lookup textures
	gl::Texture2d::Format fmt;
	fmt.setMinFilter( GL_LINEAR );
	fmt.setMagFilter( GL_LINEAR );
	fmt.setWrap( GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE );

	// Search Texture (Grayscale, 8 bits unsigned).
	fmt.setInternalFormat( GL_R8 );
	fmt.setSwizzleMask( GL_RED, GL_RED, GL_RED, GL_ONE );
	mSearchTex = gl::Texture2d::create( searchTexBytes, GL_RED, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, fmt );

	// Area Texture (Red+Green Channels, 8 bits unsigned).
	fmt.setInternalFormat( GL_RG8 );
	fmt.setSwizzleMask( GL_RED, GL_GREEN, GL_ZERO, GL_ONE );
	mAreaTex = gl::Texture2d::create( areaTexBytes, GL_RG, AREATEX_WIDTH, AREATEX_HEIGHT, fmt );

	// Specify the Fbo format.
	gl::Texture2d::Format tfmt;
	tfmt.setMinFilter( GL_LINEAR );
	tfmt.setMagFilter( GL_LINEAR );
	tfmt.setInternalFormat( GL_RGBA8 );

	mFboFormat.setColorTextureFormat( tfmt );
	mFboFormat.enableDepthBuffer( false );
}

void SMAA::apply( const gl::FboRef &destination, const gl::FboRef &source )
{
	gl::ScopedFramebuffer fbo( destination );
	gl::ScopedViewport viewport( 0, 0, destination->getWidth(), destination->getHeight() );
	gl::ScopedMatrices matrices;
	gl::setMatricesWindow( destination->getSize() );

	// Make sure our source is linearly interpolated.
	GLenum minFilter = source->getFormat().getColorTextureFormat().getMinFilter();
	GLenum magFilter = source->getFormat().getColorTextureFormat().getMagFilter();
	bool filterChanged = ( minFilter != GL_LINEAR || magFilter != GL_LINEAR );
	if( filterChanged ) {
		source->getColorTexture()->setMinFilter( GL_LINEAR );
		source->getColorTexture()->setMagFilter( GL_LINEAR );
	}

	// Perform SMAA anti-aliasing.
	gl::clear( ColorA( 0, 0, 0, 0 ) );
	draw( source->getColorTexture(), destination->getBounds() );

	// Restore texture parameters.
	if( filterChanged ) {
		source->getColorTexture()->setMinFilter( minFilter );
		source->getColorTexture()->setMagFilter( magFilter );
	}
}

void SMAA::draw( const gl::Texture2dRef &source, const Area &bounds )
{
	if( ! mGlslFirstPass || ! mGlslSecondPass || ! mGlslThirdPass )
		return;

	// Create or resize buffers.
	const int width = source->getWidth();
	const int height = source->getHeight();
	createBuffers( width, height );

	// Apply first two passes.
	doEdgePass( source );
	doBlendPass();

	// Apply SMAA.
	gl::ScopedTextureBind tex0( source );
	gl::ScopedTextureBind tex1( mFboBlendPass->getColorTexture(), 1 );
	gl::ScopedGlslProg glslScope( mGlslThirdPass );
	mGlslThirdPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslThirdPass->uniform( "uColorTex", 0 );
	mGlslThirdPass->uniform( "uBlendTex", 1 );

	gl::color( Color::white() );
	gl::drawSolidRect( bounds );

	//	gl::draw( mAreaTex );
}

gl::TextureRef SMAA::getEdgePass()
{
	return mFboEdgePass->getColorTexture();
}

gl::TextureRef SMAA::getBlendPass()
{
	return mFboBlendPass->getColorTexture();
}

void SMAA::createBuffers( int width, int height )
{
	// Create or resize frame buffers
	if( !mFboEdgePass || mFboEdgePass->getWidth() != width || mFboEdgePass->getHeight() != height ) {
		mFboEdgePass = gl::Fbo::create( width, height, mFboFormat );
	}

	if( !mFboBlendPass || mFboBlendPass->getWidth() != width || mFboBlendPass->getHeight() != height ) {
		mFboBlendPass = gl::Fbo::create( width, height, mFboFormat );
	}

	mMetrics = vec4( 1.0f / width, 1.0f / height, (float) width, (float) height );
}

void SMAA::doEdgePass( const gl::Texture2dRef &source )
{
	// Enable frame buffer, bind textures and shader.
	gl::ScopedFramebuffer fbo( mFboEdgePass );
	gl::clear( ColorA( 0, 0, 0, 0 ) );

	gl::ScopedTextureBind tex0( source );
	gl::ScopedGlslProg glslScope( mGlslFirstPass );
	mGlslFirstPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslFirstPass->uniform( "uColorTex", 0 );

	// Execute shader by drawing a 'full screen' rectangle.
	gl::color( Color::white() );
	gl::drawSolidRect( mFboEdgePass->getBounds() );
}

void SMAA::doBlendPass()
{
	// Enable frame buffer, bind textures and shader.
	gl::ScopedFramebuffer fbo( mFboBlendPass );
	gl::clear( ColorA( 0, 0, 0, 0 ) );

	gl::ScopedTextureBind tex0( mFboEdgePass->getColorTexture() );
	gl::ScopedTextureBind tex1( mAreaTex, 1 );
	gl::ScopedTextureBind tex2( mSearchTex, 2 );
	gl::ScopedGlslProg glslScope( mGlslSecondPass );
	mGlslSecondPass->uniform( "SMAA_RT_METRICS", mMetrics );
	mGlslSecondPass->uniform( "uEdgesTex", 0 );
	mGlslSecondPass->uniform( "uAreaTex", 1 );
	mGlslSecondPass->uniform( "uSearchTex", 2 );

	// Execute shader by drawing a 'full screen' rectangle.
	gl::color( Color::white() );
	gl::drawSolidRect( mFboBlendPass->getBounds() );
}<|endoftext|>