<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2016 Francois Beaune, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// appleseed.renderer headers.
#include "renderer/kernel/intersection/intersectionsettings.h"
#include "renderer/modeling/object/curveobject.h"
#include "renderer/modeling/object/meshobject.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/project/projectfilewriter.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/containers.h"
#include "renderer/modeling/scene/scene.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/platform/thread.h"
#include "foundation/utility/autoreleaseptr.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/test.h"

// Boost headers.
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"

// Standard headers.
#include <string>

using namespace boost;
using namespace boost::filesystem;
using namespace foundation;
using namespace renderer;
using namespace std;

TEST_SUITE(Renderer_Modeling_Project_ProjectFileWriter)
{
    struct Fixture
    {
        const path                  m_input_directory;
        const path                  m_output_directory;
        auto_release_ptr<Project>   m_project;

        Fixture()
          : m_input_directory(canonical("unit tests/inputs/test_projectfilewriter/"))
          , m_output_directory(canonical("unit tests/outputs/test_projectfilewriter/"))
        {
            remove_all(m_output_directory);

            // On Windows, the create_directory() call below will fail with an Access Denied error
            // if a File Explorer window was opened in the output directory that we just deleted.
            // A small pause solves the problem. The namespace qualifier is required on Linux.
            foundation::sleep(50);

            create_directory(m_output_directory);
        }

        void create_project()
        {
            m_project = ProjectFactory::create("project");
            m_project->set_scene(SceneFactory::create());

            m_project->set_path((m_output_directory / "project.appleseed").string().c_str());
            m_project->search_paths().set_root_path(m_output_directory.string());
        }

        void create_assembly()
        {
            m_project->get_scene()->assemblies().insert(AssemblyFactory().create("assembly"));
        }

        Assembly* get_assembly()
        {
            return m_project->get_scene()->assemblies().get_by_name("assembly");
        }

        template <typename T>
        void create_mesh_object(const char* object_name, const T& filename)
        {
            get_assembly()->objects().insert(
                auto_release_ptr<Object>(
                    MeshObjectFactory::create(
                        object_name,
                        ParamArray().insert("filename", filename))));
        }

        void create_mesh_object(const char* object_name, const path& filename)
        {
            create_mesh_object(object_name, filename.string());
        }

        const char* get_mesh_object_filename(const char* object_name)
        {
            return get_assembly()->objects().get_by_name(object_name)->get_parameters().get("filename");
        }

        void create_curve_object(const char* object_name)
        {
            auto_release_ptr<CurveObject> curve_object(
                CurveObjectFactory::create(object_name, ParamArray()));

            static const GVector3 ControlPoints[] = { GVector3(0.0, 0.0, 0.0), GVector3(0.0, 1.0, 0.0) };
            curve_object->push_curve1(Curve1Type(ControlPoints, GScalar(0.1)));

            get_assembly()->objects().insert(auto_release_ptr<Object>(curve_object));
        }

        void create_geometry_file(const path& filepath)
        {
            const path output_path = m_output_directory / filepath;
            create_directories(output_path.parent_path());
            copy_file(m_input_directory / "object.obj", output_path);
        }
    };

    TEST_CASE_F(Write_CopyAllAssetsIsNotSet_HandleAssetPaths, Fixture)
    {
        m_project = ProjectFactory::create("project");
        m_project->set_scene(SceneFactory::create());

        const path project_directory = m_input_directory / "setup/main/";
        m_project->set_path((project_directory / "project.appleseed").string().c_str());
        m_project->search_paths().set_root_path(project_directory.string());

        m_project->search_paths().push_back("subdirectory");
        m_project->search_paths().push_back(canonical(project_directory / "../alternate/subdirectory").string());

        create_assembly();
        create_mesh_object("asset1", "asset1.obj"); // found in project's root directory
        create_mesh_object("asset2", "asset2.obj"); // found in subdirectory/ via relative search path
        create_mesh_object("asset3", "asset3.obj"); // found in ../alternate/subdirectory/ via absolute search path
        create_mesh_object("asset4", "subdirectory/asset4.obj");
        create_mesh_object("asset5", canonical(project_directory / "asset5.obj"));
        create_mesh_object("asset6", canonical(project_directory / "../alternate/asset6.obj"));
        create_mesh_object("asset7", "../alternate/asset7.obj");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "project.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);

        // Check the search paths.
        EXPECT_EQ(2, m_project->search_paths().size());
        EXPECT_EQ(string("subdirectory"), m_project->search_paths()[0]);
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/subdirectory").string(), m_project->search_paths()[1]);

        // Check the asset paths.
        EXPECT_EQ(string("asset1.obj"),                                        get_mesh_object_filename("asset1"));
        EXPECT_EQ(string("asset2.obj"),                                        get_mesh_object_filename("asset2"));
        EXPECT_EQ(string("asset3.obj"),                                        get_mesh_object_filename("asset3"));
        EXPECT_EQ(string("subdirectory/asset4.obj"),                           get_mesh_object_filename("asset4"));
        EXPECT_EQ(canonical(project_directory / "asset5.obj"),                 get_mesh_object_filename("asset5"));
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/asset6.obj"), get_mesh_object_filename("asset6"));
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/asset7.obj"), get_mesh_object_filename("asset7"));
    }

    TEST_CASE_F(Write_CopyAllAssetsIsSet_HandleAssetPaths, Fixture)
    {
        m_project = ProjectFactory::create("project");
        m_project->set_scene(SceneFactory::create());

        const path project_directory = m_input_directory / "setup/main/";
        m_project->set_path((project_directory / "project.appleseed").string().c_str());
        m_project->search_paths().set_root_path(project_directory.string());

        m_project->search_paths().push_back("subdirectory");
        m_project->search_paths().push_back(canonical(project_directory / "../alternate/subdirectory").string());

        create_assembly();
        create_mesh_object("asset1", "asset1.obj"); // found in project's root directory
        create_mesh_object("asset2", "asset2.obj"); // found in subdirectory/ via search paths
        create_mesh_object("asset3", "asset3.obj"); // found in ../alternate/subdirectory/ via absolute search path
        create_mesh_object("asset4", "subdirectory/asset4.obj");
        create_mesh_object("asset5", canonical(project_directory / "asset5.obj"));
        create_mesh_object("asset6", canonical(project_directory / "../alternate/asset6.obj"));
        create_mesh_object("asset7", "../alternate/asset7.obj");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "project.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment |
                ProjectFileWriter::CopyAllAssets);

        ASSERT_TRUE(success);

        // Check the search paths.
        EXPECT_EQ(1, m_project->search_paths().size());
        EXPECT_EQ(string("subdirectory"), m_project->search_paths()[0]);

        // Check the asset paths.
        EXPECT_EQ(string("asset1.obj"),              get_mesh_object_filename("asset1"));
        EXPECT_EQ(string("asset2.obj"),              get_mesh_object_filename("asset2"));
        EXPECT_EQ(string("assets/asset3.obj"),       get_mesh_object_filename("asset3"));
        EXPECT_EQ(string("subdirectory/asset4.obj"), get_mesh_object_filename("asset4"));
        EXPECT_EQ(string("assets/asset5.obj"),       get_mesh_object_filename("asset5"));
        EXPECT_EQ(string("assets/asset6.obj"),       get_mesh_object_filename("asset6"));
        EXPECT_EQ(string("assets/asset7.obj"),       get_mesh_object_filename("asset7"));
    }

    TEST_CASE_F(Write_MeshObjectWithMultivaluedFilenameParameter_DoesNotAddAnotherFilenameParameter, Fixture)
    {
        create_geometry_file("bunny.0.obj");
        create_geometry_file("bunny.1.obj");

        create_project();
        create_assembly();
        create_mesh_object(
            "bunny",
            ParamArray()
                .insert("0", "bunny.0.obj")
                .insert("1", "bunny.1.obj"));

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "multivaluedfilenameobject.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);
        EXPECT_FALSE(get_assembly()->objects().get_by_name("bunny")->get_parameters().strings().exist("filename"));
    }

    TEST_CASE_F(Write_CurveObjectWithoutFilePath_OmitWritingGeometryFilesIsNotSet_CreatesCurvesFileAndAssignsFilePath, Fixture)
    {
        create_project();
        create_assembly();
        create_curve_object("curve_object");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "curve_object.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);
        EXPECT_TRUE(exists(m_output_directory / "curve_object.curves"));
        EXPECT_EQ(
            string("curve_object.curves"),
            get_assembly()->objects().get_by_name("curve_object")->get_parameters().get("filepath"));
    }

    TEST_CASE_F(Write_CurveObjectWithoutFilePath_OmitWritingGeometryFilesIsSet_OnlyAssignsFilePath, Fixture)
    {
        create_project();
        create_assembly();
        create_curve_object("curve_object");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "curve_object.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment |
                ProjectFileWriter::OmitWritingGeometryFiles);

        ASSERT_TRUE(success);
        EXPECT_FALSE(exists(m_output_directory / "curve_object.curves"));
        EXPECT_EQ(
            string("curve_object.curves"),
            get_assembly()->objects().get_by_name("curve_object")->get_parameters().get("filepath"));
    }
}
<commit_msg>Fix unit tests<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2016 Francois Beaune, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// appleseed.renderer headers.
#include "renderer/kernel/intersection/intersectionsettings.h"
#include "renderer/modeling/object/curveobject.h"
#include "renderer/modeling/object/meshobject.h"
#include "renderer/modeling/object/object.h"
#include "renderer/modeling/project/project.h"
#include "renderer/modeling/project/projectfilewriter.h"
#include "renderer/modeling/scene/assembly.h"
#include "renderer/modeling/scene/containers.h"
#include "renderer/modeling/scene/scene.h"
#include "renderer/utility/paramarray.h"

// appleseed.foundation headers.
#include "foundation/platform/thread.h"
#include "foundation/utility/autoreleaseptr.h"
#include "foundation/utility/searchpaths.h"
#include "foundation/utility/test.h"

// Boost headers.
#include "boost/filesystem/operations.hpp"
#include "boost/filesystem/path.hpp"

// Standard headers.
#include <string>

using namespace boost;
using namespace boost::filesystem;
using namespace foundation;
using namespace renderer;
using namespace std;

TEST_SUITE(Renderer_Modeling_Project_ProjectFileWriter)
{
    struct Fixture
    {
        const path                  m_input_directory;
        const path                  m_output_directory;
        auto_release_ptr<Project>   m_project;

        Fixture()
          : m_input_directory(absolute("unit tests/inputs/test_projectfilewriter/"))
          , m_output_directory(absolute("unit tests/outputs/test_projectfilewriter/"))
        {
            remove_all(m_output_directory);

            // On Windows, the create_directory() call below will fail with an Access Denied error
            // if a File Explorer window was opened in the output directory that we just deleted.
            // A small pause solves the problem. The namespace qualifier is required on Linux.
            foundation::sleep(50);

            create_directory(m_output_directory);
        }

        void create_project()
        {
            m_project = ProjectFactory::create("project");
            m_project->set_scene(SceneFactory::create());

            m_project->set_path((m_output_directory / "project.appleseed").string().c_str());
            m_project->search_paths().set_root_path(m_output_directory.string());
        }

        void create_assembly()
        {
            m_project->get_scene()->assemblies().insert(AssemblyFactory().create("assembly"));
        }

        Assembly* get_assembly()
        {
            return m_project->get_scene()->assemblies().get_by_name("assembly");
        }

        template <typename T>
        void create_mesh_object(const char* object_name, const T& filename)
        {
            get_assembly()->objects().insert(
                auto_release_ptr<Object>(
                    MeshObjectFactory::create(
                        object_name,
                        ParamArray().insert("filename", filename))));
        }

        void create_mesh_object(const char* object_name, const path& filename)
        {
            create_mesh_object(object_name, filename.string());
        }

        const char* get_mesh_object_filename(const char* object_name)
        {
            return get_assembly()->objects().get_by_name(object_name)->get_parameters().get("filename");
        }

        void create_curve_object(const char* object_name)
        {
            auto_release_ptr<CurveObject> curve_object(
                CurveObjectFactory::create(object_name, ParamArray()));

            static const GVector3 ControlPoints[] = { GVector3(0.0, 0.0, 0.0), GVector3(0.0, 1.0, 0.0) };
            curve_object->push_curve1(Curve1Type(ControlPoints, GScalar(0.1)));

            get_assembly()->objects().insert(auto_release_ptr<Object>(curve_object));
        }

        void create_geometry_file(const path& filepath)
        {
            const path output_path = m_output_directory / filepath;
            create_directories(output_path.parent_path());
            copy_file(m_input_directory / "object.obj", output_path);
        }
    };

    TEST_CASE_F(Write_CopyAllAssetsIsNotSet_HandleAssetPaths, Fixture)
    {
        m_project = ProjectFactory::create("project");
        m_project->set_scene(SceneFactory::create());

        const path project_directory = m_input_directory / "setup/main/";
        m_project->set_path((project_directory / "project.appleseed").string().c_str());
        m_project->search_paths().set_root_path(project_directory.string());

        m_project->search_paths().push_back("subdirectory");
        m_project->search_paths().push_back(canonical(project_directory / "../alternate/subdirectory").string());

        create_assembly();
        create_mesh_object("asset1", "asset1.obj"); // found in project's root directory
        create_mesh_object("asset2", "asset2.obj"); // found in subdirectory/ via relative search path
        create_mesh_object("asset3", "asset3.obj"); // found in ../alternate/subdirectory/ via absolute search path
        create_mesh_object("asset4", "subdirectory/asset4.obj");
        create_mesh_object("asset5", canonical(project_directory / "asset5.obj"));
        create_mesh_object("asset6", canonical(project_directory / "../alternate/asset6.obj"));
        create_mesh_object("asset7", "../alternate/asset7.obj");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "project.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);

        // Check the search paths.
        EXPECT_EQ(2, m_project->search_paths().size());
        EXPECT_EQ(string("subdirectory"), m_project->search_paths()[0]);
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/subdirectory").string(), m_project->search_paths()[1]);

        // Check the asset paths.
        EXPECT_EQ(string("asset1.obj"),                                        get_mesh_object_filename("asset1"));
        EXPECT_EQ(string("asset2.obj"),                                        get_mesh_object_filename("asset2"));
        EXPECT_EQ(string("asset3.obj"),                                        get_mesh_object_filename("asset3"));
        EXPECT_EQ(string("subdirectory/asset4.obj"),                           get_mesh_object_filename("asset4"));
        EXPECT_EQ(canonical(project_directory / "asset5.obj"),                 get_mesh_object_filename("asset5"));
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/asset6.obj"), get_mesh_object_filename("asset6"));
        EXPECT_EQ(canonical(m_input_directory / "setup/alternate/asset7.obj"), get_mesh_object_filename("asset7"));
    }

    TEST_CASE_F(Write_CopyAllAssetsIsSet_HandleAssetPaths, Fixture)
    {
        m_project = ProjectFactory::create("project");
        m_project->set_scene(SceneFactory::create());

        const path project_directory = m_input_directory / "setup/main/";
        m_project->set_path((project_directory / "project.appleseed").string().c_str());
        m_project->search_paths().set_root_path(project_directory.string());

        m_project->search_paths().push_back("subdirectory");
        m_project->search_paths().push_back(canonical(project_directory / "../alternate/subdirectory").string());

        create_assembly();
        create_mesh_object("asset1", "asset1.obj"); // found in project's root directory
        create_mesh_object("asset2", "asset2.obj"); // found in subdirectory/ via search paths
        create_mesh_object("asset3", "asset3.obj"); // found in ../alternate/subdirectory/ via absolute search path
        create_mesh_object("asset4", "subdirectory/asset4.obj");
        create_mesh_object("asset5", canonical(project_directory / "asset5.obj"));
        create_mesh_object("asset6", canonical(project_directory / "../alternate/asset6.obj"));
        create_mesh_object("asset7", "../alternate/asset7.obj");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "project.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment |
                ProjectFileWriter::CopyAllAssets);

        ASSERT_TRUE(success);

        // Check the search paths.
        EXPECT_EQ(1, m_project->search_paths().size());
        EXPECT_EQ(string("subdirectory"), m_project->search_paths()[0]);

        // Check the asset paths.
        EXPECT_EQ(string("asset1.obj"),              get_mesh_object_filename("asset1"));
        EXPECT_EQ(string("asset2.obj"),              get_mesh_object_filename("asset2"));
        EXPECT_EQ(string("assets/asset3.obj"),       get_mesh_object_filename("asset3"));
        EXPECT_EQ(string("subdirectory/asset4.obj"), get_mesh_object_filename("asset4"));
        EXPECT_EQ(string("assets/asset5.obj"),       get_mesh_object_filename("asset5"));
        EXPECT_EQ(string("assets/asset6.obj"),       get_mesh_object_filename("asset6"));
        EXPECT_EQ(string("assets/asset7.obj"),       get_mesh_object_filename("asset7"));
    }

    TEST_CASE_F(Write_MeshObjectWithMultivaluedFilenameParameter_DoesNotAddAnotherFilenameParameter, Fixture)
    {
        create_geometry_file("bunny.0.obj");
        create_geometry_file("bunny.1.obj");

        create_project();
        create_assembly();
        create_mesh_object(
            "bunny",
            ParamArray()
                .insert("0", "bunny.0.obj")
                .insert("1", "bunny.1.obj"));

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "multivaluedfilenameobject.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);
        EXPECT_FALSE(get_assembly()->objects().get_by_name("bunny")->get_parameters().strings().exist("filename"));
    }

    TEST_CASE_F(Write_CurveObjectWithoutFilePath_OmitWritingGeometryFilesIsNotSet_CreatesCurvesFileAndAssignsFilePath, Fixture)
    {
        create_project();
        create_assembly();
        create_curve_object("curve_object");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "curve_object.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment);

        ASSERT_TRUE(success);
        EXPECT_TRUE(exists(m_output_directory / "curve_object.curves"));
        EXPECT_EQ(
            string("curve_object.curves"),
            get_assembly()->objects().get_by_name("curve_object")->get_parameters().get("filepath"));
    }

    TEST_CASE_F(Write_CurveObjectWithoutFilePath_OmitWritingGeometryFilesIsSet_OnlyAssignsFilePath, Fixture)
    {
        create_project();
        create_assembly();
        create_curve_object("curve_object");

        const bool success =
            ProjectFileWriter::write(
                m_project.ref(),
                (m_output_directory / "curve_object.appleseed").string().c_str(),
                ProjectFileWriter::OmitHeaderComment |
                ProjectFileWriter::OmitWritingGeometryFiles);

        ASSERT_TRUE(success);
        EXPECT_FALSE(exists(m_output_directory / "curve_object.curves"));
        EXPECT_EQ(
            string("curve_object.curves"),
            get_assembly()->objects().get_by_name("curve_object")->get_parameters().get("filepath"));
    }
}
<|endoftext|>


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

// Interface header.
#include "connectableentity.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/global/globaltypes.h"
#include "renderer/modeling/input/source.h"

// Standard headers.
#include <cassert>

namespace renderer
{

bool ConnectableEntity::is_uniform_zero(const Source* source)
{
    assert(source);

    if (source->is_uniform())
    {
        Spectrum exitance;
        Alpha alpha;
        source->evaluate_uniform(exitance, alpha);

        if (exitance == Spectrum(0.0f))
            return true;
    }

    return false;
}

bool ConnectableEntity::is_uniform_zero(const char* input_name) const
{
    return is_uniform_zero(m_inputs.source(input_name));
}

bool ConnectableEntity::is_uniform_zero(const Source* source, const Source* multiplier)
{
    return is_uniform_zero(source) || is_uniform_zero(multiplier);
}

bool ConnectableEntity::is_uniform_zero(const char* input_name, const char* multiplier_name) const
{
    return is_uniform_zero(m_inputs.source(input_name), m_inputs.source(multiplier_name));
}

bool ConnectableEntity::check_uniform(const char* input_name) const
{
    const Source* source = m_inputs.source(input_name);

    assert(source);

    if (source->is_uniform())
        return true;

    RENDERER_LOG_ERROR(
        "the \"%s\" input of \"%s\" must be bound to a scalar or a color.",
        input_name,
        get_name());

    return false;
}

void ConnectableEntity::check_non_zero_exitance(const Source* source) const
{
    if (is_uniform_zero(source))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const char* input_name) const
{
    if (is_uniform_zero(input_name))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const Source* source, const Source* multiplier) const
{
    if (is_uniform_zero(source, multiplier))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const char* input_name, const char* multiplier_name) const
{
    if (is_uniform_zero(input_name, multiplier_name))
        warn_zero_exitance();
}

void ConnectableEntity::warn_zero_exitance() const
{
    RENDERER_LOG_WARNING(
        "\"%s\" has a zero exitance and will slow down rendering "
        "without contributing to the lighting.",
        get_name());
}

}   // namespace renderer
<commit_msg>renamed a variable.<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2012 Francois Beaune, Jupiter Jazz Limited
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "connectableentity.h"

// appleseed.renderer headers.
#include "renderer/global/globallogger.h"
#include "renderer/global/globaltypes.h"
#include "renderer/modeling/input/source.h"

// Standard headers.
#include <cassert>

namespace renderer
{

bool ConnectableEntity::is_uniform_zero(const Source* source)
{
    assert(source);

    if (source->is_uniform())
    {
        Spectrum spectrum;
        Alpha alpha;
        source->evaluate_uniform(spectrum, alpha);

        if (spectrum == Spectrum(0.0f))
            return true;
    }

    return false;
}

bool ConnectableEntity::is_uniform_zero(const char* input_name) const
{
    return is_uniform_zero(m_inputs.source(input_name));
}

bool ConnectableEntity::is_uniform_zero(const Source* source, const Source* multiplier)
{
    return is_uniform_zero(source) || is_uniform_zero(multiplier);
}

bool ConnectableEntity::is_uniform_zero(const char* input_name, const char* multiplier_name) const
{
    return is_uniform_zero(m_inputs.source(input_name), m_inputs.source(multiplier_name));
}

bool ConnectableEntity::check_uniform(const char* input_name) const
{
    const Source* source = m_inputs.source(input_name);

    assert(source);

    if (source->is_uniform())
        return true;

    RENDERER_LOG_ERROR(
        "the \"%s\" input of \"%s\" must be bound to a scalar or a color.",
        input_name,
        get_name());

    return false;
}

void ConnectableEntity::check_non_zero_exitance(const Source* source) const
{
    if (is_uniform_zero(source))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const char* input_name) const
{
    if (is_uniform_zero(input_name))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const Source* source, const Source* multiplier) const
{
    if (is_uniform_zero(source, multiplier))
        warn_zero_exitance();
}

void ConnectableEntity::check_non_zero_exitance(const char* input_name, const char* multiplier_name) const
{
    if (is_uniform_zero(input_name, multiplier_name))
        warn_zero_exitance();
}

void ConnectableEntity::warn_zero_exitance() const
{
    RENDERER_LOG_WARNING(
        "\"%s\" has a zero exitance and will slow down rendering "
        "without contributing to the lighting.",
        get_name());
}

}   // namespace renderer
<|endoftext|>


<commit_before><commit_msg>write_bin_mesh() and read_bin_mesh() functions.<commit_after><|endoftext|>


<commit_before>#include "AnimationController.hpp"
#include "Skeleton.hpp"
#include "AnimationClip.hpp"
#include <Utility/Log.hpp>
#include "../Hymn.hpp"

using namespace Animation;

AnimationController::~AnimationController() {
    Clear();
}

void AnimationController::Save(const std::string& path) {
    // Open file.
    std::ofstream file(path, std::ios::binary);

    // Check if file is open, if not log and early return.
    if (!file.is_open()) {
        Log() << "Could not save animation controller file: " << path << "\n";
        file.close();
        return;
    }

    uint32_t numNodes = animationNodes.size();
    file.write(reinterpret_cast<char*>(&numNodes), sizeof(uint32_t));

    for (Node* node : animationNodes) {
        if (typeid(*node) == typeid(AnimationAction)) {
            NodeType nodetype = ACTION;
            file.write(reinterpret_cast<char*>(&nodetype), sizeof(NodeType));
        } else {
            NodeType nodetype = TRANSITION;
            file.write(reinterpret_cast<char*>(&nodetype), sizeof(NodeType));
        }

        node->Save(&file);
    }

    uint32_t numBools = boolMap.size();
    file.write(reinterpret_cast<char*>(&numBools), sizeof(uint32_t));
    for (BoolItem* b : boolMap) {
        file.write(reinterpret_cast<char*>(b), sizeof(BoolItem));
    }

    uint32_t numFloats = floatMap.size();
    file.write(reinterpret_cast<char*>(&numFloats), sizeof(uint32_t));
    for (FloatItem* f : floatMap) {
        file.write(reinterpret_cast<char*>(f), sizeof(FloatItem));
    }

    // Close file.
    file.close();
}

void AnimationController::Load(const std::string& name) {
    std::size_t pos = name.find_last_of('/');
    this->name = name.substr(pos + 1);
    path = name.substr(0, pos + 1);

    // Open file.
    std::ifstream file(Hymn().GetPath() + "/" + name + ".asset", std::ios::binary);

    // Check if file is open, if not log and early return.
    if (!file.is_open()) {
        Log() << "Could not load animation controller file: " << Hymn().GetPath() + "/" + name + ".asset" << "\n";
        file.close();
        return;
    }

    // Clear if anything is loaded.
    Clear();

    uint32_t numNodes = 0;
    file.read(reinterpret_cast<char*>(&numNodes), sizeof(uint32_t));
    for (unsigned int i = 0; i < numNodes; ++i)  {
        NodeType nodeType;
        file.read(reinterpret_cast<char*>(&nodeType), sizeof(NodeType));
        if (nodeType == ACTION) {
            AnimationAction* node = new AnimationAction;
            node->Load(&file);
            animationNodes.push_back(node);
        } else {
            AnimationTransition* node = new AnimationTransition;
            node->Load(&file);
            animationNodes.push_back(node);
        }
    }

    uint32_t numBools = boolMap.size();
    file.read(reinterpret_cast<char*>(&numBools), sizeof(uint32_t));
    boolMap.resize(numBools);
    for (auto i = 0; i < numBools; ++i)
        file.read(reinterpret_cast<char*>(boolMap[i]), sizeof(BoolItem));

    uint32_t numFloats = floatMap.size();
    file.read(reinterpret_cast<char*>(&numFloats), sizeof(uint32_t));
    floatMap.resize(numFloats);
    for (auto i = 0; i < numFloats; ++i)
        file.read(reinterpret_cast<char*>(floatMap[i]), sizeof(FloatItem));

    // Close file.
    file.close();
}

void AnimationController::Clear() {
    for (Node* node : animationNodes) {
        delete node;
    }
    animationNodes.clear();

    for (BoolItem* b : boolMap) {
        delete b;
    }
    boolMap.clear();

    for (FloatItem* f : floatMap) {
        delete f;
    }
    floatMap.clear();
}
<commit_msg>Remove braces around single line body.<commit_after>#include "AnimationController.hpp"
#include "Skeleton.hpp"
#include "AnimationClip.hpp"
#include <Utility/Log.hpp>
#include "../Hymn.hpp"

using namespace Animation;

AnimationController::~AnimationController() {
    Clear();
}

void AnimationController::Save(const std::string& path) {
    // Open file.
    std::ofstream file(path, std::ios::binary);

    // Check if file is open, if not log and early return.
    if (!file.is_open()) {
        Log() << "Could not save animation controller file: " << path << "\n";
        file.close();
        return;
    }

    uint32_t numNodes = animationNodes.size();
    file.write(reinterpret_cast<char*>(&numNodes), sizeof(uint32_t));

    for (Node* node : animationNodes) {
        if (typeid(*node) == typeid(AnimationAction)) {
            NodeType nodetype = ACTION;
            file.write(reinterpret_cast<char*>(&nodetype), sizeof(NodeType));
        } else {
            NodeType nodetype = TRANSITION;
            file.write(reinterpret_cast<char*>(&nodetype), sizeof(NodeType));
        }

        node->Save(&file);
    }

    uint32_t numBools = boolMap.size();
    file.write(reinterpret_cast<char*>(&numBools), sizeof(uint32_t));
    for (BoolItem* b : boolMap)
        file.write(reinterpret_cast<char*>(b), sizeof(BoolItem));

    uint32_t numFloats = floatMap.size();
    file.write(reinterpret_cast<char*>(&numFloats), sizeof(uint32_t));
    for (FloatItem* f : floatMap)
        file.write(reinterpret_cast<char*>(f), sizeof(FloatItem));

    // Close file.
    file.close();
}

void AnimationController::Load(const std::string& name) {
    std::size_t pos = name.find_last_of('/');
    this->name = name.substr(pos + 1);
    path = name.substr(0, pos + 1);

    // Open file.
    std::ifstream file(Hymn().GetPath() + "/" + name + ".asset", std::ios::binary);

    // Check if file is open, if not log and early return.
    if (!file.is_open()) {
        Log() << "Could not load animation controller file: " << Hymn().GetPath() + "/" + name + ".asset" << "\n";
        file.close();
        return;
    }

    // Clear if anything is loaded.
    Clear();

    uint32_t numNodes = 0;
    file.read(reinterpret_cast<char*>(&numNodes), sizeof(uint32_t));
    for (unsigned int i = 0; i < numNodes; ++i)  {
        NodeType nodeType;
        file.read(reinterpret_cast<char*>(&nodeType), sizeof(NodeType));
        if (nodeType == ACTION) {
            AnimationAction* node = new AnimationAction;
            node->Load(&file);
            animationNodes.push_back(node);
        } else {
            AnimationTransition* node = new AnimationTransition;
            node->Load(&file);
            animationNodes.push_back(node);
        }
    }

    uint32_t numBools = boolMap.size();
    file.read(reinterpret_cast<char*>(&numBools), sizeof(uint32_t));
    boolMap.resize(numBools);
    for (auto i = 0; i < numBools; ++i)
        file.read(reinterpret_cast<char*>(boolMap[i]), sizeof(BoolItem));

    uint32_t numFloats = floatMap.size();
    file.read(reinterpret_cast<char*>(&numFloats), sizeof(uint32_t));
    floatMap.resize(numFloats);
    for (auto i = 0; i < numFloats; ++i)
        file.read(reinterpret_cast<char*>(floatMap[i]), sizeof(FloatItem));

    // Close file.
    file.close();
}

void AnimationController::Clear() {
    for (Node* node : animationNodes)
        delete node;
    animationNodes.clear();

    for (BoolItem* b : boolMap)
        delete b;
    boolMap.clear();

    for (FloatItem* f : floatMap)
        delete f;
    floatMap.clear();
}
<|endoftext|>


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

#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <vector>

#include "client/linux/crash_generation/crash_generation_server.h"
#include "client/linux/crash_generation/client_info.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include "common/linux/safe_readlink.h"

static const char kCommandQuit = 'x';

namespace google_breakpad {

CrashGenerationServer::CrashGenerationServer(
  const int listen_fd,
  OnClientDumpRequestCallback dump_callback,
  void* dump_context,
  OnClientExitingCallback exit_callback,
  void* exit_context,
  bool generate_dumps,
  const string* dump_path) :
    server_fd_(listen_fd),
    dump_callback_(dump_callback),
    dump_context_(dump_context),
    exit_callback_(exit_callback),
    exit_context_(exit_context),
    generate_dumps_(generate_dumps),
    started_(false)
{
  if (dump_path)
    dump_dir_ = *dump_path;
  else
    dump_dir_ = "/tmp";
}

CrashGenerationServer::~CrashGenerationServer()
{
  if (started_)
    Stop();
}

bool
CrashGenerationServer::Start()
{
  if (started_ || 0 > server_fd_)
    return false;

  int control_pipe[2];
  if (pipe(control_pipe))
    return false;

  if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
    return false;
  if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
    return false;

  if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
    return false;

  control_pipe_in_ = control_pipe[0];
  control_pipe_out_ = control_pipe[1];

  if (pthread_create(&thread_, NULL,
                     ThreadMain, reinterpret_cast<void*>(this)))
    return false;

  started_ = true;
  return true;
}

void
CrashGenerationServer::Stop()
{
  assert(pthread_self() != thread_);

  if (!started_)
    return;

  HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));

  void* dummy;
  pthread_join(thread_, &dummy);

  started_ = false;
}

//static
bool
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
{
  int fds[2];

  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
    return false;

  static const int on = 1;
  // Enable passcred on the server end of the socket
  if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
    return false;

  if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
    return false;
  if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
    return false;

  *client_fd = fds[0];
  *server_fd = fds[1];
  return true;
}

// The following methods/functions execute on the server thread

void
CrashGenerationServer::Run()
{
  struct pollfd pollfds[2];
  memset(&pollfds, 0, sizeof(pollfds));

  pollfds[0].fd = server_fd_;
  pollfds[0].events = POLLIN;

  pollfds[1].fd = control_pipe_in_;
  pollfds[1].events = POLLIN;

  while (true) {
    // infinite timeout
    int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
    if (-1 == nevents) {
      if (EINTR == errno) {
        continue;
      } else {
        return;
      }
    }

    if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
      return;

    if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
      return;
  }
}

bool
CrashGenerationServer::ClientEvent(short revents)
{
  if (POLLHUP & revents)
    return false;
  assert(POLLIN & revents);

  // A process has crashed and has signaled us by writing a datagram
  // to the death signal socket. The datagram contains the crash context needed
  // for writing the minidump as well as a file descriptor and a credentials
  // block so that they can't lie about their pid.

  // The length of the control message:
  static const unsigned kControlMsgSize =
      CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
  // The length of the regular payload:
  static const unsigned kCrashContextSize =
      sizeof(google_breakpad::ExceptionHandler::CrashContext);

  struct msghdr msg = {0};
  struct iovec iov[1];
  char crash_context[kCrashContextSize];
  char control[kControlMsgSize];
  const ssize_t expected_msg_size = sizeof(crash_context);

  iov[0].iov_base = crash_context;
  iov[0].iov_len = sizeof(crash_context);
  msg.msg_iov = iov;
  msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
  msg.msg_control = control;
  msg.msg_controllen = kControlMsgSize;

  const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
  if (msg_size != expected_msg_size)
    return true;

  if (msg.msg_controllen != kControlMsgSize ||
      msg.msg_flags & ~MSG_TRUNC)
    return true;

  // Walk the control payload and extract the file descriptor and validated pid.
  pid_t crashing_pid = -1;
  int signal_fd = -1;
  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
       hdr = CMSG_NXTHDR(&msg, hdr)) {
    if (hdr->cmsg_level != SOL_SOCKET)
      continue;
    if (hdr->cmsg_type == SCM_RIGHTS) {
      const unsigned len = hdr->cmsg_len -
          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
      assert(len % sizeof(int) == 0u);
      const unsigned num_fds = len / sizeof(int);
      if (num_fds > 1 || num_fds == 0) {
        // A nasty process could try and send us too many descriptors and
        // force a leak.
        for (unsigned i = 0; i < num_fds; ++i)
          close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
        return true;
      } else {
        signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
      }
    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
      const struct ucred *cred =
          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
      crashing_pid = cred->pid;
    }
  }

  if (crashing_pid == -1 || signal_fd == -1) {
    if (signal_fd)
      close(signal_fd);
    return true;
  }

  string minidump_filename;
  if (!MakeMinidumpFilename(minidump_filename))
    return true;

  if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
                                      crashing_pid, crash_context,
                                      kCrashContextSize)) {
    close(signal_fd);
    return true;
  }

  if (dump_callback_) {
    ClientInfo info(crashing_pid, this);

    dump_callback_(dump_context_, &info, &minidump_filename);
  }

  // Send the done signal to the process: it can exit now.
  // (Closing this will make the child's sys_read unblock and return 0.)
  close(signal_fd);

  return true;
}

bool
CrashGenerationServer::ControlEvent(short revents)
{
  if (POLLHUP & revents)
    return false;
  assert(POLLIN & revents);

  char command;
  if (read(control_pipe_in_, &command, 1))
    return false;

  switch (command) {
  case kCommandQuit:
    return false;
  default:
    assert(0);
  }

  return true;
}

bool
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
{
  GUID guid;
  char guidString[kGUIDStringLength+1];

  if (!(CreateGUID(&guid)
        && GUIDToString(&guid, guidString, sizeof(guidString))))
    return false;

  char path[PATH_MAX];
  snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);

  outFilename = path;
  return true;
}

// static
void*
CrashGenerationServer::ThreadMain(void *arg)
{
  reinterpret_cast<CrashGenerationServer*>(arg)->Run();
  return NULL;
}

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

#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <vector>

#include "client/linux/crash_generation/crash_generation_server.h"
#include "client/linux/crash_generation/client_info.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include "common/linux/safe_readlink.h"

static const char kCommandQuit = 'x';

namespace google_breakpad {

CrashGenerationServer::CrashGenerationServer(
  const int listen_fd,
  OnClientDumpRequestCallback dump_callback,
  void* dump_context,
  OnClientExitingCallback exit_callback,
  void* exit_context,
  bool generate_dumps,
  const string* dump_path) :
    server_fd_(listen_fd),
    dump_callback_(dump_callback),
    dump_context_(dump_context),
    exit_callback_(exit_callback),
    exit_context_(exit_context),
    generate_dumps_(generate_dumps),
    started_(false)
{
  if (dump_path)
    dump_dir_ = *dump_path;
  else
    dump_dir_ = "/tmp";
}

CrashGenerationServer::~CrashGenerationServer()
{
  if (started_)
    Stop();
}

bool
CrashGenerationServer::Start()
{
  if (started_ || 0 > server_fd_)
    return false;

  int control_pipe[2];
  if (pipe(control_pipe))
    return false;

  if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
    return false;
  if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
    return false;

  if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
    return false;

  control_pipe_in_ = control_pipe[0];
  control_pipe_out_ = control_pipe[1];

  if (pthread_create(&thread_, NULL,
                     ThreadMain, reinterpret_cast<void*>(this)))
    return false;

  started_ = true;
  return true;
}

void
CrashGenerationServer::Stop()
{
  assert(pthread_self() != thread_);

  if (!started_)
    return;

  HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));

  void* dummy;
  pthread_join(thread_, &dummy);

  close(control_pipe_in_);
  close(control_pipe_out_);

  started_ = false;
}

//static
bool
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
{
  int fds[2];

  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
    return false;

  static const int on = 1;
  // Enable passcred on the server end of the socket
  if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
    return false;

  if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
    return false;
  if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
    return false;

  *client_fd = fds[0];
  *server_fd = fds[1];
  return true;
}

// The following methods/functions execute on the server thread

void
CrashGenerationServer::Run()
{
  struct pollfd pollfds[2];
  memset(&pollfds, 0, sizeof(pollfds));

  pollfds[0].fd = server_fd_;
  pollfds[0].events = POLLIN;

  pollfds[1].fd = control_pipe_in_;
  pollfds[1].events = POLLIN;

  while (true) {
    // infinite timeout
    int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
    if (-1 == nevents) {
      if (EINTR == errno) {
        continue;
      } else {
        return;
      }
    }

    if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
      return;

    if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
      return;
  }
}

bool
CrashGenerationServer::ClientEvent(short revents)
{
  if (POLLHUP & revents)
    return false;
  assert(POLLIN & revents);

  // A process has crashed and has signaled us by writing a datagram
  // to the death signal socket. The datagram contains the crash context needed
  // for writing the minidump as well as a file descriptor and a credentials
  // block so that they can't lie about their pid.

  // The length of the control message:
  static const unsigned kControlMsgSize =
      CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
  // The length of the regular payload:
  static const unsigned kCrashContextSize =
      sizeof(google_breakpad::ExceptionHandler::CrashContext);

  struct msghdr msg = {0};
  struct iovec iov[1];
  char crash_context[kCrashContextSize];
  char control[kControlMsgSize];
  const ssize_t expected_msg_size = sizeof(crash_context);

  iov[0].iov_base = crash_context;
  iov[0].iov_len = sizeof(crash_context);
  msg.msg_iov = iov;
  msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
  msg.msg_control = control;
  msg.msg_controllen = kControlMsgSize;

  const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
  if (msg_size != expected_msg_size)
    return true;

  if (msg.msg_controllen != kControlMsgSize ||
      msg.msg_flags & ~MSG_TRUNC)
    return true;

  // Walk the control payload and extract the file descriptor and validated pid.
  pid_t crashing_pid = -1;
  int signal_fd = -1;
  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
       hdr = CMSG_NXTHDR(&msg, hdr)) {
    if (hdr->cmsg_level != SOL_SOCKET)
      continue;
    if (hdr->cmsg_type == SCM_RIGHTS) {
      const unsigned len = hdr->cmsg_len -
          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
      assert(len % sizeof(int) == 0u);
      const unsigned num_fds = len / sizeof(int);
      if (num_fds > 1 || num_fds == 0) {
        // A nasty process could try and send us too many descriptors and
        // force a leak.
        for (unsigned i = 0; i < num_fds; ++i)
          close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
        return true;
      } else {
        signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
      }
    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
      const struct ucred *cred =
          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
      crashing_pid = cred->pid;
    }
  }

  if (crashing_pid == -1 || signal_fd == -1) {
    if (signal_fd)
      close(signal_fd);
    return true;
  }

  string minidump_filename;
  if (!MakeMinidumpFilename(minidump_filename))
    return true;

  if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
                                      crashing_pid, crash_context,
                                      kCrashContextSize)) {
    close(signal_fd);
    return true;
  }

  if (dump_callback_) {
    ClientInfo info(crashing_pid, this);

    dump_callback_(dump_context_, &info, &minidump_filename);
  }

  // Send the done signal to the process: it can exit now.
  // (Closing this will make the child's sys_read unblock and return 0.)
  close(signal_fd);

  return true;
}

bool
CrashGenerationServer::ControlEvent(short revents)
{
  if (POLLHUP & revents)
    return false;
  assert(POLLIN & revents);

  char command;
  if (read(control_pipe_in_, &command, 1))
    return false;

  switch (command) {
  case kCommandQuit:
    return false;
  default:
    assert(0);
  }

  return true;
}

bool
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
{
  GUID guid;
  char guidString[kGUIDStringLength+1];

  if (!(CreateGUID(&guid)
        && GUIDToString(&guid, guidString, sizeof(guidString))))
    return false;

  char path[PATH_MAX];
  snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);

  outFilename = path;
  return true;
}

// static
void*
CrashGenerationServer::ThreadMain(void *arg)
{
  reinterpret_cast<CrashGenerationServer*>(arg)->Run();
  return NULL;
}

}  // namespace google_breakpad
<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    vtkPolyDataToImageStencil.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPolyDataToImageStencil.h"
#include "vtkPolyData.h"
#include "vtkOBBTree.h"
#include "vtkObjectFactory.h"

#include <math.h>

vtkCxxRevisionMacro(vtkPolyDataToImageStencil, "1.3");
vtkStandardNewMacro(vtkPolyDataToImageStencil);

//----------------------------------------------------------------------------
vtkPolyDataToImageStencil::vtkPolyDataToImageStencil()
{
  this->OBBTree = NULL;
  this->Tolerance = 1e-3;
}

//----------------------------------------------------------------------------
vtkPolyDataToImageStencil::~vtkPolyDataToImageStencil()
{
  if (this->OBBTree)
    {
    this->OBBTree->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Input: " << this->GetInput() << "\n";
  os << indent << "Tolerance: " << this->Tolerance << "\n";
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::SetInput(vtkPolyData *input)
{
  this->vtkProcessObject::SetNthInput(0, input);
}

//----------------------------------------------------------------------------
vtkPolyData *vtkPolyDataToImageStencil::GetInput()
{
  if (this->NumberOfInputs < 1)
    {
    return NULL;
    }

  return (vtkPolyData *)(this->Inputs[0]);
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::ExecuteData(vtkDataObject *out)
{
  // need to build the OBB tree
  vtkPolyData *polydata = this->GetInput();
  if (this->OBBTree == NULL)
    {
    this->OBBTree = vtkOBBTree::New();
    }
  this->OBBTree->SetDataSet(polydata);
  this->OBBTree->SetTolerance(this->Tolerance);
  this->OBBTree->BuildLocator();

  // do superclass stuff
  this->vtkImageStencilSource::ExecuteData(out);
}

//----------------------------------------------------------------------------
static inline
void vtkAddEntryToList(int *&clist, int &clistlen, int &clistmaxlen, int r)
{
  if (clistlen >= clistmaxlen)
    { // need to allocate more space
    clistmaxlen *= 2;
    int *newclist = new int[clistmaxlen];
    for (int k = 0; k < clistlen; k++)
      {
      newclist[k] = clist[k];
      }
    delete [] clist;
    clist = newclist;
    }

  if (clistlen > 0 && r <= clist[clistlen-1])
    { // chop out zero-length extents
    clistlen--;
    }
  else
    {
    clist[clistlen++] = r;
    }
}

//----------------------------------------------------------------------------
static
void vtkTurnPointsIntoList(vtkPoints *points, int *&clist, int &clistlen,
                           int extent[6], float origin[3], float spacing[3],
                           int dim)
{
  int clistmaxlen = 2;
  clistlen = 0;
  clist = new int[clistmaxlen];

  int npoints = points->GetNumberOfPoints();
  for (int idP = 0; idP < npoints; idP++)
    {
    float *point = points->GetPoint(idP);
    int r = (int)ceil((point[dim] - origin[dim])/spacing[dim]);
    if (r < extent[2*dim])
      {
      r = extent[2*dim];
      }
    if (r > extent[2*dim+1])
      {
      break;
      }
    vtkAddEntryToList(clist, clistlen, clistmaxlen, r);
    }
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::ThreadedExecute(vtkImageStencilData *data,
                                                int extent[6],
                                                int vtkNotUsed(id))
{
  float *spacing = data->GetSpacing();
  float *origin = data->GetOrigin();

  vtkOBBTree *tree = this->OBBTree;
  vtkPoints *points = vtkPoints::New();

  float p0[3],p1[3];

  p1[0] = p0[0] = extent[0]*spacing[0] + origin[0];
  p1[0] = p0[1] = extent[2]*spacing[1] + origin[1];
  p0[2] = extent[4]*spacing[2] + origin[2];
  p1[2] = extent[5]*spacing[2] + origin[2];

  int zstate = tree->InsideOrOutside(p0);
  if (zstate == 0)
    {
    zstate = -1;
    }
  int *zlist = 0;
  int zlistlen = 0;
  int zlistidx = 0;
  if (extent[4] != extent[5])
    {
    tree->IntersectWithLine(p0, p1, points, 0);
    vtkTurnPointsIntoList(points, zlist, zlistlen,
                          extent, origin, spacing, 2);
    }

  for (int idZ = extent[4]; idZ <= extent[5]; idZ++)
    {
    if (zlistidx < zlistlen && idZ >= zlist[zlistidx])
      {
      zstate = -zstate;
      zlistidx++;
      }

    p1[0] = p0[0] = extent[0]*spacing[0] + origin[0];
    p0[1] = extent[2]*spacing[1] + origin[1];
    p1[1] = extent[3]*spacing[1] + origin[1];
    p1[2] = p0[2] = idZ*spacing[2] + origin[2];

    int ystate = zstate;
    int *ylist = 0;
    int ylistlen = 0;
    int ylistidx = 0;
    if (extent[2] != extent[3])
      {
      tree->IntersectWithLine(p0, p1, points, 0);
      vtkTurnPointsIntoList(points, ylist, ylistlen,
                            extent, origin, spacing, 1);
      }

    for (int idY = extent[2]; idY <= extent[3]; idY++)
      {
      if (ylistidx < ylistlen && idY >= ylist[ylistidx])
        {
        ystate = -ystate;
        ylistidx++;
        }

      p0[1] = p1[1] = idY*spacing[1] + origin[1];
      p0[2] = p1[2] = idZ*spacing[2] + origin[2];
      p0[0] = extent[0]*spacing[0] + origin[0];
      p1[0] = extent[1]*spacing[0] + origin[0];

      int xstate = ystate;
      int *xlist = 0;
      int xlistlen = 0;
      int xlistidx = 0;
      tree->IntersectWithLine(p0, p1, points, 0);
      vtkTurnPointsIntoList(points, xlist, xlistlen,
                            extent, origin, spacing, 0);

      // now turn 'xlist' into sub-extents:
      int r1 = extent[0];
      int r2 = extent[1];
      for (xlistidx = 0; xlistidx < xlistlen; xlistidx++)
        {
        xstate = -xstate;
        if (xstate < 0)
          { // sub extent starts
          r1 = xlist[xlistidx];
          }
        else
          { // sub extent ends
          r2 = xlist[xlistidx] - 1;
          data->InsertNextExtent(r1, r2, idY, idZ);
          }
        }
      if (xstate < 0)
        { // if inside at end, cap off the sub extent
        data->InsertNextExtent(r1, extent[1], idY, idZ);
        }

      if (xlist)
        {
        delete [] xlist;
        }

      } // for idY

    if (ylist)
      {
      delete [] ylist;
      }

    } // for idZ

  if (zlist)
    {
    delete [] zlist;
    }
  points->Delete();
}
<commit_msg>ERR: fix bug (thx to David Netherway), add progress reporting<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPolyDataToImageStencil.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 1993-2002 Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPolyDataToImageStencil.h"
#include "vtkPolyData.h"
#include "vtkOBBTree.h"
#include "vtkObjectFactory.h"

#include <math.h>

vtkCxxRevisionMacro(vtkPolyDataToImageStencil, "1.4");
vtkStandardNewMacro(vtkPolyDataToImageStencil);

//----------------------------------------------------------------------------
vtkPolyDataToImageStencil::vtkPolyDataToImageStencil()
{
  this->OBBTree = NULL;
  this->Tolerance = 1e-3;
}

//----------------------------------------------------------------------------
vtkPolyDataToImageStencil::~vtkPolyDataToImageStencil()
{
  if (this->OBBTree)
    {
    this->OBBTree->Delete();
    }
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Input: " << this->GetInput() << "\n";
  os << indent << "Tolerance: " << this->Tolerance << "\n";
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::SetInput(vtkPolyData *input)
{
  this->vtkProcessObject::SetNthInput(0, input);
}

//----------------------------------------------------------------------------
vtkPolyData *vtkPolyDataToImageStencil::GetInput()
{
  if (this->NumberOfInputs < 1)
    {
    return NULL;
    }

  return (vtkPolyData *)(this->Inputs[0]);
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::ExecuteData(vtkDataObject *out)
{
  // need to build the OBB tree
  vtkPolyData *polydata = this->GetInput();
  if (this->OBBTree == NULL)
    {
    this->OBBTree = vtkOBBTree::New();
    }
  this->OBBTree->SetDataSet(polydata);
  this->OBBTree->SetTolerance(this->Tolerance);
  this->OBBTree->BuildLocator();

  // do superclass stuff
  this->vtkImageStencilSource::ExecuteData(out);
}

//----------------------------------------------------------------------------
static inline
void vtkAddEntryToList(int *&clist, int &clistlen, int &clistmaxlen, int r)
{
  if (clistlen >= clistmaxlen)
    { // need to allocate more space
    clistmaxlen *= 2;
    int *newclist = new int[clistmaxlen];
    for (int k = 0; k < clistlen; k++)
      {
      newclist[k] = clist[k];
      }
    delete [] clist;
    clist = newclist;
    }

  if (clistlen > 0 && r <= clist[clistlen-1])
    { // chop out zero-length extents
    clistlen--;
    }
  else
    {
    clist[clistlen++] = r;
    }
}

//----------------------------------------------------------------------------
static
void vtkTurnPointsIntoList(vtkPoints *points, int *&clist, int &clistlen,
                           int extent[6], float origin[3], float spacing[3],
                           int dim)
{
  int clistmaxlen = 2;
  clistlen = 0;
  clist = new int[clistmaxlen];

  int npoints = points->GetNumberOfPoints();
  for (int idP = 0; idP < npoints; idP++)
    {
    float *point = points->GetPoint(idP);
    int r = (int)ceil((point[dim] - origin[dim])/spacing[dim]);
    if (r < extent[2*dim])
      {
      r = extent[2*dim];
      }
    if (r > extent[2*dim+1])
      {
      break;
      }
    vtkAddEntryToList(clist, clistlen, clistmaxlen, r);
    }
}

//----------------------------------------------------------------------------
void vtkPolyDataToImageStencil::ThreadedExecute(vtkImageStencilData *data,
                                                int extent[6],
                                                int id)
{
  // for keeping track of progress
  unsigned long count = 0;
  unsigned long target = (unsigned long)
    ((extent[5] - extent[4] + 1)*(extent[3] - extent[2] + 1)/50.0);
  target++;

  float *spacing = data->GetSpacing();
  float *origin = data->GetOrigin();

  vtkOBBTree *tree = this->OBBTree;
  vtkPoints *points = vtkPoints::New();

  float p0[3],p1[3];

  p1[0] = p0[0] = extent[0]*spacing[0] + origin[0];
  p1[1] = p0[1] = extent[2]*spacing[1] + origin[1];
  p0[2] = extent[4]*spacing[2] + origin[2];
  p1[2] = extent[5]*spacing[2] + origin[2];

  int zstate = tree->InsideOrOutside(p0);
  if (zstate == 0)
    {
    zstate = -1;
    }
  int *zlist = 0;
  int zlistlen = 0;
  int zlistidx = 0;
  if (extent[4] != extent[5])
    {
    tree->IntersectWithLine(p0, p1, points, 0);
    vtkTurnPointsIntoList(points, zlist, zlistlen,
                          extent, origin, spacing, 2);
    }

  for (int idZ = extent[4]; idZ <= extent[5]; idZ++)
    {
    if (zlistidx < zlistlen && idZ >= zlist[zlistidx])
      {
      zstate = -zstate;
      zlistidx++;
      }

    p1[0] = p0[0] = extent[0]*spacing[0] + origin[0];
    p0[1] = extent[2]*spacing[1] + origin[1];
    p1[1] = extent[3]*spacing[1] + origin[1];
    p1[2] = p0[2] = idZ*spacing[2] + origin[2];

    int ystate = zstate;
    int *ylist = 0;
    int ylistlen = 0;
    int ylistidx = 0;
    if (extent[2] != extent[3])
      {
      tree->IntersectWithLine(p0, p1, points, 0);
      vtkTurnPointsIntoList(points, ylist, ylistlen,
                            extent, origin, spacing, 1);
      }

    for (int idY = extent[2]; idY <= extent[3]; idY++)
      {
      if (ylistidx < ylistlen && idY >= ylist[ylistidx])
        {
        ystate = -ystate;
        ylistidx++;
        }

      if (id == 0)
        { // update progress if we're the main thread
        if (count%target == 0)
          {
          this->UpdateProgress(count/(50.0*target));
          }
        count++;
        }

      p0[1] = p1[1] = idY*spacing[1] + origin[1];
      p0[2] = p1[2] = idZ*spacing[2] + origin[2];
      p0[0] = extent[0]*spacing[0] + origin[0];
      p1[0] = extent[1]*spacing[0] + origin[0];

      int xstate = ystate;
      int *xlist = 0;
      int xlistlen = 0;
      int xlistidx = 0;
      tree->IntersectWithLine(p0, p1, points, 0);
      vtkTurnPointsIntoList(points, xlist, xlistlen,
                            extent, origin, spacing, 0);

      // now turn 'xlist' into sub-extents:
      int r1 = extent[0];
      int r2 = extent[1];
      for (xlistidx = 0; xlistidx < xlistlen; xlistidx++)
        {
        xstate = -xstate;
        if (xstate < 0)
          { // sub extent starts
          r1 = xlist[xlistidx];
          }
        else
          { // sub extent ends
          r2 = xlist[xlistidx] - 1;
          data->InsertNextExtent(r1, r2, idY, idZ);
          }
        }
      if (xstate < 0)
        { // if inside at end, cap off the sub extent
        data->InsertNextExtent(r1, extent[1], idY, idZ);
        }

      if (xlist)
        {
        delete [] xlist;
        }

      } // for idY

    if (ylist)
      {
      delete [] ylist;
      }

    } // for idZ

  if (zlist)
    {
    delete [] zlist;
    }
  points->Delete();
}
<|endoftext|>


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

#include "boost/python.hpp"

#include "IECorePython/ParameterBinding.h"
#include "IECore/NumericParameter.h"
#include "IECore/CompoundObject.h"

#include "IECorePython/NumericParameterBinding.h"
#include "IECorePython/Wrapper.h"
#include "IECorePython/RunTimeTypedBinding.h"

using namespace std;
using namespace boost;
using namespace boost::python;
using namespace IECore;

namespace IECorePython
{

template<typename T>
class NumericParameterWrap : public NumericParameter<T>, public Wrapper<NumericParameter<T> >
{
	public :

		NumericParameterWrap( PyObject *self, const std::string &n, const std::string &d, T v = T(), T minValue = Imath::limits<T>::min(),
			T maxValue = Imath::limits<T>::max(), const object &p = boost::python::tuple(), bool po = false, CompoundObjectPtr ud = 0 )
			:	NumericParameter<T>( n, d, v, minValue, maxValue, parameterPresets<typename NumericParameter<T>::PresetsContainer>( p ), po, ud ), Wrapper<NumericParameter<T> >( self, this ) {};

		IECOREPYTHON_PARAMETERWRAPPERFNS( NumericParameter<T> );
		IE_CORE_DECLAREMEMBERPTR( NumericParameterWrap<T> );
};

template<typename T>
static void bindNumericParameter()
{
	using boost::python::arg;

	RunTimeTypedClass<NumericParameter<T>, NumericParameterWrap<T> >()
		.def(
			init<const std::string &, const std::string &, boost::python::optional< T, T, T, const object &, bool, CompoundObjectPtr> >
			(
				(
					arg( "name" ),
					arg( "description" ),
					arg( "defaultValue" ) = T(),
					arg( "minValue" ) = Imath::limits<T>::min(),
					arg( "maxValue" ) = Imath::limits<T>::max(),
					arg( "presets" ) = boost::python::tuple(),
					arg( "presetsOnly" ) = false,
					arg( "userData" ) = CompoundObject::Ptr( 0 )
				)
			)
		)
		.add_property( "numericDefaultValue", &NumericParameter<T>::numericDefaultValue  )
		.def( "getNumericValue", &NumericParameter<T>::getNumericValue  )
		.def( "setNumericValue", &NumericParameter<T>::setNumericValue  )
		.def( "getTypedValue", &NumericParameter<T>::getNumericValue )		// added for consistency
		.def( "setTypedValue", &NumericParameter<T>::setNumericValue )		// added for consistency
		.IECOREPYTHON_DEFPARAMETERWRAPPERFNS( NumericParameter<T> )
		.def( "hasMinValue", &NumericParameter<T>::hasMinValue )
		.def( "hasMaxValue", &NumericParameter<T>::hasMaxValue )
		.add_property( "minValue", &NumericParameter<T>::minValue )
		.add_property( "maxValue", &NumericParameter<T>::maxValue )
	;
}

void bindNumericParameter()
{
	bindNumericParameter<int>();
	bindNumericParameter<float>();
	bindNumericParameter<double>();
}

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

#include "boost/python.hpp"

#include "IECorePython/ParameterBinding.h"
#include "IECore/NumericParameter.h"
#include "IECore/CompoundObject.h"

#include "IECorePython/NumericParameterBinding.h"
#include "IECorePython/RunTimeTypedBinding.h"

using namespace std;
using namespace boost;
using namespace boost::python;
using namespace IECore;
using namespace IECorePython;

namespace
{

template<typename T>
class NumericParameterWrapper : public ParameterWrapper<NumericParameter<T> >
{
	public :

		NumericParameterWrapper(
			PyObject *self, const std::string &n, const std::string &d, T v = T(), T minValue = Imath::limits<T>::min(),
			T maxValue = Imath::limits<T>::max(), const object &p = boost::python::tuple(), bool po = false, CompoundObjectPtr ud = 0
		)
			: ParameterWrapper<NumericParameter<T> >( self, n, d, v, minValue, maxValue, parameterPresets<typename NumericParameter<T>::PresetsContainer>( p ), po, ud )
		{
		};

};

} //namespace

namespace IECorePython
{

template<typename T>
static void bindNumericParameter()
{
	using boost::python::arg;

	ParameterClass<NumericParameter<T>, NumericParameterWrapper<T> >()
		.def(
			init<const std::string &, const std::string &, boost::python::optional< T, T, T, const object &, bool, CompoundObjectPtr> >
			(
				(
					arg( "name" ),
					arg( "description" ),
					arg( "defaultValue" ) = T(),
					arg( "minValue" ) = Imath::limits<T>::min(),
					arg( "maxValue" ) = Imath::limits<T>::max(),
					arg( "presets" ) = boost::python::tuple(),
					arg( "presetsOnly" ) = false,
					arg( "userData" ) = CompoundObject::Ptr( 0 )
				)
			)
		)
		.add_property( "numericDefaultValue", &NumericParameter<T>::numericDefaultValue  )
		.def( "getNumericValue", &NumericParameter<T>::getNumericValue  )
		.def( "setNumericValue", &NumericParameter<T>::setNumericValue  )
		.def( "getTypedValue", &NumericParameter<T>::getNumericValue )		// added for consistency
		.def( "setTypedValue", &NumericParameter<T>::setNumericValue )		// added for consistency
		.def( "hasMinValue", &NumericParameter<T>::hasMinValue )
		.def( "hasMaxValue", &NumericParameter<T>::hasMaxValue )
		.add_property( "minValue", &NumericParameter<T>::minValue )
		.add_property( "maxValue", &NumericParameter<T>::maxValue )
	;
}

void bindNumericParameter()
{
	bindNumericParameter<int>();
	bindNumericParameter<float>();
	bindNumericParameter<double>();
}

} // namespace IECorePython
<|endoftext|>