<commit_before>#include <gtest/gtest.h>
#include <iostream>
#include <eigen3/Eigen/Dense>
#include <mrpt/vision/pnp_algos.h>

TEST(CPnP_Test, lhm_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

    obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_lhm(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 2);

}

TEST(CPnP_Test, p3p_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

    obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_p3p(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 0.5);

}

TEST(CPnP_Test, ppnp_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

	obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_ppnp(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 0.5);

}

TEST(CPnP_Test, posit_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n), i_pts(2,n);

    obj_pts << 0, 20,  10, 15, 14, 16,
               0, 0, -10, -5, -2, -5,
               0, 40,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

    i_pts=img_pts.block(0,0,2,n);

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_posit(obj_pts, i_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 2);

}<commit_msg>Added dls and epnp gtest to pnp-dev<commit_after>#include <gtest/gtest.h>
#include <iostream>
#include <eigen3/Eigen/Dense>
#include <mrpt/vision/pnp_algos.h>

TEST(CPnP_Test, lhm_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

    obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_lhm(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 2);

}

TEST(CPnP_Test, p3p_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

    obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_p3p(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 0.5);

}

TEST(CPnP_Test, ppnp_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n);

	obj_pts << 20, 0,  10, 15, 14, 16,
                0, 0, -10, -5, -2, -5,
               40, 0,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_ppnp(obj_pts, img_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 0.5);

}

TEST(CPnP_Test, posit_ALGO_TEST)
{
    int n = 6;

	Eigen::Matrix3d R(3, 3);
	Eigen::Vector3d t;
	R << -0.3536, 0.3536, -0.8660,
		0.9330, 0.0670, -0.3536,
		-0.0670, -0.9330, -0.3536;

	t << 20, -30, 100;

	Eigen::MatrixXd obj_pts(3, n), img_pts(3, n), i_pts(2,n);

    obj_pts << 0, 20,  10, 15, 14, 16,
               0, 0, -10, -5, -2, -5,
               0, 40,   0, 24, 21, 13;

	for (int i = 0; i < n; i++)
	{
		img_pts.col(i) = (R * obj_pts.col(i) + t);
		img_pts.col(i) /= img_pts(2,i);
	}

    i_pts=img_pts.block(0,0,2,n);

	Eigen::Matrix3d R_est;
	Eigen::Vector3d t_est;

    Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

    pnp::CPnP p;

    Eigen::MatrixXd pose_est(6,1);
    p.CPnP_posit(obj_pts, i_pts, n, I3, pose_est);

    t_est<<pose_est(0), pose_est(1), pose_est(2);

    double err_t = (t-t_est).norm();

    EXPECT_LE(err_t, 2);

}

#if MRPT_HAS_OPENCV

    TEST(CPnP_Test, epnp_ALGO_TEST)
    {
        int n = 6;

        Eigen::Matrix3d R(3, 3);
        Eigen::Vector3d t;
        R << -0.3536, 0.3536, -0.8660,
            0.9330, 0.0670, -0.3536,
            -0.0670, -0.9330, -0.3536;

        t << 20, -30, 100;

        Eigen::MatrixXd obj_pts(3, n), img_pts(3, n), i_pts(2,n);

        obj_pts << 20, 0,  10, 15, 14, 16,
                    0, 0, -10, -5, -2, -5,
                   40, 0,   0, 24, 21, 13;

        for (int i = 0; i < n; i++)
        {
            img_pts.col(i) = (R * obj_pts.col(i) + t);
            img_pts.col(i) /= img_pts(2,i);
        }

        Eigen::Matrix3d R_est;
        Eigen::Vector3d t_est;

        Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

        pnp::CPnP p;

        i_pts= img_pts.block(0,0,2,n);

        Eigen::MatrixXd pose_est(6,1);
        p.CPnP_epnp(obj_pts, i_pts, n, I3, pose_est);

        t_est<<pose_est(0), pose_est(1), pose_est(2);

        double err_t = (t-t_est).norm();

        EXPECT_LE(err_t, 2);

    }

    TEST(CPnP_Test, dls_ALGO_TEST)
    {
        int n = 6;

        Eigen::Matrix3d R(3, 3);
        Eigen::Vector3d t;
        R << -0.3536, 0.3536, -0.8660,
            0.9330, 0.0670, -0.3536,
            -0.0670, -0.9330, -0.3536;

        t << 20, -30, 100;

        Eigen::MatrixXd obj_pts(3, n), img_pts(3, n), i_pts(2,n);

        obj_pts << 20, 0,  10, 15, 14, 16,
                    0, 0, -10, -5, -2, -5,
                   40, 0,   0, 24, 21, 13;

        for (int i = 0; i < n; i++)
        {
            img_pts.col(i) = (R * obj_pts.col(i) + t);
            img_pts.col(i) /= img_pts(2,i);
        }

        Eigen::Matrix3d R_est;
        Eigen::Vector3d t_est;

        Eigen::MatrixXd I3 = Eigen::MatrixXd::Identity(3, 3);

        pnp::CPnP p;

        i_pts= img_pts.block(0,0,2,n);

        Eigen::MatrixXd pose_est(6,1);
        p.CPnP_dls(obj_pts, i_pts, n, I3, pose_est);

        t_est<<pose_est(0), pose_est(1), pose_est(2);

        double err_t = (t-t_est).norm();

        EXPECT_LE(err_t, 0.5);

    }

#endif<|endoftext|>


<commit_before>/*
    This file is part of cpp-ethereum.

    cpp-ethereum is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    cpp-ethereum is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Lefteris <lefteris@ethdev.com>
 * @date 2015
 * Converts the AST into json format
 */

#include <libsolidity/ast/ASTJsonConverter.h>
#include <boost/algorithm/string/join.hpp>
#include <libsolidity/ast/AST.h>

using namespace std;

namespace dev
{
namespace solidity
{

void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val)
{
	// special handling for booleans
	if (_key == "const" || _key == "public" || _key == "local" ||
		_key == "lvalue" || _key == "local_lvalue" || _key == "prefix")
		_obj[_key] = (_val == "1") ? true : false;
	else
		// else simply add it as a string
		_obj[_key] = _val;
}

void ASTJsonConverter::addJsonNode(
	ASTNode const& _node,
	string const& _nodeName,
	initializer_list<pair<string const, string const>> _list,
	bool _hasChildren = false
)
{
	Json::Value node;

	node["id"] = reinterpret_cast<Json::UInt64>(&_node);
	node["src"] = sourceLocationToString(_node.location());
	node["name"] = _nodeName;
	if (_list.size() != 0)
	{
		Json::Value attrs;
		for (auto& e: _list)
			addKeyValue(attrs, e.first, e.second);
		node["attributes"] = attrs;
	}

	m_jsonNodePtrs.top()->append(node);

	if (_hasChildren)
	{
		Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
		Json::Value children(Json::arrayValue);
		addedNode["children"] = children;
		m_jsonNodePtrs.push(&addedNode["children"]);
	}
}

string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const
{
	int sourceIndex{-1};
	if (_location.sourceName && m_sourceIndices.count(*_location.sourceName))
		sourceIndex = m_sourceIndices.at(*_location.sourceName);
	int length = -1;
	if (_location.start >= 0 && _location.end >= 0)
		length = _location.end - _location.start;
	return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex);
}

ASTJsonConverter::ASTJsonConverter(
	ASTNode const& _ast,
	map<string, unsigned> _sourceIndices
): m_ast(&_ast), m_sourceIndices(_sourceIndices)
{
	Json::Value children(Json::arrayValue);

	m_astJson["name"] = "root";
	m_astJson["children"] = children;
	m_jsonNodePtrs.push(&m_astJson["children"]);
}

void ASTJsonConverter::print(ostream& _stream)
{
	process();
	_stream << m_astJson;
}

Json::Value const& ASTJsonConverter::json()
{
	process();
	return m_astJson;
}

bool ASTJsonConverter::visit(ImportDirective const& _node)
{
	addJsonNode(_node, "Import", { make_pair("file", _node.path())});
	return true;
}

bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
	addJsonNode(_node, "Contract", { make_pair("name", _node.name()) }, true);
	return true;
}

bool ASTJsonConverter::visit(StructDefinition const& _node)
{
	addJsonNode(_node, "Struct", { make_pair("name", _node.name()) }, true);
	return true;
}

bool ASTJsonConverter::visit(ParameterList const& _node)
{
	addJsonNode(_node, "ParameterList", {}, true);
	return true;
}

bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
	addJsonNode(_node, "Function",
				{ make_pair("name", _node.name()),
					make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())),
					make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) },
				true);
	return true;
}

bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
	addJsonNode(_node, "VariableDeclaration", {
		make_pair("name", _node.name()),
		make_pair("name", _node.name()),
	}, true);
	return true;
}

bool ASTJsonConverter::visit(TypeName const&)
{
	return true;
}

bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
	addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) });
	return true;
}

bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
{
	addJsonNode(_node, "UserDefinedTypeName", {
		make_pair("name", boost::algorithm::join(_node.namePath(), "."))
	});
	return true;
}

bool ASTJsonConverter::visit(Mapping const& _node)
{
	addJsonNode(_node, "Mapping", {}, true);
	return true;
}

bool ASTJsonConverter::visit(InlineAssembly const& _node)
{
	addJsonNode(_node, "InlineAssembly", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Block const& _node)
{
	addJsonNode(_node, "Block", {}, true);
	return true;
}

bool ASTJsonConverter::visit(IfStatement const& _node)
{
	addJsonNode(_node, "IfStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(WhileStatement const& _node)
{
	addJsonNode(_node, "WhileStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(ForStatement const& _node)
{
	addJsonNode(_node, "ForStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Continue const& _node)
{
	addJsonNode(_node, "Continue", {});
	return true;
}

bool ASTJsonConverter::visit(Break const& _node)
{
	addJsonNode(_node, "Break", {});
	return true;
}

bool ASTJsonConverter::visit(Return const& _node)
{
	addJsonNode(_node, "Return", {}, true);;
	return true;
}

bool ASTJsonConverter::visit(Throw const& _node)
{
	addJsonNode(_node, "Throw", {}, true);;
	return true;
}

bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
	addJsonNode(_node, "VariableDefinition", {}, true);
	return true;
}

bool ASTJsonConverter::visit(ExpressionStatement const& _node)
{
	addJsonNode(_node, "ExpressionStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Conditional const& _node)
{
	addJsonNode(_node, "Conditional", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Assignment const& _node)
{
	addJsonNode(_node, "Assignment",
				{ make_pair("operator", Token::toString(_node.assignmentOperator())),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(TupleExpression const& _node)
{
	addJsonNode(_node, "TupleExpression",{}, true);
	return true;
}

bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
	addJsonNode(_node, "UnaryOperation",
				{ make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())),
					make_pair("operator", Token::toString(_node.getOperator())),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
	addJsonNode(_node, "BinaryOperation", {
		make_pair("operator", Token::toString(_node.getOperator())),
		make_pair("type", type(_node))
	}, true);
	return true;
}

bool ASTJsonConverter::visit(FunctionCall const& _node)
{
	addJsonNode(_node, "FunctionCall", {
		make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)),
		make_pair("type", type(_node))
	}, true);
	return true;
}

bool ASTJsonConverter::visit(NewExpression const& _node)
{
	addJsonNode(_node, "NewExpression", { make_pair("type", type(_node)) }, true);
	return true;
}

bool ASTJsonConverter::visit(MemberAccess const& _node)
{
	addJsonNode(_node, "MemberAccess",
				{ make_pair("member_name", _node.memberName()),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(IndexAccess const& _node)
{
	addJsonNode(_node, "IndexAccess", { make_pair("type", type(_node)) }, true);
	return true;
}

bool ASTJsonConverter::visit(Identifier const& _node)
{
	addJsonNode(_node, "Identifier",
				{ make_pair("value", _node.name()), make_pair("type", type(_node)) });
	return true;
}

bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
{
	addJsonNode(_node, "ElementaryTypenameExpression",
				{ make_pair("value", _node.typeName().toString()), make_pair("type", type(_node)) });
	return true;
}

bool ASTJsonConverter::visit(Literal const& _node)
{
	char const* tokenString = Token::toString(_node.token());
	addJsonNode(_node, "Literal",
				{ make_pair("string", (tokenString) ? tokenString : "null"),
					make_pair("value", _node.value()),
					make_pair("type", type(_node)) });
	return true;
}

void ASTJsonConverter::endVisit(ImportDirective const&)
{
}

void ASTJsonConverter::endVisit(ContractDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(StructDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ParameterList const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(FunctionDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(VariableDeclaration const&)
{
}

void ASTJsonConverter::endVisit(TypeName const&)
{
}

void ASTJsonConverter::endVisit(ElementaryTypeName const&)
{
}

void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
{
}

void ASTJsonConverter::endVisit(Mapping const&)
{
}

void ASTJsonConverter::endVisit(InlineAssembly const&)
{
}

void ASTJsonConverter::endVisit(Block const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(IfStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(WhileStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ForStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Continue const&)
{
}

void ASTJsonConverter::endVisit(Break const&)
{
}

void ASTJsonConverter::endVisit(Return const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Throw const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(VariableDeclarationStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ExpressionStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Conditional const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Assignment const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(TupleExpression const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(UnaryOperation const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(BinaryOperation const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(FunctionCall const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(NewExpression const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(MemberAccess const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(IndexAccess const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Identifier const&)
{
}

void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
{
}

void ASTJsonConverter::endVisit(Literal const&)
{
}

void ASTJsonConverter::process()
{
	if (!processed)
		m_ast->accept(*this);
	processed = true;
}

string ASTJsonConverter::type(Expression const& _expression)
{
	return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
}

string ASTJsonConverter::type(VariableDeclaration const& _varDecl)
{
	return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown";
}

}
}
<commit_msg>Fix JSON AST structure<commit_after>/*
    This file is part of cpp-ethereum.

    cpp-ethereum is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    cpp-ethereum is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with cpp-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @author Lefteris <lefteris@ethdev.com>
 * @date 2015
 * Converts the AST into json format
 */

#include <libsolidity/ast/ASTJsonConverter.h>
#include <boost/algorithm/string/join.hpp>
#include <libsolidity/ast/AST.h>

using namespace std;

namespace dev
{
namespace solidity
{

void ASTJsonConverter::addKeyValue(Json::Value& _obj, string const& _key, string const& _val)
{
	// special handling for booleans
	if (_key == "const" || _key == "public" || _key == "local" ||
		_key == "lvalue" || _key == "local_lvalue" || _key == "prefix")
		_obj[_key] = (_val == "1") ? true : false;
	else
		// else simply add it as a string
		_obj[_key] = _val;
}

void ASTJsonConverter::addJsonNode(
	ASTNode const& _node,
	string const& _nodeName,
	initializer_list<pair<string const, string const>> _list,
	bool _hasChildren = false
)
{
	Json::Value node;

	node["id"] = reinterpret_cast<Json::UInt64>(&_node);
	node["src"] = sourceLocationToString(_node.location());
	node["name"] = _nodeName;
	if (_list.size() != 0)
	{
		Json::Value attrs;
		for (auto& e: _list)
			addKeyValue(attrs, e.first, e.second);
		node["attributes"] = attrs;
	}

	m_jsonNodePtrs.top()->append(node);

	if (_hasChildren)
	{
		Json::Value& addedNode = (*m_jsonNodePtrs.top())[m_jsonNodePtrs.top()->size() - 1];
		Json::Value children(Json::arrayValue);
		addedNode["children"] = children;
		m_jsonNodePtrs.push(&addedNode["children"]);
	}
}

string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) const
{
	int sourceIndex{-1};
	if (_location.sourceName && m_sourceIndices.count(*_location.sourceName))
		sourceIndex = m_sourceIndices.at(*_location.sourceName);
	int length = -1;
	if (_location.start >= 0 && _location.end >= 0)
		length = _location.end - _location.start;
	return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex);
}

ASTJsonConverter::ASTJsonConverter(
	ASTNode const& _ast,
	map<string, unsigned> _sourceIndices
): m_ast(&_ast), m_sourceIndices(_sourceIndices)
{
	Json::Value children(Json::arrayValue);

	m_astJson["name"] = "root";
	m_astJson["children"] = children;
	m_jsonNodePtrs.push(&m_astJson["children"]);
}

void ASTJsonConverter::print(ostream& _stream)
{
	process();
	_stream << m_astJson;
}

Json::Value const& ASTJsonConverter::json()
{
	process();
	return m_astJson;
}

bool ASTJsonConverter::visit(ImportDirective const& _node)
{
	addJsonNode(_node, "Import", { make_pair("file", _node.path())});
	return true;
}

bool ASTJsonConverter::visit(ContractDefinition const& _node)
{
	addJsonNode(_node, "Contract", { make_pair("name", _node.name()) }, true);
	return true;
}

bool ASTJsonConverter::visit(StructDefinition const& _node)
{
	addJsonNode(_node, "Struct", { make_pair("name", _node.name()) }, true);
	return true;
}

bool ASTJsonConverter::visit(ParameterList const& _node)
{
	addJsonNode(_node, "ParameterList", {}, true);
	return true;
}

bool ASTJsonConverter::visit(FunctionDefinition const& _node)
{
	addJsonNode(_node, "Function",
				{ make_pair("name", _node.name()),
					make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())),
					make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) },
				true);
	return true;
}

bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
	addJsonNode(_node, "VariableDeclaration", {
		make_pair("name", _node.name()),
		make_pair("name", _node.name()),
	}, true);
	return true;
}

bool ASTJsonConverter::visit(TypeName const&)
{
	return true;
}

bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
	addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) });
	return true;
}

bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
{
	addJsonNode(_node, "UserDefinedTypeName", {
		make_pair("name", boost::algorithm::join(_node.namePath(), "."))
	});
	return true;
}

bool ASTJsonConverter::visit(Mapping const& _node)
{
	addJsonNode(_node, "Mapping", {}, true);
	return true;
}

bool ASTJsonConverter::visit(InlineAssembly const& _node)
{
	addJsonNode(_node, "InlineAssembly", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Block const& _node)
{
	addJsonNode(_node, "Block", {}, true);
	return true;
}

bool ASTJsonConverter::visit(IfStatement const& _node)
{
	addJsonNode(_node, "IfStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(WhileStatement const& _node)
{
	addJsonNode(_node, "WhileStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(ForStatement const& _node)
{
	addJsonNode(_node, "ForStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Continue const& _node)
{
	addJsonNode(_node, "Continue", {});
	return true;
}

bool ASTJsonConverter::visit(Break const& _node)
{
	addJsonNode(_node, "Break", {});
	return true;
}

bool ASTJsonConverter::visit(Return const& _node)
{
	addJsonNode(_node, "Return", {}, true);;
	return true;
}

bool ASTJsonConverter::visit(Throw const& _node)
{
	addJsonNode(_node, "Throw", {}, true);;
	return true;
}

bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
	addJsonNode(_node, "VariableDefinition", {}, true);
	return true;
}

bool ASTJsonConverter::visit(ExpressionStatement const& _node)
{
	addJsonNode(_node, "ExpressionStatement", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Conditional const& _node)
{
	addJsonNode(_node, "Conditional", {}, true);
	return true;
}

bool ASTJsonConverter::visit(Assignment const& _node)
{
	addJsonNode(_node, "Assignment",
				{ make_pair("operator", Token::toString(_node.assignmentOperator())),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(TupleExpression const& _node)
{
	addJsonNode(_node, "TupleExpression",{}, true);
	return true;
}

bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
	addJsonNode(_node, "UnaryOperation",
				{ make_pair("prefix", boost::lexical_cast<std::string>(_node.isPrefixOperation())),
					make_pair("operator", Token::toString(_node.getOperator())),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
	addJsonNode(_node, "BinaryOperation", {
		make_pair("operator", Token::toString(_node.getOperator())),
		make_pair("type", type(_node))
	}, true);
	return true;
}

bool ASTJsonConverter::visit(FunctionCall const& _node)
{
	addJsonNode(_node, "FunctionCall", {
		make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)),
		make_pair("type", type(_node))
	}, true);
	return true;
}

bool ASTJsonConverter::visit(NewExpression const& _node)
{
	addJsonNode(_node, "NewExpression", { make_pair("type", type(_node)) }, true);
	return true;
}

bool ASTJsonConverter::visit(MemberAccess const& _node)
{
	addJsonNode(_node, "MemberAccess",
				{ make_pair("member_name", _node.memberName()),
					make_pair("type", type(_node)) },
				true);
	return true;
}

bool ASTJsonConverter::visit(IndexAccess const& _node)
{
	addJsonNode(_node, "IndexAccess", { make_pair("type", type(_node)) }, true);
	return true;
}

bool ASTJsonConverter::visit(Identifier const& _node)
{
	addJsonNode(_node, "Identifier",
				{ make_pair("value", _node.name()), make_pair("type", type(_node)) });
	return true;
}

bool ASTJsonConverter::visit(ElementaryTypeNameExpression const& _node)
{
	addJsonNode(_node, "ElementaryTypenameExpression",
				{ make_pair("value", _node.typeName().toString()), make_pair("type", type(_node)) });
	return true;
}

bool ASTJsonConverter::visit(Literal const& _node)
{
	char const* tokenString = Token::toString(_node.token());
	addJsonNode(_node, "Literal",
				{ make_pair("string", (tokenString) ? tokenString : "null"),
					make_pair("value", _node.value()),
					make_pair("type", type(_node)) });
	return true;
}

void ASTJsonConverter::endVisit(ImportDirective const&)
{
}

void ASTJsonConverter::endVisit(ContractDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(StructDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ParameterList const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(FunctionDefinition const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(VariableDeclaration const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(TypeName const&)
{
}

void ASTJsonConverter::endVisit(ElementaryTypeName const&)
{
}

void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
{
}

void ASTJsonConverter::endVisit(Mapping const&)
{
}

void ASTJsonConverter::endVisit(InlineAssembly const&)
{
}

void ASTJsonConverter::endVisit(Block const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(IfStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(WhileStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ForStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Continue const&)
{
}

void ASTJsonConverter::endVisit(Break const&)
{
}

void ASTJsonConverter::endVisit(Return const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Throw const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(VariableDeclarationStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(ExpressionStatement const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Conditional const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Assignment const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(TupleExpression const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(UnaryOperation const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(BinaryOperation const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(FunctionCall const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(NewExpression const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(MemberAccess const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(IndexAccess const&)
{
	goUp();
}

void ASTJsonConverter::endVisit(Identifier const&)
{
}

void ASTJsonConverter::endVisit(ElementaryTypeNameExpression const&)
{
}

void ASTJsonConverter::endVisit(Literal const&)
{
}

void ASTJsonConverter::process()
{
	if (!processed)
		m_ast->accept(*this);
	processed = true;
}

string ASTJsonConverter::type(Expression const& _expression)
{
	return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
}

string ASTJsonConverter::type(VariableDeclaration const& _varDecl)
{
	return _varDecl.annotation().type ? _varDecl.annotation().type->toString() : "Unknown";
}

}
}
<|endoftext|>


<commit_before>// tImageTIFF.cpp
//
// This knows how to load/save TIFFs. It knows the details of the tiff file format and loads the data into multiple
// tPixel arrays, one for each frame (in a TIFF thay are called pages). These arrays may be 'stolen' by tPictures.
//
// Copyright (c) 2020, 2021 Tristan Grimmer.
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <Foundation/tVersion.cmake.h>
#include <Foundation/tStandard.h>
#include <Foundation/tString.h>
#include <System/tFile.h>
#include <System/tScript.h>
#include "Image/tImageTIFF.h"
#include "LibTIFF/include/tiff.h"
#include "LibTIFF/include/tiffio.h"
#include "LibTIFF/include/tiffvers.h"
using namespace tSystem;
namespace tImage
{

int tImageTIFF::ReadSoftwarePageDuration(TIFF* tiff) const
{
	void* data = nullptr;
	int durationMilliSec = -1;
	int success = TIFFGetField(tiff, TIFFTAG_SOFTWARE, &data);
	if (!success || !data)
		return durationMilliSec;

	tString softwareStr((char*)data);
	tScriptReader script(softwareStr, false);
	tExpression tacentView = script.First();
	if (tacentView.GetAtomString() == "TacentLibrary")
	{
		tExpression tacentVers = tacentView.Next();
		tExpression durationEx = tacentVers.Next();
		tExpression durCmd = durationEx.Item0();
		if (durCmd.GetAtomString() == "PageDur")
		{
			tExpression durVal = durationEx.Item1();
			durationMilliSec = durVal.GetAtomInt();
		}
	}

	return durationMilliSec;
}

bool tImageTIFF::WriteSoftwarePageDuration(TIFF* tiff, int milliseconds)
{
	tString softwareDesc;
	tsPrintf(softwareDesc, "TacentLibrary V%d.%d.%d [PageDur d]", tVersion::Major, tVersion::Minor, tVersion::Revision, milliseconds);
	int success = TIFFSetField(tiff, TIFFTAG_SOFTWARE, softwareDesc.Chars());
	return success ? true : false;
}

bool tImageTIFF::Load(const tString& tiffFile)
{
	Clear();

	if (tSystem::tGetFileType(tiffFile) != tSystem::tFileType::TIFF)
		return false;

	if (!tFileExists(tiffFile))
		return false;

	TIFF* tiff = TIFFOpen(tiffFile.Chars(), "rb");
	if (!tiff)
		return false;

	// Create all frames.
	do
	{
		int width = 0; int height = 0;
		TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
		TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
		if ((width <= 0) || (height <= 0))
			break;

		int numPixels = width*height;
		int durationMilliSeconds = ReadSoftwarePageDuration(tiff);

		uint32* pixels = (uint32*)_TIFFmalloc(numPixels * sizeof(uint32));
		int successCode = TIFFReadRGBAImage(tiff, width, height, pixels, 0);
		if (!successCode)
		{
			_TIFFfree(pixels);
			break;
		}

		tFrame* frame = new tFrame;
		frame->Width = width;
		frame->Height = height;
		frame->Pixels = new tPixel[width*height];
		frame->SrcPixelFormat = tPixelFormat::R8G8B8A8;

		// If duration not set we use a default of 1 second.
		frame->Duration = (durationMilliSeconds >= 0) ? float(durationMilliSeconds)/1000.0f : 1.0f;

		for (int p = 0; p < width*height; p++)
			frame->Pixels[p] = pixels[p];

		_TIFFfree(pixels);
		Frames.Append(frame);
	} while (TIFFReadDirectory(tiff));

	TIFFClose(tiff);
	if (Frames.GetNumItems() == 0)
		return false;

	SrcPixelFormat = tPixelFormat::R8G8B8A8;
	return true;
}

bool tImageTIFF::Set(tList<tFrame>& srcFrames, bool stealFrames)
{
	Clear();
	if (srcFrames.GetNumItems() <= 0)
		return false;

	tPixelFormat SrcPixelFormat = tPixelFormat::R8G8B8A8;
	if (stealFrames)
	{
		while (tFrame* frame = srcFrames.Remove())
			Frames.Append(frame);
		return true;
	}

	for (tFrame* frame = srcFrames.Head(); frame; frame = frame->Next())
		Frames.Append(new tFrame(*frame));

	return true;
}

bool tImageTIFF::IsOpaque() const
{
	for (tFrame* frame = Frames.Head(); frame; frame = frame->Next())
		if (!frame->IsOpaque())
			return false;

	return true;
}

bool tImageTIFF::Save(const tString& tiffFilename, bool useZLibComp, int overrideFrameDuration)
{
	if (!IsValid())
		return false;

	TIFF* tiffFile = TIFFOpen(tiffFilename.Chars(), "wb");
	if (!tiffFile)
		return false;

	int rowSize = 0;
	uint8* rowBuf = nullptr;
	for (tFrame* frame = Frames.First(); frame; frame = frame->Next())
	{
		// Writes image from last loop and starts a new directory.
		if (frame != Frames.First())
			TIFFWriteDirectory(tiffFile);

		bool isOpaque = frame->IsOpaque();
		int bytesPerPixel = isOpaque ? 3 : 4;
		int w = frame->Width;
		int h = frame->Height;

		TIFFSetField(tiffFile, TIFFTAG_IMAGEWIDTH, w);
		TIFFSetField(tiffFile, TIFFTAG_IMAGELENGTH, h);
		TIFFSetField(tiffFile, TIFFTAG_SAMPLESPERPIXEL, bytesPerPixel);
		TIFFSetField(tiffFile, TIFFTAG_BITSPERSAMPLE, 8);
		TIFFSetField(tiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
		TIFFSetField(tiffFile, TIFFTAG_COMPRESSION, useZLibComp ? COMPRESSION_DEFLATE : COMPRESSION_NONE);
		TIFFSetField(tiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
		TIFFSetField(tiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);

		int pageDurMilliSec = (overrideFrameDuration >= 0) ? overrideFrameDuration : int(frame->Duration*1000.0f);
		WriteSoftwarePageDuration(tiffFile, pageDurMilliSec);

		int rowSizeNeeded = TIFFScanlineSize(tiffFile);
		tAssert(rowSizeNeeded == (w*bytesPerPixel));

		// Let's not reallocate the line buffer every frame. Often all frames will be the same size.
		if (rowSize != rowSizeNeeded)
		{
			if (rowBuf)
				_TIFFfree(rowBuf);
			rowBuf = (uint8*)_TIFFmalloc(rowSizeNeeded);
			rowSize = rowSizeNeeded;
		}

		TIFFSetField(tiffFile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffFile, w*bytesPerPixel));
		for (int r = 0; r < h; r++)
		{
			for (int x = 0; x < w; x++)
			{
				int idx = (h-r-1)*w + x;
				rowBuf[x*bytesPerPixel + 0] = frame->Pixels[idx].R;
				rowBuf[x*bytesPerPixel + 1] = frame->Pixels[idx].G;
				rowBuf[x*bytesPerPixel + 2] = frame->Pixels[idx].B;
				if (bytesPerPixel == 4)
					rowBuf[x*bytesPerPixel + 3] = frame->Pixels[idx].A;
			}

			int errCode = TIFFWriteScanline(tiffFile, rowBuf, r, 0);
			if (errCode < 0)
				continue;
		}
    }

	if (rowBuf)
		_TIFFfree(rowBuf);
	TIFFClose(tiffFile);

	return true;
}

}
<commit_msg>fix typo.<commit_after>// tImageTIFF.cpp
//
// This knows how to load/save TIFFs. It knows the details of the tiff file format and loads the data into multiple
// tPixel arrays, one for each frame (in a TIFF thay are called pages). These arrays may be 'stolen' by tPictures.
//
// Copyright (c) 2020, 2021 Tristan Grimmer.
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <Foundation/tVersion.cmake.h>
#include <Foundation/tStandard.h>
#include <Foundation/tString.h>
#include <System/tFile.h>
#include <System/tScript.h>
#include "Image/tImageTIFF.h"
#include "LibTIFF/include/tiff.h"
#include "LibTIFF/include/tiffio.h"
#include "LibTIFF/include/tiffvers.h"
using namespace tSystem;
namespace tImage
{

int tImageTIFF::ReadSoftwarePageDuration(TIFF* tiff) const
{
	void* data = nullptr;
	int durationMilliSec = -1;
	int success = TIFFGetField(tiff, TIFFTAG_SOFTWARE, &data);
	if (!success || !data)
		return durationMilliSec;

	tString softwareStr((char*)data);
	tScriptReader script(softwareStr, false);
	tExpression tacentView = script.First();
	if (tacentView.GetAtomString() == "TacentLibrary")
	{
		tExpression tacentVers = tacentView.Next();
		tExpression durationEx = tacentVers.Next();
		tExpression durCmd = durationEx.Item0();
		if (durCmd.GetAtomString() == "PageDur")
		{
			tExpression durVal = durationEx.Item1();
			durationMilliSec = durVal.GetAtomInt();
		}
	}

	return durationMilliSec;
}

bool tImageTIFF::WriteSoftwarePageDuration(TIFF* tiff, int milliseconds)
{
	tString softwareDesc;
	tsPrintf(softwareDesc, "TacentLibrary V%d.%d.%d [PageDur %d]", tVersion::Major, tVersion::Minor, tVersion::Revision, milliseconds);
	int success = TIFFSetField(tiff, TIFFTAG_SOFTWARE, softwareDesc.Chars());
	return success ? true : false;
}

bool tImageTIFF::Load(const tString& tiffFile)
{
	Clear();

	if (tSystem::tGetFileType(tiffFile) != tSystem::tFileType::TIFF)
		return false;

	if (!tFileExists(tiffFile))
		return false;

	TIFF* tiff = TIFFOpen(tiffFile.Chars(), "rb");
	if (!tiff)
		return false;

	// Create all frames.
	do
	{
		int width = 0; int height = 0;
		TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
		TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
		if ((width <= 0) || (height <= 0))
			break;

		int numPixels = width*height;
		int durationMilliSeconds = ReadSoftwarePageDuration(tiff);

		uint32* pixels = (uint32*)_TIFFmalloc(numPixels * sizeof(uint32));
		int successCode = TIFFReadRGBAImage(tiff, width, height, pixels, 0);
		if (!successCode)
		{
			_TIFFfree(pixels);
			break;
		}

		tFrame* frame = new tFrame;
		frame->Width = width;
		frame->Height = height;
		frame->Pixels = new tPixel[width*height];
		frame->SrcPixelFormat = tPixelFormat::R8G8B8A8;

		// If duration not set we use a default of 1 second.
		frame->Duration = (durationMilliSeconds >= 0) ? float(durationMilliSeconds)/1000.0f : 1.0f;

		for (int p = 0; p < width*height; p++)
			frame->Pixels[p] = pixels[p];

		_TIFFfree(pixels);
		Frames.Append(frame);
	} while (TIFFReadDirectory(tiff));

	TIFFClose(tiff);
	if (Frames.GetNumItems() == 0)
		return false;

	SrcPixelFormat = tPixelFormat::R8G8B8A8;
	return true;
}

bool tImageTIFF::Set(tList<tFrame>& srcFrames, bool stealFrames)
{
	Clear();
	if (srcFrames.GetNumItems() <= 0)
		return false;

	tPixelFormat SrcPixelFormat = tPixelFormat::R8G8B8A8;
	if (stealFrames)
	{
		while (tFrame* frame = srcFrames.Remove())
			Frames.Append(frame);
		return true;
	}

	for (tFrame* frame = srcFrames.Head(); frame; frame = frame->Next())
		Frames.Append(new tFrame(*frame));

	return true;
}

bool tImageTIFF::IsOpaque() const
{
	for (tFrame* frame = Frames.Head(); frame; frame = frame->Next())
		if (!frame->IsOpaque())
			return false;

	return true;
}

bool tImageTIFF::Save(const tString& tiffFilename, bool useZLibComp, int overrideFrameDuration)
{
	if (!IsValid())
		return false;

	TIFF* tiffFile = TIFFOpen(tiffFilename.Chars(), "wb");
	if (!tiffFile)
		return false;

	int rowSize = 0;
	uint8* rowBuf = nullptr;
	for (tFrame* frame = Frames.First(); frame; frame = frame->Next())
	{
		// Writes image from last loop and starts a new directory.
		if (frame != Frames.First())
			TIFFWriteDirectory(tiffFile);

		bool isOpaque = frame->IsOpaque();
		int bytesPerPixel = isOpaque ? 3 : 4;
		int w = frame->Width;
		int h = frame->Height;

		TIFFSetField(tiffFile, TIFFTAG_IMAGEWIDTH, w);
		TIFFSetField(tiffFile, TIFFTAG_IMAGELENGTH, h);
		TIFFSetField(tiffFile, TIFFTAG_SAMPLESPERPIXEL, bytesPerPixel);
		TIFFSetField(tiffFile, TIFFTAG_BITSPERSAMPLE, 8);
		TIFFSetField(tiffFile, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
		TIFFSetField(tiffFile, TIFFTAG_COMPRESSION, useZLibComp ? COMPRESSION_DEFLATE : COMPRESSION_NONE);
		TIFFSetField(tiffFile, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
		TIFFSetField(tiffFile, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);

		int pageDurMilliSec = (overrideFrameDuration >= 0) ? overrideFrameDuration : int(frame->Duration*1000.0f);
		WriteSoftwarePageDuration(tiffFile, pageDurMilliSec);

		int rowSizeNeeded = TIFFScanlineSize(tiffFile);
		tAssert(rowSizeNeeded == (w*bytesPerPixel));

		// Let's not reallocate the line buffer every frame. Often all frames will be the same size.
		if (rowSize != rowSizeNeeded)
		{
			if (rowBuf)
				_TIFFfree(rowBuf);
			rowBuf = (uint8*)_TIFFmalloc(rowSizeNeeded);
			rowSize = rowSizeNeeded;
		}

		TIFFSetField(tiffFile, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffFile, w*bytesPerPixel));
		for (int r = 0; r < h; r++)
		{
			for (int x = 0; x < w; x++)
			{
				int idx = (h-r-1)*w + x;
				rowBuf[x*bytesPerPixel + 0] = frame->Pixels[idx].R;
				rowBuf[x*bytesPerPixel + 1] = frame->Pixels[idx].G;
				rowBuf[x*bytesPerPixel + 2] = frame->Pixels[idx].B;
				if (bytesPerPixel == 4)
					rowBuf[x*bytesPerPixel + 3] = frame->Pixels[idx].A;
			}

			int errCode = TIFFWriteScanline(tiffFile, rowBuf, r, 0);
			if (errCode < 0)
				continue;
		}
    }

	if (rowBuf)
		_TIFFfree(rowBuf);
	TIFFClose(tiffFile);

	return true;
}

}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  oryol-shdc
//------------------------------------------------------------------------------
#include "ExportUtil/CmdLineArgs.h"
#include "ExportUtil/Log.h"
#include "spirv_hlsl.hpp"
#include "spirv_msl.hpp"
#include "pystring.h"
#include <stdio.h>

using namespace OryolTools;
using namespace spv;
using namespace spirv_cross;
using namespace std;

//------------------------------------------------------------------------------
vector<uint32_t> read_spirv_file(const string& path) {
    FILE* fp = fopen(path.c_str(), "rb");
    if (!fp) {
        Log::Fatal("Failed to open SPIRV file '%s'\n", path.c_str());
    }
    fseek(fp, 0, SEEK_END);
    long len = ftell(fp) / sizeof(uint32_t);
    fseek(fp, 0, SEEK_SET);
    vector<uint32_t> spirv(len);
    if (fread(spirv.data(), sizeof(uint32_t), len, fp) != size_t(len)) {
        Log::Fatal("Error reading SPIRV file '%s'\n", path.c_str());
    }
    fclose(fp);
    return spirv;
}

//------------------------------------------------------------------------------
void write_source_file(const string& path, const string& content) {
    FILE* fp = fopen(path.c_str(), "w");
    if (!fp) {
        Log::Fatal("Failed to open '%s' for writing\n", path.c_str());
    }
    fwrite(content.c_str(), 1, content.length(), fp);
    fclose(fp);
}

//------------------------------------------------------------------------------
void to_glsl_100(const vector<uint32_t>& spirv, const string& base_path) {
    auto compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(spirv));
    auto opts = compiler->get_options();
    opts.version = 100;
    opts.es = true;
    compiler->set_options(opts);
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v100 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl100.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_120(const vector<uint32_t>& spirv, const string& base_path) {
    auto compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(spirv));
    auto opts = compiler->get_options();
    opts.version = 120;
    opts.es = false;
    compiler->set_options(opts);
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v120 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl120.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_es3(const vector<uint32_t>& spirv, const string& base_path) {
    auto compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(spirv));
    auto opts = compiler->get_options();
    opts.version = 300;
    opts.es = true;
    compiler->set_options(opts);
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL es3 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsles3.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_330(const vector<uint32_t>& spirv, const string& base_path) {
    string dst_path = base_path + ".glsl330.glsl";
    auto compiler = unique_ptr<CompilerGLSL>(new CompilerGLSL(spirv));
    auto opts = compiler->get_options();
    opts.version = 330;
    opts.es = false;
    compiler->set_options(opts);
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v330 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl330.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_hlsl_sm5(const vector<uint32_t>& spirv, const string& base_path) {
    string dst_path = base_path + ".hlsl";
    auto compiler = unique_ptr<CompilerHLSL>(new CompilerHLSL(spirv));
    auto opts = compiler->get_options();
    opts.shader_model = 50;
    compiler->set_options(opts);
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile HLSL5 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".hlsl", src);
    }
}

//------------------------------------------------------------------------------
void to_mlsl(const vector<uint32_t>& spirv, const string& base_path) {
    string dst_path = base_path + ".metal";
    auto compiler = unique_ptr<CompilerMSL>(new CompilerMSL(spirv));
    string src = compiler->compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile MetalSL source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".metal", src);
    }
}

//------------------------------------------------------------------------------
int main(int argc, const char** argv) {
    CmdLineArgs args;
    args.AddBool("-help", "show help");
    args.AddString("-spirv", "SPIR-V input file", "");
    if (!args.Parse(argc, argv)) {
        Log::Warn("Failed to parse args!\n");
        return 10;
    }

    if (args.HasArg("-help")) {
        Log::Info("Oryol SPIR-V to GLSL/HLSL/MSL cross-compiler\n"
                  "Based on SPIRV-Cross: https://github.com/KhronosGroup/SPIRV-Cross\n");
        args.ShowHelp();
        return 0;
    }
    string spirv_path = args.GetString("-spirv");
    if (spirv_path.empty()) {
        Log::Fatal("-spirv arg expected");
    }

    // load SPIRV byte code
    auto spirv = read_spirv_file(spirv_path);

    // ...translate and write to output files...
    string base_path, ext;
    pystring::os::path::splitext(base_path, ext, spirv_path);
    to_glsl_100(spirv, base_path);
    to_glsl_120(spirv, base_path);
    to_glsl_es3(spirv, base_path);
    to_glsl_330(spirv, base_path);
    to_hlsl_sm5(spirv, base_path);
    to_mlsl(spirv, base_path);

    return 0;
}
<commit_msg>shdc: start looking at the reflection info<commit_after>//------------------------------------------------------------------------------
//  oryol-shdc
//------------------------------------------------------------------------------
#include "ExportUtil/CmdLineArgs.h"
#include "ExportUtil/Log.h"
#include "spirv_hlsl.hpp"
#include "spirv_msl.hpp"
#include "pystring.h"
#include <stdio.h>

using namespace OryolTools;
using namespace spv;
using namespace spirv_cross;
using namespace std;

//------------------------------------------------------------------------------
vector<uint32_t> read_spirv_file(const string& path) {
    FILE* fp = fopen(path.c_str(), "rb");
    if (!fp) {
        Log::Fatal("Failed to open SPIRV file '%s'\n", path.c_str());
    }
    fseek(fp, 0, SEEK_END);
    long len = ftell(fp) / sizeof(uint32_t);
    fseek(fp, 0, SEEK_SET);
    vector<uint32_t> spirv(len);
    if (fread(spirv.data(), sizeof(uint32_t), len, fp) != size_t(len)) {
        Log::Fatal("Error reading SPIRV file '%s'\n", path.c_str());
    }
    fclose(fp);
    return spirv;
}

//------------------------------------------------------------------------------
void write_source_file(const string& path, const string& content) {
    FILE* fp = fopen(path.c_str(), "w");
    if (!fp) {
        Log::Fatal("Failed to open '%s' for writing\n", path.c_str());
    }
    fwrite(content.c_str(), 1, content.length(), fp);
    fclose(fp);
}

//------------------------------------------------------------------------------
void extract_resource_info(Compiler* compiler) {
    switch (compiler->get_execution_model()) {
        case ExecutionModelVertex:      Log::Info("type: vertex shader\n"); break;
        case ExecutionModelFragment:    Log::Info("type: fragment shader\n"); break;
        default: Log::Info("unsupported shader type\n");
    }
    ShaderResources res = compiler->get_shader_resources();
    for (const auto& ub : res.uniform_buffers) {
        Log::Info("uniform_buffer: %s\n", ub.name.c_str());
        const SPIRType& type = compiler->get_type(ub.base_type_id);
        for (int member_index = 0; member_index < type.member_types.size(); member_index++) {
            string member_name = compiler->get_member_name(ub.base_type_id, member_index);
            Log::Info("    %s\n", member_name.c_str());
        }
    }
    for (const auto& img : res.sampled_images) {
        Log::Info("sampled_image: %s\n", img.name.c_str());
    }
    for (const auto& input : res.stage_inputs) {
        Log::Info("input: %s\n", input.name.c_str());
    }
    for (const auto& output : res.stage_outputs) {
        Log::Info("output: %s\n", output.name.c_str());
    }
    for (const auto& storage_image : res.storage_images) {
        Log::Info("storage_image: %s\n", storage_image.name.c_str());
    }
}

//------------------------------------------------------------------------------
void to_glsl_100(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerGLSL compiler(spirv);
    extract_resource_info(&compiler);
    auto opts = compiler.get_options();
    opts.version = 100;
    opts.es = true;
    compiler.set_options(opts);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v100 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl100.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_120(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerGLSL compiler(spirv);
    auto opts = compiler.get_options();
    opts.version = 120;
    opts.es = false;
    compiler.set_options(opts);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v120 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl120.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_es3(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerGLSL compiler(spirv);
    auto opts = compiler.get_options();
    opts.version = 300;
    opts.es = true;
    compiler.set_options(opts);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL es3 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsles3.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_glsl_330(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerGLSL compiler(spirv);
    auto opts = compiler.get_options();
    opts.version = 330;
    opts.es = false;
    compiler.set_options(opts);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile GLSL v330 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".glsl330.glsl", src);
    }
}

//------------------------------------------------------------------------------
void to_hlsl_sm5(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerHLSL compiler(spirv);
    auto opts = compiler.get_options();
    opts.shader_model = 50;
    compiler.set_options(opts);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile HLSL5 source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".hlsl", src);
    }
}

//------------------------------------------------------------------------------
void to_mlsl(const vector<uint32_t>& spirv, const string& base_path) {
    CompilerMSL compiler(spirv);
    string src = compiler.compile();
    if (src.empty()) {
        Log::Fatal("Failed to compile MetalSL source for '%s'!\n", base_path.c_str());
    }
    else {
        write_source_file(base_path + ".metal", src);
    }
}

//------------------------------------------------------------------------------
int main(int argc, const char** argv) {
    CmdLineArgs args;
    args.AddBool("-help", "show help");
    args.AddString("-spirv", "SPIR-V input file", "");
    if (!args.Parse(argc, argv)) {
        Log::Warn("Failed to parse args!\n");
        return 10;
    }

    if (args.HasArg("-help")) {
        Log::Info("Oryol SPIR-V to GLSL/HLSL/MSL cross-compiler\n"
                  "Based on SPIRV-Cross: https://github.com/KhronosGroup/SPIRV-Cross\n");
        args.ShowHelp();
        return 0;
    }
    string spirv_path = args.GetString("-spirv");
    if (spirv_path.empty()) {
        Log::Fatal("-spirv arg expected");
    }

    // load SPIRV byte code
    auto spirv = read_spirv_file(spirv_path);

    // ...translate and write to output files...
    string base_path, ext;
    pystring::os::path::splitext(base_path, ext, spirv_path);
    to_glsl_100(spirv, base_path);
    to_glsl_120(spirv, base_path);
    to_glsl_es3(spirv, base_path);
    to_glsl_330(spirv, base_path);
    to_hlsl_sm5(spirv, base_path);
    to_mlsl(spirv, base_path);

    return 0;
}
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/
#include <stdlib.h>

#include <osg/Referenced>
#include <osg/Notify>
#include <osg/ApplicationUsage>
#include <osg/Observer>

#include <typeinfo>
#include <memory>
#include <set>

#include <OpenThreads/ScopedLock>
#include <OpenThreads/Mutex>

#include <osg/DeleteHandler>

namespace osg
{

//#define ENFORCE_THREADSAFE
//#define DEBUG_OBJECT_ALLOCATION_DESTRUCTION

// specialized smart pointer, used to get round auto_ptr<>'s lack of the destructor reseting itself to 0.
template<typename T>
struct ResetPointer
{
    ResetPointer():
        _ptr(0) {}

    ResetPointer(T* ptr):
        _ptr(ptr) {}

    ~ResetPointer()
    {
        delete _ptr;
        _ptr = 0;
    }

    inline ResetPointer& operator = (T* ptr)
    {
        if (_ptr==ptr) return *this;
        delete _ptr;
        _ptr = ptr;
        return *this;
    }

    void reset(T* ptr)
    {
        if (_ptr==ptr) return;
        delete _ptr;
        _ptr = ptr;
    }

    inline T& operator*()  { return *_ptr; }

    inline const T& operator*() const { return *_ptr; }

    inline T* operator->() { return _ptr; }

    inline const T* operator->() const   { return _ptr; }

    T* get() { return _ptr; }

    const T* get() const { return _ptr; }

    T* _ptr;
};

typedef ResetPointer<DeleteHandler> DeleteHandlerPointer;
typedef ResetPointer<OpenThreads::Mutex> GlobalMutexPointer;

OpenThreads::Mutex* Referenced::getGlobalReferencedMutex()
{
    static GlobalMutexPointer s_ReferencedGlobalMutext = new OpenThreads::Mutex;
    return s_ReferencedGlobalMutext.get();
}

// helper class for forcing the global mutex to be constructed when the library is loaded.
struct InitGlobalMutexes
{
    InitGlobalMutexes()
    {
        Referenced::getGlobalReferencedMutex();
    }
};
static InitGlobalMutexes s_initGlobalMutexes;

#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
static bool s_useThreadSafeReferenceCounting = getenv("OSG_THREAD_SAFE_REF_UNREF")!=0;
#endif
// static std::auto_ptr<DeleteHandler> s_deleteHandler(0);
static DeleteHandlerPointer s_deleteHandler(0);

static ApplicationUsageProxy Referenced_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREAD_SAFE_REF_UNREF","");

void Referenced::setThreadSafeReferenceCounting(bool enableThreadSafeReferenceCounting)
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    s_useThreadSafeReferenceCounting = enableThreadSafeReferenceCounting;
#endif
}

bool Referenced::getThreadSafeReferenceCounting()
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    return true;
#else
    return s_useThreadSafeReferenceCounting;
#endif
}

void Referenced::setDeleteHandler(DeleteHandler* handler)
{
    s_deleteHandler.reset(handler);
}

DeleteHandler* Referenced::getDeleteHandler()
{
    return s_deleteHandler.get();
}

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
OpenThreads::Mutex& getNumObjectMutex()
{
    static OpenThreads::Mutex s_numObjectMutex;
    return s_numObjectMutex;
}
static int s_numObjects = 0;
#endif

Referenced::Referenced():
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (s_useThreadSafeReferenceCounting)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif

}

Referenced::Referenced(bool threadSafeRefUnref):
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (threadSafeRefUnref)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif
}

Referenced::Referenced(const Referenced&):
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (s_useThreadSafeReferenceCounting)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif
}

Referenced::~Referenced()
{
#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        --s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif

    if (_refCount>0)
    {
        OSG_WARN<<"Warning: deleting still referenced object "<<this<<" of type '"<<typeid(this).name()<<"'"<<std::endl;
        OSG_WARN<<"         the final reference count was "<<_refCount<<", memory corruption possible."<<std::endl;
    }

    // signal observers that we are being deleted.
    signalObserversAndDelete(true, false);

    // delete the ObserverSet
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (_observerSet.get()) static_cast<ObserverSet*>(_observerSet.get())->unref();
#else
    if (_observerSet) static_cast<ObserverSet*>(_observerSet)->unref();
#endif

#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (_refMutex) delete _refMutex;
#endif
}

ObserverSet* Referenced::getOrCreateObserverSet() const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet.get());
    while (0 == observerSet)
    {
        ObserverSet* newObserverSet = new ObserverSet(this);
        newObserverSet->ref();

        if (!_observerSet.assign(newObserverSet, 0))
        {
            newObserverSet->unref();
        }

        observerSet = static_cast<ObserverSet*>(_observerSet.get());
    }
    return observerSet;
#else
    if (_refMutex)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_refMutex);
        if (!_observerSet)
        {
            _observerSet = new ObserverSet(this);
            static_cast<ObserverSet*>(_observerSet)->ref();
        }
        return static_cast<ObserverSet*>(_observerSet);
    }
    else
    {
        if (!_observerSet) _observerSet = new ObserverSet(this);
        return static_cast<ObserverSet*>(_observerSet);
    }
#endif
}

void Referenced::addObserver(Observer* observer) const
{
    getOrCreateObserverSet()->addObserver(observer);
}

void Referenced::removeObserver(Observer* observer) const
{
    getOrCreateObserverSet()->removeObserver(observer);
}

void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet.get());
#else
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet);
#endif

    if (observerSet && signalDelete)
    {
        observerSet->signalObjectDeleted(const_cast<Referenced*>(this));
    }

    if (doDelete)
    {
        if (_refCount!=0) OSG_NOTICE<<"Warning Referenced::signalObserversAndDelete(,,) doing delete with _refCount="<<_refCount<<std::endl;

        if (getDeleteHandler()) deleteUsingDeleteHandler();
        else delete this;
    }
}

void Referenced::setThreadSafeRefUnref(bool threadSafe)
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (threadSafe)
    {
        if (!_refMutex)
        {
            // we want thread safe ref()/unref() so assign a mutex
            _refMutex = new OpenThreads::Mutex;
        }
    }
    else
    {
        if (_refMutex)
        {
            // we don't want thread safe ref()/unref() so remove any assigned mutex
            OpenThreads::Mutex* tmpMutexPtr = _refMutex;
            _refMutex = 0;
            delete tmpMutexPtr;
        }
    }
#endif
}

int Referenced::unref_nodelete() const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    return --_refCount;
#else
    if (_refMutex)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_refMutex);
        return --_refCount;
    }
    else
    {
        return --_refCount;
    }
#endif
}

void Referenced::deleteUsingDeleteHandler() const
{
    getDeleteHandler()->requestDelete(this);
}

} // end of namespace osg
<commit_msg>From Stephan Huber, "just a follow-up: I looked at the code of osg/Referenced.cpp and notice a discrepancy in getOrCreateObserverSet. For the atomic- and mutex-codepaths the newly created observerset gets manually ref'ed, but not in the codepath used when OPENTHREADS_ATOMIC_USE_MUTEX is defined. I added the manual ref and tadaaa, the crash went away."<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
*/
#include <stdlib.h>

#include <osg/Referenced>
#include <osg/Notify>
#include <osg/ApplicationUsage>
#include <osg/Observer>

#include <typeinfo>
#include <memory>
#include <set>

#include <OpenThreads/ScopedLock>
#include <OpenThreads/Mutex>

#include <osg/DeleteHandler>

namespace osg
{

//#define ENFORCE_THREADSAFE
//#define DEBUG_OBJECT_ALLOCATION_DESTRUCTION

// specialized smart pointer, used to get round auto_ptr<>'s lack of the destructor reseting itself to 0.
template<typename T>
struct ResetPointer
{
    ResetPointer():
        _ptr(0) {}

    ResetPointer(T* ptr):
        _ptr(ptr) {}

    ~ResetPointer()
    {
        delete _ptr;
        _ptr = 0;
    }

    inline ResetPointer& operator = (T* ptr)
    {
        if (_ptr==ptr) return *this;
        delete _ptr;
        _ptr = ptr;
        return *this;
    }

    void reset(T* ptr)
    {
        if (_ptr==ptr) return;
        delete _ptr;
        _ptr = ptr;
    }

    inline T& operator*()  { return *_ptr; }

    inline const T& operator*() const { return *_ptr; }

    inline T* operator->() { return _ptr; }

    inline const T* operator->() const   { return _ptr; }

    T* get() { return _ptr; }

    const T* get() const { return _ptr; }

    T* _ptr;
};

typedef ResetPointer<DeleteHandler> DeleteHandlerPointer;
typedef ResetPointer<OpenThreads::Mutex> GlobalMutexPointer;

OpenThreads::Mutex* Referenced::getGlobalReferencedMutex()
{
    static GlobalMutexPointer s_ReferencedGlobalMutext = new OpenThreads::Mutex;
    return s_ReferencedGlobalMutext.get();
}

// helper class for forcing the global mutex to be constructed when the library is loaded.
struct InitGlobalMutexes
{
    InitGlobalMutexes()
    {
        Referenced::getGlobalReferencedMutex();
    }
};
static InitGlobalMutexes s_initGlobalMutexes;

#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
static bool s_useThreadSafeReferenceCounting = getenv("OSG_THREAD_SAFE_REF_UNREF")!=0;
#endif
// static std::auto_ptr<DeleteHandler> s_deleteHandler(0);
static DeleteHandlerPointer s_deleteHandler(0);

static ApplicationUsageProxy Referenced_e0(ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_THREAD_SAFE_REF_UNREF","");

void Referenced::setThreadSafeReferenceCounting(bool enableThreadSafeReferenceCounting)
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    s_useThreadSafeReferenceCounting = enableThreadSafeReferenceCounting;
#endif
}

bool Referenced::getThreadSafeReferenceCounting()
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    return true;
#else
    return s_useThreadSafeReferenceCounting;
#endif
}

void Referenced::setDeleteHandler(DeleteHandler* handler)
{
    s_deleteHandler.reset(handler);
}

DeleteHandler* Referenced::getDeleteHandler()
{
    return s_deleteHandler.get();
}

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
OpenThreads::Mutex& getNumObjectMutex()
{
    static OpenThreads::Mutex s_numObjectMutex;
    return s_numObjectMutex;
}
static int s_numObjects = 0;
#endif

Referenced::Referenced():
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (s_useThreadSafeReferenceCounting)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif

}

Referenced::Referenced(bool threadSafeRefUnref):
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (threadSafeRefUnref)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif
}

Referenced::Referenced(const Referenced&):
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    _observerSet(0),
    _refCount(0)
#else
    _refMutex(0),
    _refCount(0),
    _observerSet(0)
#endif
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
#ifndef ENFORCE_THREADSAFE
    if (s_useThreadSafeReferenceCounting)
#endif
        _refMutex = new OpenThreads::Mutex;
#endif

#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        ++s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif
}

Referenced::~Referenced()
{
#ifdef DEBUG_OBJECT_ALLOCATION_DESTRUCTION
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getNumObjectMutex());
        --s_numObjects;
        printf("Object created, total num=%d\n",s_numObjects);
    }
#endif

    if (_refCount>0)
    {
        OSG_WARN<<"Warning: deleting still referenced object "<<this<<" of type '"<<typeid(this).name()<<"'"<<std::endl;
        OSG_WARN<<"         the final reference count was "<<_refCount<<", memory corruption possible."<<std::endl;
    }

    // signal observers that we are being deleted.
    signalObserversAndDelete(true, false);

    // delete the ObserverSet
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (_observerSet.get()) static_cast<ObserverSet*>(_observerSet.get())->unref();
#else
    if (_observerSet) static_cast<ObserverSet*>(_observerSet)->unref();
#endif

#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (_refMutex) delete _refMutex;
#endif
}

ObserverSet* Referenced::getOrCreateObserverSet() const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet.get());
    while (0 == observerSet)
    {
        ObserverSet* newObserverSet = new ObserverSet(this);
        newObserverSet->ref();

        if (!_observerSet.assign(newObserverSet, 0))
        {
            newObserverSet->unref();
        }

        observerSet = static_cast<ObserverSet*>(_observerSet.get());
    }
    return observerSet;
#else
    if (_refMutex)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_refMutex);
        if (!_observerSet)
        {
            _observerSet = new ObserverSet(this);
            static_cast<ObserverSet*>(_observerSet)->ref();
        }
        return static_cast<ObserverSet*>(_observerSet);
    }
    else
    {
        if (!_observerSet)
        {
            _observerSet = new ObserverSet(this);
            static_cast<ObserverSet*>(_observerSet)->ref();
        }
        return static_cast<ObserverSet*>(_observerSet);
    }
#endif
}

void Referenced::addObserver(Observer* observer) const
{
    getOrCreateObserverSet()->addObserver(observer);
}

void Referenced::removeObserver(Observer* observer) const
{
    getOrCreateObserverSet()->removeObserver(observer);
}

void Referenced::signalObserversAndDelete(bool signalDelete, bool doDelete) const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet.get());
#else
    ObserverSet* observerSet = static_cast<ObserverSet*>(_observerSet);
#endif

    if (observerSet && signalDelete)
    {
        observerSet->signalObjectDeleted(const_cast<Referenced*>(this));
    }

    if (doDelete)
    {
        if (_refCount!=0) OSG_NOTICE<<"Warning Referenced::signalObserversAndDelete(,,) doing delete with _refCount="<<_refCount<<std::endl;

        if (getDeleteHandler()) deleteUsingDeleteHandler();
        else delete this;
    }
}

void Referenced::setThreadSafeRefUnref(bool threadSafe)
{
#if !defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    if (threadSafe)
    {
        if (!_refMutex)
        {
            // we want thread safe ref()/unref() so assign a mutex
            _refMutex = new OpenThreads::Mutex;
        }
    }
    else
    {
        if (_refMutex)
        {
            // we don't want thread safe ref()/unref() so remove any assigned mutex
            OpenThreads::Mutex* tmpMutexPtr = _refMutex;
            _refMutex = 0;
            delete tmpMutexPtr;
        }
    }
#endif
}

int Referenced::unref_nodelete() const
{
#if defined(_OSG_REFERENCED_USE_ATOMIC_OPERATIONS)
    return --_refCount;
#else
    if (_refMutex)
    {
        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*_refMutex);
        return --_refCount;
    }
    else
    {
        return --_refCount;
    }
#endif
}

void Referenced::deleteUsingDeleteHandler() const
{
    getDeleteHandler()->requestDelete(this);
}

} // end of namespace osg
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file
 **/

#include "cyber/common/file.h"
#include "cyber/common/log.h"
#include "gflags/gflags.h"

#include "modules/common/math/vec2d.h"
#include "modules/common/util/util.h"
#include "modules/map/pnc_map/path.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/reference_line/qp_spline_reference_line_smoother.h"
#include "modules/planning/reference_line/reference_line.h"
#include "modules/planning/reference_line/reference_line_smoother.h"

DEFINE_string(input_file, "", "input file with format x,y per line");
DEFINE_string(output_file, "", "output file with format x,y per line");
DEFINE_double(smooth_length, 200, "Smooth this amount of length ");

namespace apollo {
namespace planning {

using common::math::LineSegment2d;
using common::math::Vec2d;
using common::util::DistanceXY;
using hdmap::MapPathPoint;

class SmootherUtil {
 public:
  explicit SmootherUtil(const std::string& filename) : filename_(filename) {
    std::ifstream ifs(filename.c_str(), std::ifstream::in);
    std::string point_str;
    while (std::getline(ifs, point_str)) {
      size_t idx = point_str.find(',');
      if (idx == std::string::npos) {
        continue;
      }
      auto x_str = point_str.substr(0, idx);
      auto y_str = point_str.substr(idx + 1);
      raw_points_.emplace_back(std::stod(x_str), std::stod(y_str));
    }
    CHECK(cyber::common::GetProtoFromFile(FLAGS_smoother_config_filename,
                                          &config_))
        << "Failed to read smoother config file: "
        << FLAGS_smoother_config_filename;
  }

  bool Smooth() {
    if (raw_points_.size() <= 2) {
      AERROR << "the original point size is " << raw_points_.size();
      return false;
    }
    size_t i = 1;
    {
      std::vector<ReferencePoint> ref_points;
      double s = 0.0;
      for (; s < FLAGS_smooth_length && i < raw_points_.size(); ++i) {
        LineSegment2d segment(raw_points_[i - 1], raw_points_[i]);
        ref_points.emplace_back(MapPathPoint(raw_points_[i], segment.heading()),
                                0.0, 0.0);
        s += segment.length();
      }
      ReferenceLine init_ref(ref_points);
      // Prefer "std::make_unique" to direct use of "new".
      // Reference "https://herbsutter.com/gotw/_102/" for details.
      auto smoother_ptr =
          std::make_unique<QpSplineReferenceLineSmoother>(config_);
      auto anchors =
          CreateAnchorPoints(init_ref.reference_points().front(), init_ref);
      smoother_ptr->SetAnchorPoints(anchors);
      ReferenceLine smoothed_init_ref;
      if (!smoother_ptr->Smooth(init_ref, &smoothed_init_ref)) {
        AERROR << "smooth initial reference line failed";
        return false;
      }
      ref_points_ = smoothed_init_ref.reference_points();
    }
    for (; i < raw_points_.size(); ++i) {
      double s = 0.0;
      size_t j = ref_points_.size() - 1;
      while (j > 0 && s < FLAGS_smooth_length / 2.0) {
        s += DistanceXY(ref_points_[j - 1], ref_points_[j]);
        --j;
      }
      ReferenceLine prev_half_ref(ref_points_.begin() + j, ref_points_.end());
      ref_points_.erase(ref_points_.begin() + j, ref_points_.end());
      common::SLPoint sl;
      prev_half_ref.XYToSL(raw_points_[i], &sl);
      while (sl.s() <= prev_half_ref.Length() && i + 1 < raw_points_.size()) {
        prev_half_ref.XYToSL(raw_points_[i + 1], &sl);
        ++i;
      }
      s = 0.0;
      j = i;
      auto ref_points = prev_half_ref.reference_points();
      while (j + 1 < raw_points_.size() && s < FLAGS_smooth_length / 2.0) {
        Vec2d vec = raw_points_[j + 1] - raw_points_[j];
        s += vec.Length();
        ref_points.emplace_back(MapPathPoint(raw_points_[j], vec.Angle()), 0.0,
                                0.0);
        ++j;
      }
      i = j;
      ReferenceLine local_ref(ref_points);
      auto anchors = CreateAnchorPoints(ref_points.front(), local_ref);
      // Prefer "std::make_unique" to direct use of "new".
      // Reference "https://herbsutter.com/gotw/_102/" for details.
      auto smoother_ptr =
          std::make_unique<QpSplineReferenceLineSmoother>(config_);
      smoother_ptr->SetAnchorPoints(anchors);
      ReferenceLine smoothed_local_ref;
      if (!smoother_ptr->Smooth(local_ref, &smoothed_local_ref)) {
        AERROR << "Failed to smooth reference line";
        return false;
      }
      ref_points_.insert(ref_points_.end(),
                         smoothed_local_ref.reference_points().begin(),
                         smoothed_local_ref.reference_points().end());
    }
    return true;
  }

  void Export(const std::string& filename) {
    std::ofstream ofs(filename.c_str());
    if (ofs.fail()) {
      AERROR << "Fail to open file " << filename;
      return;
    }
    ofs.precision(6);
    double s = 0.0;
    // skip the first point and the last point
    for (size_t i = 1; i + 1 < ref_points_.size(); ++i) {
      const auto& point = ref_points_[i];
      ofs << std::fixed << "{\"kappa\": " << point.kappa() << ", \"s\": " << s
          << ", \"theta\": " << point.heading() << ", \"x\":" << point.x()
          << ", \"y\":" << point.y() << ", \"dkappa\":" << point.dkappa()
          << "}";
      s += DistanceXY(point, ref_points_[i + 1]);
    }
    ofs.close();
    AINFO << "Smoothed result saved to " << filename;
  }

 private:
  std::vector<AnchorPoint> CreateAnchorPoints(const ReferencePoint& init_point,
                                              const ReferenceLine& ref_line) {
    std::vector<AnchorPoint> anchor_points;
    int num_of_anchors = std::max(
        2, static_cast<int>(
               ref_line.Length() / config_.max_constraint_interval() + 0.5));
    std::vector<double> anchor_s;
    common::util::uniform_slice(0.0, ref_line.Length(), num_of_anchors - 1,
                                &anchor_s);
    common::SLPoint sl;
    if (!ref_line.XYToSL(Vec2d(init_point.x(), init_point.y()), &sl)) {
      AERROR << "Failed to project init point to reference line";
      return anchor_points;
    }
    bool set_init_point = false;
    for (const double s : anchor_s) {
      if (s + config_.max_constraint_interval() / 2.0 < sl.s()) {
        continue;
      }
      ReferencePoint ref_point;
      if (!set_init_point) {
        set_init_point = true;
        ref_point = init_point;
      } else {
        ref_point = ref_line.GetReferencePoint(s);
      }
      AnchorPoint anchor;
      anchor.path_point.set_x(ref_point.x());
      anchor.path_point.set_y(ref_point.y());
      anchor.path_point.set_z(0.0);
      anchor.path_point.set_s(s);
      anchor.path_point.set_theta(ref_point.heading());
      anchor.path_point.set_kappa(ref_point.kappa());
      anchor.lateral_bound = config_.lateral_boundary_bound();
      anchor.longitudinal_bound = config_.longitudinal_boundary_bound();
      anchor_points.emplace_back(anchor);
    }
    anchor_points.front().longitudinal_bound = 0;
    anchor_points.front().lateral_bound = 0;
    anchor_points.front().enforced = true;
    anchor_points.back().longitudinal_bound = 0;
    anchor_points.back().lateral_bound = 0;
    anchor_points.back().enforced = true;
    return anchor_points;
  }

 private:
  std::string filename_;
  std::vector<common::math::Vec2d> raw_points_;
  std::vector<ReferencePoint> ref_points_;
  ReferenceLine smoothed_ref_;
  ReferenceLineSmootherConfig config_;
};

}  // namespace planning
}  // namespace apollo

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  google::ParseCommandLineFlags(&argc, &argv, true);

  if (FLAGS_input_file.empty()) {
    AERROR << "need to provide --input_file";
    return 0;
  }
  apollo::planning::SmootherUtil smoother_util(FLAGS_input_file);
  if (!smoother_util.Smooth()) {
    AERROR << "Failed to smooth a the line";
  }
  if (FLAGS_output_file.empty()) {
    FLAGS_output_file = FLAGS_input_file + ".smoothed";
    AINFO << "Output file not provided, set to: " << FLAGS_output_file;
  }
  smoother_util.Export(FLAGS_output_file);
  return 0;
}
<commit_msg>*update end line white space<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * 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.
 *****************************************************************************/

/**
 * @file
 **/

#include "cyber/common/file.h"
#include "cyber/common/log.h"
#include "gflags/gflags.h"

#include "modules/common/math/vec2d.h"
#include "modules/common/util/util.h"
#include "modules/map/pnc_map/path.h"
#include "modules/planning/common/planning_gflags.h"
#include "modules/planning/reference_line/qp_spline_reference_line_smoother.h"
#include "modules/planning/reference_line/reference_line.h"
#include "modules/planning/reference_line/reference_line_smoother.h"

DEFINE_string(input_file, "", "input file with format x,y per line");
DEFINE_string(output_file, "", "output file with format x,y per line");
DEFINE_double(smooth_length, 200, "Smooth this amount of length ");

namespace apollo {
namespace planning {

using common::math::LineSegment2d;
using common::math::Vec2d;
using common::util::DistanceXY;
using hdmap::MapPathPoint;

class SmootherUtil {
 public:
  explicit SmootherUtil(const std::string& filename) : filename_(filename) {
    std::ifstream ifs(filename.c_str(), std::ifstream::in);
    std::string point_str;
    while (std::getline(ifs, point_str)) {
      size_t idx = point_str.find(',');
      if (idx == std::string::npos) {
        continue;
      }
      auto x_str = point_str.substr(0, idx);
      auto y_str = point_str.substr(idx + 1);
      raw_points_.emplace_back(std::stod(x_str), std::stod(y_str));
    }
    CHECK(cyber::common::GetProtoFromFile(FLAGS_smoother_config_filename,
                                          &config_))
        << "Failed to read smoother config file: "
        << FLAGS_smoother_config_filename;
  }

  bool Smooth() {
    if (raw_points_.size() <= 2) {
      AERROR << "the original point size is " << raw_points_.size();
      return false;
    }
    size_t i = 1;
    {
      std::vector<ReferencePoint> ref_points;
      double s = 0.0;
      for (; s < FLAGS_smooth_length && i < raw_points_.size(); ++i) {
        LineSegment2d segment(raw_points_[i - 1], raw_points_[i]);
        ref_points.emplace_back(MapPathPoint(raw_points_[i], segment.heading()),
                                0.0, 0.0);
        s += segment.length();
      }
      ReferenceLine init_ref(ref_points);
      // Prefer "std::make_unique" to direct use of "new".
      // Reference "https://herbsutter.com/gotw/_102/" for details.
      auto smoother_ptr =
          std::make_unique<QpSplineReferenceLineSmoother>(config_);
      auto anchors =
          CreateAnchorPoints(init_ref.reference_points().front(), init_ref);
      smoother_ptr->SetAnchorPoints(anchors);
      ReferenceLine smoothed_init_ref;
      if (!smoother_ptr->Smooth(init_ref, &smoothed_init_ref)) {
        AERROR << "smooth initial reference line failed";
        return false;
      }
      ref_points_ = smoothed_init_ref.reference_points();
    }
    for (; i < raw_points_.size(); ++i) {
      double s = 0.0;
      size_t j = ref_points_.size() - 1;
      while (j > 0 && s < FLAGS_smooth_length / 2.0) {
        s += DistanceXY(ref_points_[j - 1], ref_points_[j]);
        --j;
      }
      ReferenceLine prev_half_ref(ref_points_.begin() + j, ref_points_.end());
      ref_points_.erase(ref_points_.begin() + j, ref_points_.end());
      common::SLPoint sl;
      prev_half_ref.XYToSL(raw_points_[i], &sl);
      while (sl.s() <= prev_half_ref.Length() && i + 1 < raw_points_.size()) {
        prev_half_ref.XYToSL(raw_points_[i + 1], &sl);
        ++i;
      }
      s = 0.0;
      j = i;
      auto ref_points = prev_half_ref.reference_points();
      while (j + 1 < raw_points_.size() && s < FLAGS_smooth_length / 2.0) {
        Vec2d vec = raw_points_[j + 1] - raw_points_[j];
        s += vec.Length();
        ref_points.emplace_back(MapPathPoint(raw_points_[j], vec.Angle()), 0.0,
                                0.0);
        ++j;
      }
      i = j;
      ReferenceLine local_ref(ref_points);
      auto anchors = CreateAnchorPoints(ref_points.front(), local_ref);
      // Prefer "std::make_unique" to direct use of "new".
      // Reference "https://herbsutter.com/gotw/_102/" for details.
      auto smoother_ptr =
          std::make_unique<QpSplineReferenceLineSmoother>(config_);
      smoother_ptr->SetAnchorPoints(anchors);
      ReferenceLine smoothed_local_ref;
      if (!smoother_ptr->Smooth(local_ref, &smoothed_local_ref)) {
        AERROR << "Failed to smooth reference line";
        return false;
      }
      ref_points_.insert(ref_points_.end(),
                         smoothed_local_ref.reference_points().begin(),
                         smoothed_local_ref.reference_points().end());
    }
    return true;
  }

  void Export(const std::string& filename) {
    std::ofstream ofs(filename.c_str());
    if (ofs.fail()) {
      AERROR << "Fail to open file " << filename;
      return;
    }
    ofs.precision(6);
    double s = 0.0;
    // skip the first point and the last point
    for (size_t i = 1; i + 1 < ref_points_.size(); ++i) {
      const auto& point = ref_points_[i];
      ofs << std::fixed << "{\"kappa\": " << point.kappa() << ", \"s\": " << s
          << ", \"theta\": " << point.heading() << ", \"x\":" << point.x()
          << ", \"y\":" << point.y() << ", \"dkappa\":" << point.dkappa()
          << "}";
      s += DistanceXY(point, ref_points_[i + 1]);
    }
    ofs.close();
    AINFO << "Smoothed result saved to " << filename;
  }

 private:
  std::vector<AnchorPoint> CreateAnchorPoints(const ReferencePoint& init_point,
                                              const ReferenceLine& ref_line) {
    std::vector<AnchorPoint> anchor_points;
    int num_of_anchors = std::max(
        2, static_cast<int>(
               ref_line.Length() / config_.max_constraint_interval() + 0.5));
    std::vector<double> anchor_s;
    common::util::uniform_slice(0.0, ref_line.Length(), num_of_anchors - 1,
                                &anchor_s);
    common::SLPoint sl;
    if (!ref_line.XYToSL(Vec2d(init_point.x(), init_point.y()), &sl)) {
      AERROR << "Failed to project init point to reference line";
      return anchor_points;
    }
    bool set_init_point = false;
    for (const double s : anchor_s) {
      if (s + config_.max_constraint_interval() / 2.0 < sl.s()) {
        continue;
      }
      ReferencePoint ref_point;
      if (!set_init_point) {
        set_init_point = true;
        ref_point = init_point;
      } else {
        ref_point = ref_line.GetReferencePoint(s);
      }
      AnchorPoint anchor;
      anchor.path_point.set_x(ref_point.x());
      anchor.path_point.set_y(ref_point.y());
      anchor.path_point.set_z(0.0);
      anchor.path_point.set_s(s);
      anchor.path_point.set_theta(ref_point.heading());
      anchor.path_point.set_kappa(ref_point.kappa());
      anchor.lateral_bound = config_.lateral_boundary_bound();
      anchor.longitudinal_bound = config_.longitudinal_boundary_bound();
      anchor_points.emplace_back(anchor);
    }
    anchor_points.front().longitudinal_bound = 0;
    anchor_points.front().lateral_bound = 0;
    anchor_points.front().enforced = true;
    anchor_points.back().longitudinal_bound = 0;
    anchor_points.back().lateral_bound = 0;
    anchor_points.back().enforced = true;
    return anchor_points;
  }

 private:
  std::string filename_;
  std::vector<common::math::Vec2d> raw_points_;
  std::vector<ReferencePoint> ref_points_;
  ReferenceLine smoothed_ref_;
  ReferenceLineSmootherConfig config_;
};

}  // namespace planning
}  // namespace apollo

int main(int argc, char* argv[]) {
  google::InitGoogleLogging(argv[0]);
  google::ParseCommandLineFlags(&argc, &argv, true);

  if (FLAGS_input_file.empty()) {
    AERROR << "need to provide --input_file";
    return 0;
  }
  apollo::planning::SmootherUtil smoother_util(FLAGS_input_file);
  if (!smoother_util.Smooth()) {
    AERROR << "Failed to smooth a the line";
  }
  if (FLAGS_output_file.empty()) {
    FLAGS_output_file = FLAGS_input_file + ".smoothed";
    AINFO << "Output file not provided, set to: " << FLAGS_output_file;
  }
  smoother_util.Export(FLAGS_output_file);
  return 0;
}
<|endoftext|>