<commit_before>/*
 * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
 * Applied Mathematics, Norway.
 *
 * Contact information: E-mail: tor.dokken@sintef.no
 * SINTEF ICT, Department of Applied Mathematics,
 * P.O. Box 124 Blindern,
 * 0314 Oslo, Norway.
 *
 * This file is part of GoTools.
 *
 * GoTools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * GoTools 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with GoTools. If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public
 * License, a covered work must retain the producer line in every data
 * file that is created or manipulated using GoTools.
 *
 * Other Usage
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the GoTools library without
 * disclosing the source code of your own applications.
 *
 * This file may be used in accordance with the terms contained in a
 * written agreement between you and SINTEF ICT.
 */

#define BOOST_TEST_MODULE compositemodel/offsetSurfaceSetTest
#include <boost/test/included/unit_test.hpp>

#include "GoTools/compositemodel/OffsetSurfaceUtils.h"
#include "GoTools/utils/errormacros.h"
#include "GoTools/geometry/Utils.h"
#include "GoTools/geometry/ObjectHeader.h"
#include "GoTools/geometry/BoundedSurface.h"
#include "GoTools/geometry/GoTools.h"

#include <fstream>
#include <math.h>
#include <vector>
#include <string>

using namespace Go;
using std::vector;
using std::string;
using std::ifstream;

// #define TEST_NEW_CASES

// #define TEST_UNSUPPORTED_CASES

// #define TEST_FAILING_CASES

#define TEST_WORKING_CASES

struct Config {
public:
    Config()
        : output_filename("tmp/testHermiteApprEvalSurf_result.g2")

    {

        const std::string data_basedir("../../gotools-private-data/compositemodel/Offset");

#ifdef GOTOOLS_TEST_PRIVATE_DATA

        // NEW CASES!!!
#ifdef TEST_NEW_CASES

#endif

        // CASES NOT SUPPORTED (EARLY EXIT WITH ERROR MESSAGE)!!!
#ifdef TEST_UNSUPPORTED_CASES

        // Degenerate patch (triangle): Ok w/ offset=1e-02,eps=1e-03. Using 0.01*epsgeo as curvature_tol.
        // Contains kink curve which is not an iso curve. Exits with failure.
        filenames.push_back(data_basedir+"/fanta_ro2_sub2b.g2");
        offset.push_back(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-03);//6;

        // Tricky case with a degenerate surface. The degenerate point is not well defined with the
        // normal varying as the evaluation approaches from different iso lines. Will require extensive
        // smoothing. Currently not handled using SplineSurface (equation system too large). We need
        // LRSplineSurface (and even with that the task may be to tricky). Exits with failure.
        filenames.push_back(data_basedir+"/fanta_ro2_sub.g2");
        offset.push_back(0.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;
#endif

        // FAILING CASES!!!
#ifdef TEST_FAILING_CASES
        // Added 2017-10-09. Crash 3. Tricky case with a degenerate underlying surface, with a trimmed
        // boundary meeting in a degenerate corner (parallell tangents).
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170929-115934.549.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04);
#endif

        // WORKING CASES!!!
#ifdef TEST_WORKING_CASES

        // Added 2017-10-09. Crash 2.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf_OffsetTest_20170929-110347.373_406_434.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04); // Initially given epsgeo = 1.0e-03, conflicts with topology (too many twin edges).

        // Added 2017-10-09. Crash 1.
        filenames.push_back(data_basedir+"/TopSolid/Source-TopSolid_SplineSurf_OffsetTest_20170929-103855.922_949.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04); // Initially given epsgeo = 1.0e-03, conflicts with topology (too many twin edges).

        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170623-173106.658.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-173106.544.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-173916.090.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-175900.205.g2");
//      filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170627-130342.267.g2"); The same set.
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // 2 bilinear quadrats with z = 0.018.
        // Tricky case which required us to to define a transition zone.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_Surf__20170313-174324.189_221.g2");
        offset.push_back(0.1);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // 4 planes meeting in kinks along linear segments.
        // Tricky case which required us to to define a transition zone.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170504-1332_kinks.g2");
        offset.push_back(3.0e-03);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // A single surface with a bump in the interior. Use a negative offset value to create a self
        // intersection in the bump. Easy when using a positive offset dist, tricky when using a negative
        // offset dist. With a large offset value (< -3.0e-03) the self intersection is global.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_bump.g2");
        offset.push_back(-3.0e-03);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // Self-intersections for offset dist of appr 0.3 and larger.
        filenames.push_back(data_basedir+"/test_bezier.g2");
        offset.push_back(0.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Self-int: offset=1e-02,eps=1e-03.
        // Success with removing self intersections using smoothing: offset=1e-03,eps=1e-04.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_Surf__20170404-123801.816.g2");
        offset.push_back(0.001);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Two orthogonal planes joined by a trimmed cylinder segment (w/ radius of curvature -1.38843).
        // 201706: Success after setting the twist vector to zero and reducing precond omega from 0.1 to 0.01.
        filenames.push_back(data_basedir+"/yta4.g2");
        offset.push_back(-1.5);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Illegal bounded surface, the trim loop is CW! The offset works but the offset surface is flipped.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170623-173916.162.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);
#endif

#endif

        GoTools::init();

    }

public:
    vector<string> filenames;
    vector<double> offset;
    vector<double> epsgeo;
    ObjectHeader header;
    //const std::string input_filename;
    const std::string output_filename;
    // const std::string input_filename("tmp/testHermiteApprEvalSurf_input.g2");
    // const std::string output_filename("tmp/testHermiteApprEvalSurf_result.g2");

};

BOOST_FIXTURE_TEST_CASE(offsetSurfaceSet, Config)
{
    int num_success = 0;
    int num_failures = 0;
    int num_exceptions = 0;
    for (size_t ki = 0; ki < filenames.size(); ++ki)
    {
        // Read input arguments
        std::ifstream infile(filenames[ki].c_str());
        BOOST_CHECK_MESSAGE(infile.good(), "Input file not found or file corrupt");

        vector<shared_ptr<ParamSurface> > sfs;
        while (!infile.eof())
        {
            infile >> header;
            shared_ptr<ParamSurface> sf;
            if (header.classType() == Class_SplineSurface)
            {
                sf = shared_ptr<ParamSurface>(new SplineSurface());
            }
            else if (header.classType() == Class_BoundedSurface)
            {
                sf = shared_ptr<ParamSurface>(new BoundedSurface());
            }
            else
            {
                BOOST_ERROR("Input surface type not yet supported: " << header.classType());
                continue;
            }

            sf->read(infile);

            sfs.push_back(sf);

            Utils::eatwhite(infile);
        }

        shared_ptr<SplineSurface> offset_sf;
        OffsetSurfaceStatus status;
        try
        {
            status = OffsetSurfaceUtils::offsetSurfaceSet(sfs, offset[ki], epsgeo[ki], offset_sf);
        }
        catch (...)
        {
            std::cout << "offsetSurfaceSet(): Caught exception for filename " << filenames[ki] << std::endl;
            BOOST_ERROR("Exception caught");
            ++num_exceptions;
            continue;
        }

        BOOST_CHECK_MESSAGE(status == OFFSET_OK,
                            "offsetSurfaceSet(): Failure! Returned status (!= 0): " << status);
        if (status == OFFSET_OK)
        {
            ++num_success;
            if (offset_sf.get())
            {
                std::ofstream fileout(output_filename);
                offset_sf->writeStandardHeader(fileout);
                offset_sf->write(fileout);
            }
        }
        else
        {
            ++num_failures;
        }
    }

    BOOST_TEST_MESSAGE("\nnum files: " << filenames.size() << ", num success: " << num_success << ", num failures: " <<
                  num_failures << ", num_exceptions: " << num_exceptions);

}
<commit_msg>Removed old code.<commit_after>/*
 * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
 * Applied Mathematics, Norway.
 *
 * Contact information: E-mail: tor.dokken@sintef.no
 * SINTEF ICT, Department of Applied Mathematics,
 * P.O. Box 124 Blindern,
 * 0314 Oslo, Norway.
 *
 * This file is part of GoTools.
 *
 * GoTools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * GoTools 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with GoTools. If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public
 * License, a covered work must retain the producer line in every data
 * file that is created or manipulated using GoTools.
 *
 * Other Usage
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the GoTools library without
 * disclosing the source code of your own applications.
 *
 * This file may be used in accordance with the terms contained in a
 * written agreement between you and SINTEF ICT.
 */

#define BOOST_TEST_MODULE compositemodel/offsetSurfaceSetTest
#include <boost/test/included/unit_test.hpp>

#include "GoTools/compositemodel/OffsetSurfaceUtils.h"
#include "GoTools/utils/errormacros.h"
#include "GoTools/geometry/Utils.h"
#include "GoTools/geometry/ObjectHeader.h"
#include "GoTools/geometry/BoundedSurface.h"
#include "GoTools/geometry/GoTools.h"

#include <fstream>
#include <math.h>
#include <vector>
#include <string>

using namespace Go;
using std::vector;
using std::string;
using std::ifstream;

// #define TEST_NEW_CASES

// #define TEST_UNSUPPORTED_CASES

// #define TEST_FAILING_CASES

#define TEST_WORKING_CASES

struct Config {
public:
    Config()
        : output_filename("tmp/offsetSurfaceSetTest_result.g2")

    {

        const std::string data_basedir("../../gotools-private-data/compositemodel/Offset");

#ifdef GOTOOLS_TEST_PRIVATE_DATA

        // NEW CASES!!!
#ifdef TEST_NEW_CASES

#endif

        // CASES NOT SUPPORTED (EARLY EXIT WITH ERROR MESSAGE)!!!
#ifdef TEST_UNSUPPORTED_CASES

        // Degenerate patch (triangle): Ok w/ offset=1e-02,eps=1e-03. Using 0.01*epsgeo as curvature_tol.
        // Contains kink curve which is not an iso curve. Exits with failure.
        filenames.push_back(data_basedir+"/fanta_ro2_sub2b.g2");
        offset.push_back(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-03);//6;

        // Tricky case with a degenerate surface. The degenerate point is not well defined with the
        // normal varying as the evaluation approaches from different iso lines. Will require extensive
        // smoothing. Currently not handled using SplineSurface (equation system too large). We need
        // LRSplineSurface (and even with that the task may be to tricky). Exits with failure.
        filenames.push_back(data_basedir+"/fanta_ro2_sub.g2");
        offset.push_back(0.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;
#endif

        // FAILING CASES!!!
#ifdef TEST_FAILING_CASES
        // Added 2017-10-09. Crash 3. Tricky case with a degenerate underlying surface, with a trimmed
        // boundary meeting in a degenerate corner (parallell tangents).
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170929-115934.549.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04);
#endif

        // WORKING CASES!!!
#ifdef TEST_WORKING_CASES

        // Added 2017-10-09. Crash 2.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf_OffsetTest_20170929-110347.373_406_434.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04); // Initially given epsgeo = 1.0e-03, conflicts with topology (too many twin edges).

        // Added 2017-10-09. Crash 1.
        filenames.push_back(data_basedir+"/TopSolid/Source-TopSolid_SplineSurf_OffsetTest_20170929-103855.922_949.g2");
        offset.push_back(1.0e-03);
        epsgeo.push_back(1.0e-04); // Initially given epsgeo = 1.0e-03, conflicts with topology (too many twin edges).

        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170623-173106.658.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-173106.544.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-173916.090.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // Added 2017-06-23
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170623-175900.205.g2");
//      filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170627-130342.267.g2"); The same set.
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);

        // 2 bilinear quadrats with z = 0.018.
        // Tricky case which required us to to define a transition zone.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_Surf__20170313-174324.189_221.g2");
        offset.push_back(0.1);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // 4 planes meeting in kinks along linear segments.
        // Tricky case which required us to to define a transition zone.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_SplineSurf__20170504-1332_kinks.g2");
        offset.push_back(3.0e-03);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // A single surface with a bump in the interior. Use a negative offset value to create a self
        // intersection in the bump. Easy when using a positive offset dist, tricky when using a negative
        // offset dist. With a large offset value (< -3.0e-03) the self intersection is global.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_bump.g2");
        offset.push_back(-3.0e-03);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-05);//3);//6;

        // Self-intersections for offset dist of appr 0.3 and larger.
        filenames.push_back(data_basedir+"/test_bezier.g2");
        offset.push_back(0.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Self-int: offset=1e-02,eps=1e-03.
        // Success with removing self intersections using smoothing: offset=1e-03,eps=1e-04.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_Surf__20170404-123801.816.g2");
        offset.push_back(0.001);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Two orthogonal planes joined by a trimmed cylinder segment (w/ radius of curvature -1.38843).
        // 201706: Success after setting the twist vector to zero and reducing precond omega from 0.1 to 0.01.
        filenames.push_back(data_basedir+"/yta4.g2");
        offset.push_back(-1.5);//1.3);//(0.01);//0.1;//1.23; //0.2;
        epsgeo.push_back(1.0e-04);//3);//6;

        // Illegal bounded surface, the trim loop is CW! The offset works but the offset surface is flipped.
        filenames.push_back(data_basedir+"/TopSolid/TopSolid_BoundedSurf__20170623-173916.162.g2");
        offset.push_back(5.0e-03);
        epsgeo.push_back(1.0e-03);
#endif

#endif

        GoTools::init();

    }

public:
    vector<string> filenames;
    vector<double> offset;
    vector<double> epsgeo;
    ObjectHeader header;
    const std::string output_filename;

};

BOOST_FIXTURE_TEST_CASE(offsetSurfaceSet, Config)
{
    int num_success = 0;
    int num_failures = 0;
    int num_exceptions = 0;
    for (size_t ki = 0; ki < filenames.size(); ++ki)
    {
        // Read input arguments
        std::ifstream infile(filenames[ki].c_str());
        BOOST_CHECK_MESSAGE(infile.good(), "Input file not found or file corrupt");

        vector<shared_ptr<ParamSurface> > sfs;
        while (!infile.eof())
        {
            infile >> header;
            shared_ptr<ParamSurface> sf;
            if (header.classType() == Class_SplineSurface)
            {
                sf = shared_ptr<ParamSurface>(new SplineSurface());
            }
            else if (header.classType() == Class_BoundedSurface)
            {
                sf = shared_ptr<ParamSurface>(new BoundedSurface());
            }
            else
            {
                BOOST_ERROR("Input surface type not yet supported: " << header.classType());
                continue;
            }

            sf->read(infile);

            sfs.push_back(sf);

            Utils::eatwhite(infile);
        }

        shared_ptr<SplineSurface> offset_sf;
        OffsetSurfaceStatus status;
        try
        {
            status = OffsetSurfaceUtils::offsetSurfaceSet(sfs, offset[ki], epsgeo[ki], offset_sf);
        }
        catch (...)
        {
            std::cout << "offsetSurfaceSet(): Caught exception for filename " << filenames[ki] << std::endl;
            BOOST_ERROR("Exception caught");
            ++num_exceptions;
            continue;
        }

        BOOST_CHECK_MESSAGE(status == OFFSET_OK,
                            "offsetSurfaceSet(): Failure! Returned status (!= 0): " << status);
        if (status == OFFSET_OK)
        {
            ++num_success;
            if (offset_sf.get())
            {
                std::ofstream fileout(output_filename);
                offset_sf->writeStandardHeader(fileout);
                offset_sf->write(fileout);
            }
        }
        else
        {
            ++num_failures;
        }
    }

    BOOST_TEST_MESSAGE("\nnum files: " << filenames.size() << ", num success: " << num_success << ", num failures: " <<
                  num_failures << ", num_exceptions: " << num_exceptions);

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
 *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
 * Copyright (C) 2010, 2012 Google Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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 GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "core/rendering/RenderLayerModelObject.h"

#include "core/frame/LocalFrame.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderView.h"

namespace blink {

bool RenderLayerModelObject::s_wasFloating = false;

RenderLayerModelObject::RenderLayerModelObject(ContainerNode* node)
    : RenderObject(node)
{
}

RenderLayerModelObject::~RenderLayerModelObject()
{
    // Our layer should have been destroyed and cleared by now
    ASSERT(!hasLayer());
    ASSERT(!m_layer);
}

void RenderLayerModelObject::destroyLayer()
{
    setHasLayer(false);
    m_layer = nullptr;
}

void RenderLayerModelObject::createLayer(LayerType type)
{
    ASSERT(!m_layer);
    m_layer = adoptPtr(new RenderLayer(this, type));
    setHasLayer(true);
    m_layer->insertOnlyThisLayer();
}

bool RenderLayerModelObject::hasSelfPaintingLayer() const
{
    return m_layer && m_layer->isSelfPaintingLayer();
}

ScrollableArea* RenderLayerModelObject::scrollableArea() const
{
    return m_layer ? m_layer->scrollableArea() : 0;
}

void RenderLayerModelObject::willBeDestroyed()
{
    if (isPositioned()) {
        // Don't use this->view() because the document's renderView has been set to 0 during destruction.
        if (LocalFrame* frame = this->frame()) {
            if (FrameView* frameView = frame->view()) {
                if (style()->hasViewportConstrainedPosition())
                    frameView->removeViewportConstrainedObject(this);
            }
        }
    }

    RenderObject::willBeDestroyed();

    destroyLayer();
}

void RenderLayerModelObject::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
{
    s_wasFloating = isFloating();

    if (RenderStyle* oldStyle = style()) {
        if (parent() && diff.needsPaintInvalidationLayer()) {
            if (oldStyle->hasAutoClip() != newStyle.hasAutoClip()
                || oldStyle->clip() != newStyle.clip())
                layer()->clipper().clearClipRectsIncludingDescendants();
        }
    }

    RenderObject::styleWillChange(diff, newStyle);
}

void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
    bool hadTransform = hasTransform();

    RenderObject::styleDidChange(diff, oldStyle);
    updateFromStyle();

    LayerType type = layerTypeRequired();
    if (type != NoLayer) {
        if (!layer() && layerCreationAllowedForSubtree()) {
            if (s_wasFloating && isFloating())
                setChildNeedsLayout();
            createLayer(type);
            if (parent() && !needsLayout()) {
                // FIXME: This invalidation is overly broad. We should update to
                // do the correct invalidation at RenderStyle::diff time. crbug.com/349061
                layer()->renderer()->setShouldDoFullPaintInvalidation(true);
                // FIXME: We should call a specialized version of this function.
                layer()->updateLayerPositionsAfterLayout();
            }
        }
    } else if (layer() && layer()->parent()) {
        setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit.
        setHasReflection(false);
        layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
        if (s_wasFloating && isFloating())
            setChildNeedsLayout();
        if (hadTransform)
            setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
    }

    if (layer()) {
        // FIXME: Ideally we shouldn't need this setter but we can't easily infer an overflow-only layer
        // from the style.
        layer()->setLayerType(type);
        layer()->styleChanged(diff, oldStyle);
    }

    if (FrameView *frameView = view()->frameView()) {
        bool newStyleIsViewportConstained = style()->hasViewportConstrainedPosition();
        bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition();
        if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) {
            if (newStyleIsViewportConstained && layer())
                frameView->addViewportConstrainedObject(this);
            else
                frameView->removeViewportConstrainedObject(this);
        }
    }
}

void RenderLayerModelObject::addLayerHitTestRects(LayerHitTestRects& rects, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
{
    if (hasLayer()) {
        if (isRenderView()) {
            // RenderView is handled with a special fast-path, but it needs to know the current layer.
            RenderObject::addLayerHitTestRects(rects, layer(), LayoutPoint(), LayoutRect());
        } else {
            // Since a RenderObject never lives outside it's container RenderLayer, we can switch
            // to marking entire layers instead. This may sometimes mark more than necessary (when
            // a layer is made of disjoint objects) but in practice is a significant performance
            // savings.
            layer()->addLayerHitTestRects(rects);
        }
    } else {
        RenderObject::addLayerHitTestRects(rects, currentLayer, layerOffset, containerRect);
    }
}

InvalidationReason RenderLayerModelObject::invalidatePaintIfNeeded(const PaintInvalidationState& paintInvalidationState, const RenderLayerModelObject& newPaintInvalidationContainer)
{
    const LayoutRect oldPaintInvalidationRect = previousPaintInvalidationRect();
    const LayoutPoint oldPositionFromPaintInvalidationContainer = previousPositionFromPaintInvalidationContainer();
    setPreviousPaintInvalidationRect(boundsRectForPaintInvalidation(&newPaintInvalidationContainer, &paintInvalidationState));
    setPreviousPositionFromPaintInvalidationContainer(RenderLayer::positionFromPaintInvalidationContainer(this, &newPaintInvalidationContainer, &paintInvalidationState));

    // If we are set to do a full paint invalidation that means the RenderView will issue
    // paint invalidations. We can then skip issuing of paint invalidations for the child
    // renderers as they'll be covered by the RenderView.
    if (view()->doingFullPaintInvalidation())
        return InvalidationNone;

    return RenderObject::invalidatePaintIfNeeded(newPaintInvalidationContainer, oldPaintInvalidationRect, oldPositionFromPaintInvalidationContainer, paintInvalidationState);
}

void RenderLayerModelObject::invalidateTreeIfNeeded(const PaintInvalidationState& paintInvalidationState)
{
    // FIXME: SVG should probably also go through this unified paint invalidation system.
    ASSERT(!needsLayout());

    if (!shouldCheckForPaintInvalidation(paintInvalidationState))
        return;

    bool establishesNewPaintInvalidationContainer = isPaintInvalidationContainer();
    const RenderLayerModelObject& newPaintInvalidationContainer = *adjustCompositedContainerForSpecialAncestors(establishesNewPaintInvalidationContainer ? this : &paintInvalidationState.paintInvalidationContainer());
    ASSERT(&newPaintInvalidationContainer == containerForPaintInvalidation());

    InvalidationReason reason = invalidatePaintIfNeeded(paintInvalidationState, newPaintInvalidationContainer);

    PaintInvalidationState childTreeWalkState(paintInvalidationState, *this, newPaintInvalidationContainer);
    if (reason == InvalidationLocationChange || reason == InvalidationFull)
        childTreeWalkState.setForceCheckForPaintInvalidation();
    RenderObject::invalidateTreeIfNeeded(childTreeWalkState);
}

} // namespace blink

<commit_msg>Remove redundant invalidation in RenderLayerModelObject::styleDidChange()<commit_after>/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 *           (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com)
 *           (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com)
 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
 * Copyright (C) 2010, 2012 Google Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * 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 GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "config.h"
#include "core/rendering/RenderLayerModelObject.h"

#include "core/frame/LocalFrame.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/RenderView.h"

namespace blink {

bool RenderLayerModelObject::s_wasFloating = false;

RenderLayerModelObject::RenderLayerModelObject(ContainerNode* node)
    : RenderObject(node)
{
}

RenderLayerModelObject::~RenderLayerModelObject()
{
    // Our layer should have been destroyed and cleared by now
    ASSERT(!hasLayer());
    ASSERT(!m_layer);
}

void RenderLayerModelObject::destroyLayer()
{
    setHasLayer(false);
    m_layer = nullptr;
}

void RenderLayerModelObject::createLayer(LayerType type)
{
    ASSERT(!m_layer);
    m_layer = adoptPtr(new RenderLayer(this, type));
    setHasLayer(true);
    m_layer->insertOnlyThisLayer();
}

bool RenderLayerModelObject::hasSelfPaintingLayer() const
{
    return m_layer && m_layer->isSelfPaintingLayer();
}

ScrollableArea* RenderLayerModelObject::scrollableArea() const
{
    return m_layer ? m_layer->scrollableArea() : 0;
}

void RenderLayerModelObject::willBeDestroyed()
{
    if (isPositioned()) {
        // Don't use this->view() because the document's renderView has been set to 0 during destruction.
        if (LocalFrame* frame = this->frame()) {
            if (FrameView* frameView = frame->view()) {
                if (style()->hasViewportConstrainedPosition())
                    frameView->removeViewportConstrainedObject(this);
            }
        }
    }

    RenderObject::willBeDestroyed();

    destroyLayer();
}

void RenderLayerModelObject::styleWillChange(StyleDifference diff, const RenderStyle& newStyle)
{
    s_wasFloating = isFloating();

    if (RenderStyle* oldStyle = style()) {
        if (parent() && diff.needsPaintInvalidationLayer()) {
            if (oldStyle->hasAutoClip() != newStyle.hasAutoClip()
                || oldStyle->clip() != newStyle.clip())
                layer()->clipper().clearClipRectsIncludingDescendants();
        }
    }

    RenderObject::styleWillChange(diff, newStyle);
}

void RenderLayerModelObject::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
    bool hadTransform = hasTransform();

    RenderObject::styleDidChange(diff, oldStyle);
    updateFromStyle();

    LayerType type = layerTypeRequired();
    if (type != NoLayer) {
        if (!layer() && layerCreationAllowedForSubtree()) {
            if (s_wasFloating && isFloating())
                setChildNeedsLayout();
            createLayer(type);
            if (parent() && !needsLayout()) {
                // FIXME: We should call a specialized version of this function.
                layer()->updateLayerPositionsAfterLayout();
            }
        }
    } else if (layer() && layer()->parent()) {
        setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit.
        setHasReflection(false);
        layer()->removeOnlyThisLayer(); // calls destroyLayer() which clears m_layer
        if (s_wasFloating && isFloating())
            setChildNeedsLayout();
        if (hadTransform)
            setNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation();
    }

    if (layer()) {
        // FIXME: Ideally we shouldn't need this setter but we can't easily infer an overflow-only layer
        // from the style.
        layer()->setLayerType(type);
        layer()->styleChanged(diff, oldStyle);
    }

    if (FrameView *frameView = view()->frameView()) {
        bool newStyleIsViewportConstained = style()->hasViewportConstrainedPosition();
        bool oldStyleIsViewportConstrained = oldStyle && oldStyle->hasViewportConstrainedPosition();
        if (newStyleIsViewportConstained != oldStyleIsViewportConstrained) {
            if (newStyleIsViewportConstained && layer())
                frameView->addViewportConstrainedObject(this);
            else
                frameView->removeViewportConstrainedObject(this);
        }
    }
}

void RenderLayerModelObject::addLayerHitTestRects(LayerHitTestRects& rects, const RenderLayer* currentLayer, const LayoutPoint& layerOffset, const LayoutRect& containerRect) const
{
    if (hasLayer()) {
        if (isRenderView()) {
            // RenderView is handled with a special fast-path, but it needs to know the current layer.
            RenderObject::addLayerHitTestRects(rects, layer(), LayoutPoint(), LayoutRect());
        } else {
            // Since a RenderObject never lives outside it's container RenderLayer, we can switch
            // to marking entire layers instead. This may sometimes mark more than necessary (when
            // a layer is made of disjoint objects) but in practice is a significant performance
            // savings.
            layer()->addLayerHitTestRects(rects);
        }
    } else {
        RenderObject::addLayerHitTestRects(rects, currentLayer, layerOffset, containerRect);
    }
}

InvalidationReason RenderLayerModelObject::invalidatePaintIfNeeded(const PaintInvalidationState& paintInvalidationState, const RenderLayerModelObject& newPaintInvalidationContainer)
{
    const LayoutRect oldPaintInvalidationRect = previousPaintInvalidationRect();
    const LayoutPoint oldPositionFromPaintInvalidationContainer = previousPositionFromPaintInvalidationContainer();
    setPreviousPaintInvalidationRect(boundsRectForPaintInvalidation(&newPaintInvalidationContainer, &paintInvalidationState));
    setPreviousPositionFromPaintInvalidationContainer(RenderLayer::positionFromPaintInvalidationContainer(this, &newPaintInvalidationContainer, &paintInvalidationState));

    // If we are set to do a full paint invalidation that means the RenderView will issue
    // paint invalidations. We can then skip issuing of paint invalidations for the child
    // renderers as they'll be covered by the RenderView.
    if (view()->doingFullPaintInvalidation())
        return InvalidationNone;

    return RenderObject::invalidatePaintIfNeeded(newPaintInvalidationContainer, oldPaintInvalidationRect, oldPositionFromPaintInvalidationContainer, paintInvalidationState);
}

void RenderLayerModelObject::invalidateTreeIfNeeded(const PaintInvalidationState& paintInvalidationState)
{
    // FIXME: SVG should probably also go through this unified paint invalidation system.
    ASSERT(!needsLayout());

    if (!shouldCheckForPaintInvalidation(paintInvalidationState))
        return;

    bool establishesNewPaintInvalidationContainer = isPaintInvalidationContainer();
    const RenderLayerModelObject& newPaintInvalidationContainer = *adjustCompositedContainerForSpecialAncestors(establishesNewPaintInvalidationContainer ? this : &paintInvalidationState.paintInvalidationContainer());
    ASSERT(&newPaintInvalidationContainer == containerForPaintInvalidation());

    InvalidationReason reason = invalidatePaintIfNeeded(paintInvalidationState, newPaintInvalidationContainer);

    PaintInvalidationState childTreeWalkState(paintInvalidationState, *this, newPaintInvalidationContainer);
    if (reason == InvalidationLocationChange || reason == InvalidationFull)
        childTreeWalkState.setForceCheckForPaintInvalidation();
    RenderObject::invalidateTreeIfNeeded(childTreeWalkState);
}

} // namespace blink

<|endoftext|>


<commit_before>/************************************************************************/
/*                                                                      */
/*     Copyright 2014 by Thorsten Beier and Ullrich Koethe              */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    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.                                   */
/*                                                                      */
/************************************************************************/

/**
 * This header provides definitions of graph-related algorithms
 */

#ifndef VIGRA_GRAPH_RAG_PROJECT_BACK_HXX
#define VIGRA_GRAPH_RAG_PROJECT_BACK_HXX

/*std*/
#include <algorithm>
#include <vector>
#include <functional>
#include <set>

/*vigra*/
#include "graphs.hxx"
#include "graph_generalization.hxx"
#include "multi_gridgraph.hxx"
#include "priority_queue.hxx"
#include "union_find.hxx"
#include "adjacency_list_graph.hxx"
#include "graph_maps.hxx"
#include "timing.hxx"

#include "timing.hxx"

namespace vigra{

    namespace detail_rag_project_back{

    template<
        class BASE_GRAPH,
        class BASE_GRAPH_LABELS,
        class RAG_FEATURES,
        class BASE_GRAPH_FEATURES
    >
    struct RagProjectBack{

        static void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
        ){
            typedef BASE_GRAPH Bg;
            typedef typename Bg::NodeIt BgNodeIt;
            typedef typename Bg::Node BgNode;

            if(ignoreLabel==-1){
                for(BgNodeIt iter(bg); iter!=lemon::INVALID; ++iter){
                    const BgNode bgNode(*iter);
                    bgFeatures[bgNode] = ragFeatures[rag.nodeFromId(bgLabels[bgNode])];
                }
            }
            else{
                for(BgNodeIt iter(bg); iter!=lemon::INVALID; ++iter){
                    const BgNode bgNode(*iter);
                    if(static_cast<Int64>(bgLabels[bgNode])!=ignoreLabel)
                        bgFeatures[bgNode] = ragFeatures[rag.nodeFromId(bgLabels[bgNode])];
                }
            }
        }
    };

    template<
        class BASE_GRAPH_LABELS,
        class RAG_FEATURES,
        class BASE_GRAPH_FEATURES
    >
    struct RagProjectBack<
        vigra::GridGraph<3, boost::undirected_tag>,
        BASE_GRAPH_LABELS,
        RAG_FEATURES,
        BASE_GRAPH_FEATURES
    >{
        typedef vigra::GridGraph<3, boost::undirected_tag> BASE_GRAPH;

        static void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
        ){
            typedef BASE_GRAPH Bg;
            typedef typename Bg::Node BgNode;

            vigra::TinyVector<Int64, 3> shape = bg.shape();

            if(ignoreLabel==-1){

                #pragma omp parallel for
                for(Int64 z=0; z<shape[2]; ++z){
                    BgNode node;
                    node[2]=z;
                    for(node[1]=0; node[1]<shape[1]; ++node[1])
                    for(node[0]=0; node[0]<shape[0]; ++node[0]){
                        bgFeatures[node] = ragFeatures[rag.nodeFromId(bgLabels[node])];
                    }
                }

            }
            else{
                #pragma omp parallel for
                for(Int64 z=0; z<shape[2]; ++z){
                    BgNode node;
                    node[2]=z;
                    for(node[1]=0; node[1]<shape[1]; ++node[1])
                    for(node[0]=0; node[0]<shape[0]; ++node[0]){
                        if(static_cast<Int64>(bgLabels[node])!=ignoreLabel)
                            bgFeatures[node] = ragFeatures[rag.nodeFromId(bgLabels[node])];
                    }
                }
            }
        }
    };

    }

    /// project node features of a region adjacency
    /// graph back to the base graph.
    ///
    /// This function can be used to show a segmentation
    /// or node features of RAG on pixel / voxel level
    template< class BASE_GRAPH,
                class BASE_GRAPH_LABELS,
                class RAG_FEATURES,
                class BASE_GRAPH_FEATURES
    >
    inline void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
    ){
        using namespace detail_rag_project_back;
        detail_rag_project_back::RagProjectBack< BASE_GRAPH,BASE_GRAPH_LABELS,RAG_FEATURES,BASE_GRAPH_FEATURES>::projectBack(rag,
            bg,ignoreLabel,bgLabels,ragFeatures,bgFeatures);
    }

}

#endif /* VIGRA_GRAPH_RAG_PROJECT_BACK_HXX */

<commit_msg>cleanup<commit_after>/************************************************************************/
/*                                                                      */
/*     Copyright 2014 by Thorsten Beier and Ullrich Koethe              */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    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.                                   */
/*                                                                      */
/************************************************************************/

/**
 * This header provides definitions of graph-related algorithms
 */

#ifndef VIGRA_GRAPH_RAG_PROJECT_BACK_HXX
#define VIGRA_GRAPH_RAG_PROJECT_BACK_HXX

/*std*/
#include <algorithm>
#include <vector>
#include <functional>
#include <set>

/*vigra*/
#include "graphs.hxx"
#include "graph_generalization.hxx"
#include "multi_gridgraph.hxx"
#include "priority_queue.hxx"
#include "union_find.hxx"
#include "adjacency_list_graph.hxx"
#include "graph_maps.hxx"

namespace vigra{

    /// \cond
    namespace detail_rag_project_back{

    template<
        class BASE_GRAPH,
        class BASE_GRAPH_LABELS,
        class RAG_FEATURES,
        class BASE_GRAPH_FEATURES
    >
    struct RagProjectBack{

        static void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
        ){
            typedef BASE_GRAPH Bg;
            typedef typename Bg::NodeIt BgNodeIt;
            typedef typename Bg::Node BgNode;

            if(ignoreLabel==-1){
                for(BgNodeIt iter(bg); iter!=lemon::INVALID; ++iter){
                    const BgNode bgNode(*iter);
                    bgFeatures[bgNode] = ragFeatures[rag.nodeFromId(bgLabels[bgNode])];
                }
            }
            else{
                for(BgNodeIt iter(bg); iter!=lemon::INVALID; ++iter){
                    const BgNode bgNode(*iter);
                    if(static_cast<Int64>(bgLabels[bgNode])!=ignoreLabel)
                        bgFeatures[bgNode] = ragFeatures[rag.nodeFromId(bgLabels[bgNode])];
                }
            }
        }
    };

    template<
        class BASE_GRAPH_LABELS,
        class RAG_FEATURES,
        class BASE_GRAPH_FEATURES
    >
    struct RagProjectBack<
        vigra::GridGraph<3, boost::undirected_tag>,
        BASE_GRAPH_LABELS,
        RAG_FEATURES,
        BASE_GRAPH_FEATURES
    >{
        typedef vigra::GridGraph<3, boost::undirected_tag> BASE_GRAPH;

        static void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
        ){
            typedef BASE_GRAPH Bg;
            typedef typename Bg::Node BgNode;

            vigra::TinyVector<Int64, 3> shape = bg.shape();

            if(ignoreLabel==-1){

                #pragma omp parallel for
                for(Int64 z=0; z<shape[2]; ++z){
                    BgNode node;
                    node[2]=z;
                    for(node[1]=0; node[1]<shape[1]; ++node[1])
                    for(node[0]=0; node[0]<shape[0]; ++node[0]){
                        bgFeatures[node] = ragFeatures[rag.nodeFromId(bgLabels[node])];
                    }
                }

            }
            else{
                #pragma omp parallel for
                for(Int64 z=0; z<shape[2]; ++z){
                    BgNode node;
                    node[2]=z;
                    for(node[1]=0; node[1]<shape[1]; ++node[1])
                    for(node[0]=0; node[0]<shape[0]; ++node[0]){
                        if(static_cast<Int64>(bgLabels[node])!=ignoreLabel)
                            bgFeatures[node] = ragFeatures[rag.nodeFromId(bgLabels[node])];
                    }
                }
            }
        }
    };

    }
    /// \endcond

    /// project node features of a region adjacency
    /// graph back to the base graph.
    ///
    /// This function can be used to show a segmentation
    /// or node features of RAG on pixel / voxel level
    template< class BASE_GRAPH,
                class BASE_GRAPH_LABELS,
                class RAG_FEATURES,
                class BASE_GRAPH_FEATURES
    >
    inline void projectBack(
            const AdjacencyListGraph & rag,
            const BASE_GRAPH & bg,
            const Int64 ignoreLabel,
            const BASE_GRAPH_LABELS bgLabels,
            const RAG_FEATURES & ragFeatures,
            BASE_GRAPH_FEATURES & bgFeatures
    ){
        using namespace detail_rag_project_back;
        detail_rag_project_back::RagProjectBack< BASE_GRAPH,BASE_GRAPH_LABELS,RAG_FEATURES,BASE_GRAPH_FEATURES>::projectBack(rag,
            bg,ignoreLabel,bgLabels,ragFeatures,bgFeatures);
    }

}

#endif /* VIGRA_GRAPH_RAG_PROJECT_BACK_HXX */

<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 <modules/qtwidgets/properties/collapsiblegroupboxwidgetqt.h>
#include <modules/qtwidgets/properties/compositepropertywidgetqt.h>
#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/properties/property.h>
#include <inviwo/core/util/settings/systemsettings.h>
#include <inviwo/core/properties/propertywidgetfactory.h>
#include <inviwo/core/network/processornetwork.h>
#include <modules/qtwidgets/editablelabelqt.h>

#include <warn/push>
#include <warn/ignore/all>
#include <QLineEdit>
#include <QToolButton>
#include <QGroupBox>
#include <QPushButton>
#include <QGridLayout>
#include <QLabel>
#include <QCheckBox>
#include <QMenu>
#include <QAction>
#include <warn/pop>

namespace inviwo {

CollapsibleGroupBoxWidgetQt::CollapsibleGroupBoxWidgetQt(std::string displayName, bool isCheckable)
    : PropertyWidgetQt()
    , PropertyOwnerObserver()
    , displayName_(displayName)
    , collapsed_(false)
    , checked_(false)
    , propertyOwner_(nullptr)
    , showIfEmpty_(false)
    , checkable_(isCheckable) {
    setObjectName("CompositeWidget");

    generateWidget();
    updateFromProperty();
}

CollapsibleGroupBoxWidgetQt::CollapsibleGroupBoxWidgetQt(Property* property, bool isCheckable)
    : PropertyWidgetQt(property)
    , PropertyOwnerObserver()
    , displayName_(property->getDisplayName())
    , collapsed_(false)
    , checked_(false)
    , propertyOwner_(nullptr)
    , showIfEmpty_(false)
    , checkable_(isCheckable) {
    setObjectName("CompositeWidget");

    generateWidget();
    updateFromProperty();
}

void CollapsibleGroupBoxWidgetQt::generateWidget() {
    propertyWidgetGroupLayout_ = new QGridLayout();
    propertyWidgetGroupLayout_->setAlignment(Qt::AlignTop);
    propertyWidgetGroupLayout_->setContentsMargins(
        PropertyWidgetQt::spacing, PropertyWidgetQt::spacing, 0, PropertyWidgetQt::spacing);
    propertyWidgetGroupLayout_->setHorizontalSpacing(0);
    propertyWidgetGroupLayout_->setVerticalSpacing(PropertyWidgetQt::spacing);

    propertyWidgetGroup_ = new QWidget(this);
    propertyWidgetGroup_->setObjectName("CompositeContents");
    propertyWidgetGroup_->setLayout(propertyWidgetGroupLayout_);

    defaultLabel_ = new QLabel("No properties available");

    propertyWidgetGroupLayout_->addWidget(defaultLabel_, 0, 0);
    propertyWidgetGroupLayout_->addItem(
        new QSpacerItem(PropertyWidgetQt::spacing, 1, QSizePolicy::Fixed), 0, 1);
    propertyWidgetGroupLayout_->setColumnStretch(0, 1);
    propertyWidgetGroupLayout_->setColumnStretch(1, 0);

    btnCollapse_ = new QToolButton(this);
    btnCollapse_->setObjectName("collapseButton");
    btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_down.png"));
    connect(btnCollapse_, &QToolButton::clicked, this,
            &CollapsibleGroupBoxWidgetQt::toggleCollapsed);

    if (property_) {
        label_ = new EditableLabelQt(this, property_, false);
    } else {
        label_ = new EditableLabelQt(this, displayName_, false);
    }
    label_->setObjectName("compositeLabel");
    QSizePolicy labelPol = label_->sizePolicy();
    labelPol.setHorizontalStretch(10);
    label_->setSizePolicy(labelPol);
    connect(label_, &EditableLabelQt::textChanged, this,
            [&]() { setDisplayName(label_->getText()); });

    resetButton_ = new QToolButton(this);
    resetButton_->setIconSize(QSize(20, 20));
    resetButton_->setObjectName("resetButton");
    connect(resetButton_, &QToolButton::clicked, this, [&]() {
        if (property_) {
            property_->resetToDefaultState();
        } else if (propertyOwner_) {
            propertyOwner_->resetAllPoperties();
        }
    });

    resetButton_->setToolTip(tr("Reset the group of properties to its default state"));

    checkBox_ = new QCheckBox(this);
    checkBox_->setMinimumSize(5, 5);
    checkBox_->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
    checkBox_->setChecked(checked_);
    checkBox_->setVisible(checkable_);

    QObject::connect(checkBox_, &QCheckBox::clicked, this,
                     [&]() { setChecked(checkBox_->isChecked()); });

    QHBoxLayout* heading = new QHBoxLayout();
    heading->setContentsMargins(0, 0, 0, 0);
    heading->setSpacing(PropertyWidgetQt::spacing);
    heading->addWidget(btnCollapse_);
    heading->addWidget(label_);
    heading->addStretch(1);
    heading->addWidget(checkBox_);
    heading->addWidget(resetButton_);

    QVBoxLayout* layout = new QVBoxLayout();
    setSpacingAndMargins(layout);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);
    layout->addLayout(heading);
    layout->addWidget(propertyWidgetGroup_);

    // Adjust the margins when using a border, i.e. margin >= border width.
    // Otherwise the border might be overdrawn by children.
    this->setContentsMargins(margin, margin, margin, margin);

    this->setLayout(layout);
}

std::unique_ptr<QMenu> CollapsibleGroupBoxWidgetQt::getContextMenu() {
    auto menu = PropertyWidgetQt::getContextMenu();

    if (propertyOwner_ && !property_) {
        menu->addAction(QString::fromStdString(displayName_));
        menu->addSeparator();

        auto resetAction = menu->addAction(tr("&Reset to default"));
        resetAction->setToolTip(tr("&RReset the group of properties to its default state"));
        connect(resetAction, &QAction::triggered, this,
                [&]() { propertyOwner_->resetAllPoperties(); });
    }
    return menu;
}

QSize CollapsibleGroupBoxWidgetQt::sizeHint() const {
    QSize size = layout()->sizeHint();
    size.setWidth(std::max(PropertyWidgetQt::minimumWidth, size.width()));
    return size;
}

QSize CollapsibleGroupBoxWidgetQt::minimumSizeHint() const {
    QSize size = layout()->sizeHint();
    QSize minSize = layout()->minimumSize();
    size.setWidth(std::max(PropertyWidgetQt::minimumWidth, minSize.width()));
    return size;
}

void CollapsibleGroupBoxWidgetQt::setReadOnly(bool readonly) {
    label_->setDisabled(readonly);
    checkBox_->setDisabled(readonly);
    resetButton_->setDisabled(readonly);
}

void CollapsibleGroupBoxWidgetQt::addProperty(Property* prop) {
    properties_.push_back(prop);

    auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
    if (auto propertyWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {
        if (auto collapsibleWidget = dynamic_cast<CollapsibleGroupBoxWidgetQt*>(propertyWidget)) {
            collapsibleWidget->setNestedDepth(this->getNestedDepth() + 1);
            // make the collapsible widget go all the way to the right border
            propertyWidgetGroupLayout_->addWidget(propertyWidget,
                                                  propertyWidgetGroupLayout_->rowCount(), 0, 1, -1);
        } else {  // not a collapsible widget
            propertyWidget->setNestedDepth(this->getNestedDepth());
            // property widget should only be added to the left column of the layout
            propertyWidgetGroupLayout_->addWidget(propertyWidget,
                                                  propertyWidgetGroupLayout_->rowCount(), 0);
        }

        propertyWidgets_.push_back(propertyWidget);
        connect(propertyWidget, &PropertyWidgetQt::updateSemantics, this,
                &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

        propertyWidget->setParentPropertyWidget(this, getBaseContainer());
        propertyWidget->initState();

    } else {
        LogWarn("Could not find a widget for property: " << prop->getClassIdentifier());
    }
}

std::string CollapsibleGroupBoxWidgetQt::getDisplayName() const { return displayName_; }

void CollapsibleGroupBoxWidgetQt::setDisplayName(const std::string& displayName) {
    displayName_ = displayName;
    if (propertyOwner_) {
        if (Processor* p = dynamic_cast<Processor*>(propertyOwner_)) {
            try {
                p->setIdentifier(displayName);
            } catch (Exception& e) {
                label_->setText(p->getIdentifier());
                LogWarn(e.getMessage());
            }
        }
    }
}

const std::vector<Property*>& CollapsibleGroupBoxWidgetQt::getProperties() { return properties_; }

void CollapsibleGroupBoxWidgetQt::toggleCollapsed() { setCollapsed(!isCollapsed()); }

bool CollapsibleGroupBoxWidgetQt::isCollapsed() const { return collapsed_; }

bool CollapsibleGroupBoxWidgetQt::isChecked() const { return checked_; }

void CollapsibleGroupBoxWidgetQt::setChecked(bool checked) {
    if (!checkable_) {
        return;
    }

    checked_ = checked;
    // update checkbox
    checkBox_->setChecked(checked_);
}

bool CollapsibleGroupBoxWidgetQt::isCheckable() const { return checkable_; }

void CollapsibleGroupBoxWidgetQt::setCheckable(bool checkable) {
    if (checkable_ == checkable) {
        return;
    }

    checkable_ = checkable;
    // update header
    checkBox_->setVisible(checkable_);
}

void CollapsibleGroupBoxWidgetQt::setVisible(bool visible) {
    bool empty = util::all_of(properties_, [](Property* w) { return !w->getVisible(); });
    defaultLabel_->setVisible(empty);

    if (showIfEmpty_ || !empty) {
        PropertyWidgetQt::setVisible(visible);
    } else {
        PropertyWidgetQt::setVisible(false);
    }
}

void CollapsibleGroupBoxWidgetQt::setCollapsed(bool collapse) {
    setUpdatesEnabled(false);
    if (collapsed_ && !collapse) {
        propertyWidgetGroup_->show();
        btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_down.png"));
    } else if (!collapsed_ && collapse) {
        propertyWidgetGroup_->hide();
        btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_right.png"));
    }
    collapsed_ = collapse;
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics(PropertyWidgetQt* widget) {
    setUpdatesEnabled(false);
    Property* prop = widget->getProperty();

    auto pit = std::find(properties_.begin(), properties_.end(), prop);
    auto wit = std::find(propertyWidgets_.begin(), propertyWidgets_.end(), widget);

    if (pit != properties_.end() && wit != propertyWidgets_.end()) {
        auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
        if (auto newWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {

#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
            propertyWidgetGroupLayout_->replaceWidget(widget, newWidget,
                                                      Qt::FindDirectChildrenOnly);
#else
            int layoutPosition = propertyWidgetGroupLayout_->indexOf(widget);
            propertyWidgetGroupLayout_->removeWidget(widget);
            propertyWidgetGroupLayout_->addWidget(newWidget, layoutPosition, 0);
#endif  // QT_VERSION >= 5.2
            widget->deleteLater();

            // Replace the item in propertyWidgets_;
            *wit = newWidget;

            connect(newWidget, &PropertyWidgetQt::updateSemantics, this,
                    &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

            newWidget->setNestedDepth(this->getNestedDepth());
            newWidget->setParentPropertyWidget(this, getBaseContainer());
            newWidget->initState();

        } else {
            LogWarn("Could not change semantic for property: " << prop->getClassIdentifier());
        }
    }
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::onDidAddProperty(Property* prop, size_t index) {
    setUpdatesEnabled(false);
    std::vector<Property*>::iterator insertPoint = properties_.begin() + index;
    if (insertPoint != properties_.end()) ++insertPoint;

    properties_.insert(insertPoint, prop);

    auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
    if (auto propertyWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {
        const int insertPos = static_cast<int>(index) + 1;

        if (auto collapsibleWidget = dynamic_cast<CollapsibleGroupBoxWidgetQt*>(propertyWidget)) {
            collapsibleWidget->setNestedDepth(this->getNestedDepth() + 1);
            // make the collapsible widget go all the way to the right border
            propertyWidgetGroupLayout_->addWidget(propertyWidget, insertPos, 0, 1, -1);

        } else {  // not a collapsible widget
            propertyWidget->setNestedDepth(this->getNestedDepth());
            // property widget should only be added to the left column of the layout
            propertyWidgetGroupLayout_->addWidget(propertyWidget, insertPos, 0);
        }

        auto widgetInsertPoint = propertyWidgets_.begin() + index;
        if (widgetInsertPoint != propertyWidgets_.end()) ++widgetInsertPoint;

        propertyWidgets_.insert(widgetInsertPoint, propertyWidget);
        connect(propertyWidget, &PropertyWidgetQt::updateSemantics, this,
                &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

        propertyWidget->setParentPropertyWidget(this, getBaseContainer());
        propertyWidget->initState();

    } else {
        LogWarn("Could not find a widget for property: " << prop->getClassIdentifier());
    }
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::onWillRemoveProperty(Property* /*prop*/, size_t index) {
    PropertyWidgetQt* propertyWidget = propertyWidgets_[index];

    propertyWidgetGroupLayout_->removeWidget(propertyWidget);
    propertyWidgets_.erase(propertyWidgets_.begin() + index);
    properties_.erase(properties_.begin() + index);
    propertyWidget->deleteLater();
}

void CollapsibleGroupBoxWidgetQt::onProcessorIdentifierChange(Processor* processor) {
    displayName_ = processor->getIdentifier();
    label_->setText(processor->getIdentifier());
}

void CollapsibleGroupBoxWidgetQt::setPropertyOwner(PropertyOwner* propertyOwner) {
    propertyOwner_ = propertyOwner;
}

PropertyOwner* CollapsibleGroupBoxWidgetQt::getPropertyOwner() const { return propertyOwner_; }

const std::vector<PropertyWidgetQt*>& CollapsibleGroupBoxWidgetQt::getPropertyWidgets() {
    return propertyWidgets_;
}

void CollapsibleGroupBoxWidgetQt::setShowIfEmpty(bool val) { showIfEmpty_ = val; }
}  // namespace inviwo
<commit_msg>Qt: delete later is evil...<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2017 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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 <modules/qtwidgets/properties/collapsiblegroupboxwidgetqt.h>
#include <modules/qtwidgets/properties/compositepropertywidgetqt.h>
#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/properties/property.h>
#include <inviwo/core/util/settings/systemsettings.h>
#include <inviwo/core/properties/propertywidgetfactory.h>
#include <inviwo/core/network/processornetwork.h>
#include <modules/qtwidgets/editablelabelqt.h>

#include <warn/push>
#include <warn/ignore/all>
#include <QLineEdit>
#include <QToolButton>
#include <QGroupBox>
#include <QPushButton>
#include <QGridLayout>
#include <QLabel>
#include <QCheckBox>
#include <QMenu>
#include <QAction>
#include <warn/pop>

namespace inviwo {

CollapsibleGroupBoxWidgetQt::CollapsibleGroupBoxWidgetQt(std::string displayName, bool isCheckable)
    : PropertyWidgetQt()
    , PropertyOwnerObserver()
    , displayName_(displayName)
    , collapsed_(false)
    , checked_(false)
    , propertyOwner_(nullptr)
    , showIfEmpty_(false)
    , checkable_(isCheckable) {
    setObjectName("CompositeWidget");

    generateWidget();
    updateFromProperty();
}

CollapsibleGroupBoxWidgetQt::CollapsibleGroupBoxWidgetQt(Property* property, bool isCheckable)
    : PropertyWidgetQt(property)
    , PropertyOwnerObserver()
    , displayName_(property->getDisplayName())
    , collapsed_(false)
    , checked_(false)
    , propertyOwner_(nullptr)
    , showIfEmpty_(false)
    , checkable_(isCheckable) {
    setObjectName("CompositeWidget");

    generateWidget();
    updateFromProperty();
}

void CollapsibleGroupBoxWidgetQt::generateWidget() {
    propertyWidgetGroupLayout_ = new QGridLayout();
    propertyWidgetGroupLayout_->setAlignment(Qt::AlignTop);
    propertyWidgetGroupLayout_->setContentsMargins(
        PropertyWidgetQt::spacing, PropertyWidgetQt::spacing, 0, PropertyWidgetQt::spacing);
    propertyWidgetGroupLayout_->setHorizontalSpacing(0);
    propertyWidgetGroupLayout_->setVerticalSpacing(PropertyWidgetQt::spacing);

    propertyWidgetGroup_ = new QWidget(this);
    propertyWidgetGroup_->setObjectName("CompositeContents");
    propertyWidgetGroup_->setLayout(propertyWidgetGroupLayout_);

    defaultLabel_ = new QLabel("No properties available");

    propertyWidgetGroupLayout_->addWidget(defaultLabel_, 0, 0);
    propertyWidgetGroupLayout_->addItem(
        new QSpacerItem(PropertyWidgetQt::spacing, 1, QSizePolicy::Fixed), 0, 1);
    propertyWidgetGroupLayout_->setColumnStretch(0, 1);
    propertyWidgetGroupLayout_->setColumnStretch(1, 0);

    btnCollapse_ = new QToolButton(this);
    btnCollapse_->setObjectName("collapseButton");
    btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_down.png"));
    connect(btnCollapse_, &QToolButton::clicked, this,
            &CollapsibleGroupBoxWidgetQt::toggleCollapsed);

    if (property_) {
        label_ = new EditableLabelQt(this, property_, false);
    } else {
        label_ = new EditableLabelQt(this, displayName_, false);
    }
    label_->setObjectName("compositeLabel");
    QSizePolicy labelPol = label_->sizePolicy();
    labelPol.setHorizontalStretch(10);
    label_->setSizePolicy(labelPol);
    connect(label_, &EditableLabelQt::textChanged, this,
            [&]() { setDisplayName(label_->getText()); });

    resetButton_ = new QToolButton(this);
    resetButton_->setIconSize(QSize(20, 20));
    resetButton_->setObjectName("resetButton");
    connect(resetButton_, &QToolButton::clicked, this, [&]() {
        if (property_) {
            property_->resetToDefaultState();
        } else if (propertyOwner_) {
            propertyOwner_->resetAllPoperties();
        }
    });

    resetButton_->setToolTip(tr("Reset the group of properties to its default state"));

    checkBox_ = new QCheckBox(this);
    checkBox_->setMinimumSize(5, 5);
    checkBox_->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
    checkBox_->setChecked(checked_);
    checkBox_->setVisible(checkable_);

    QObject::connect(checkBox_, &QCheckBox::clicked, this,
                     [&]() { setChecked(checkBox_->isChecked()); });

    QHBoxLayout* heading = new QHBoxLayout();
    heading->setContentsMargins(0, 0, 0, 0);
    heading->setSpacing(PropertyWidgetQt::spacing);
    heading->addWidget(btnCollapse_);
    heading->addWidget(label_);
    heading->addStretch(1);
    heading->addWidget(checkBox_);
    heading->addWidget(resetButton_);

    QVBoxLayout* layout = new QVBoxLayout();
    setSpacingAndMargins(layout);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);
    layout->addLayout(heading);
    layout->addWidget(propertyWidgetGroup_);

    // Adjust the margins when using a border, i.e. margin >= border width.
    // Otherwise the border might be overdrawn by children.
    this->setContentsMargins(margin, margin, margin, margin);

    this->setLayout(layout);
}

std::unique_ptr<QMenu> CollapsibleGroupBoxWidgetQt::getContextMenu() {
    auto menu = PropertyWidgetQt::getContextMenu();

    if (propertyOwner_ && !property_) {
        menu->addAction(QString::fromStdString(displayName_));
        menu->addSeparator();

        auto resetAction = menu->addAction(tr("&Reset to default"));
        resetAction->setToolTip(tr("&RReset the group of properties to its default state"));
        connect(resetAction, &QAction::triggered, this,
                [&]() { propertyOwner_->resetAllPoperties(); });
    }
    return menu;
}

QSize CollapsibleGroupBoxWidgetQt::sizeHint() const {
    QSize size = layout()->sizeHint();
    size.setWidth(std::max(PropertyWidgetQt::minimumWidth, size.width()));
    return size;
}

QSize CollapsibleGroupBoxWidgetQt::minimumSizeHint() const {
    QSize size = layout()->sizeHint();
    QSize minSize = layout()->minimumSize();
    size.setWidth(std::max(PropertyWidgetQt::minimumWidth, minSize.width()));
    return size;
}

void CollapsibleGroupBoxWidgetQt::setReadOnly(bool readonly) {
    label_->setDisabled(readonly);
    checkBox_->setDisabled(readonly);
    resetButton_->setDisabled(readonly);
}

void CollapsibleGroupBoxWidgetQt::addProperty(Property* prop) {
    properties_.push_back(prop);

    auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
    if (auto propertyWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {
        if (auto collapsibleWidget = dynamic_cast<CollapsibleGroupBoxWidgetQt*>(propertyWidget)) {
            collapsibleWidget->setNestedDepth(this->getNestedDepth() + 1);
            // make the collapsible widget go all the way to the right border
            propertyWidgetGroupLayout_->addWidget(propertyWidget,
                                                  propertyWidgetGroupLayout_->rowCount(), 0, 1, -1);
        } else {  // not a collapsible widget
            propertyWidget->setNestedDepth(this->getNestedDepth());
            // property widget should only be added to the left column of the layout
            propertyWidgetGroupLayout_->addWidget(propertyWidget,
                                                  propertyWidgetGroupLayout_->rowCount(), 0);
        }

        propertyWidgets_.push_back(propertyWidget);
        connect(propertyWidget, &PropertyWidgetQt::updateSemantics, this,
                &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

        propertyWidget->setParentPropertyWidget(this, getBaseContainer());
        propertyWidget->initState();

    } else {
        LogWarn("Could not find a widget for property: " << prop->getClassIdentifier());
    }
}

std::string CollapsibleGroupBoxWidgetQt::getDisplayName() const { return displayName_; }

void CollapsibleGroupBoxWidgetQt::setDisplayName(const std::string& displayName) {
    displayName_ = displayName;
    if (propertyOwner_) {
        if (Processor* p = dynamic_cast<Processor*>(propertyOwner_)) {
            try {
                p->setIdentifier(displayName);
            } catch (Exception& e) {
                label_->setText(p->getIdentifier());
                LogWarn(e.getMessage());
            }
        }
    }
}

const std::vector<Property*>& CollapsibleGroupBoxWidgetQt::getProperties() { return properties_; }

void CollapsibleGroupBoxWidgetQt::toggleCollapsed() { setCollapsed(!isCollapsed()); }

bool CollapsibleGroupBoxWidgetQt::isCollapsed() const { return collapsed_; }

bool CollapsibleGroupBoxWidgetQt::isChecked() const { return checked_; }

void CollapsibleGroupBoxWidgetQt::setChecked(bool checked) {
    if (!checkable_) {
        return;
    }

    checked_ = checked;
    // update checkbox
    checkBox_->setChecked(checked_);
}

bool CollapsibleGroupBoxWidgetQt::isCheckable() const { return checkable_; }

void CollapsibleGroupBoxWidgetQt::setCheckable(bool checkable) {
    if (checkable_ == checkable) {
        return;
    }

    checkable_ = checkable;
    // update header
    checkBox_->setVisible(checkable_);
}

void CollapsibleGroupBoxWidgetQt::setVisible(bool visible) {
    bool empty = util::all_of(properties_, [](Property* w) { return !w->getVisible(); });
    defaultLabel_->setVisible(empty);

    if (showIfEmpty_ || !empty) {
        PropertyWidgetQt::setVisible(visible);
    } else {
        PropertyWidgetQt::setVisible(false);
    }
}

void CollapsibleGroupBoxWidgetQt::setCollapsed(bool collapse) {
    setUpdatesEnabled(false);
    if (collapsed_ && !collapse) {
        propertyWidgetGroup_->show();
        btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_down.png"));
    } else if (!collapsed_ && collapse) {
        propertyWidgetGroup_->hide();
        btnCollapse_->setIcon(QIcon(":/stylesheets/images/arrow_lighter_right.png"));
    }
    collapsed_ = collapse;
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics(PropertyWidgetQt* widget) {
    setUpdatesEnabled(false);
    Property* prop = widget->getProperty();

    auto pit = std::find(properties_.begin(), properties_.end(), prop);
    auto wit = std::find(propertyWidgets_.begin(), propertyWidgets_.end(), widget);

    if (pit != properties_.end() && wit != propertyWidgets_.end()) {
        auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
        if (auto newWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {

#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
            propertyWidgetGroupLayout_->replaceWidget(widget, newWidget,
                                                      Qt::FindDirectChildrenOnly);
#else
            int layoutPosition = propertyWidgetGroupLayout_->indexOf(widget);
            propertyWidgetGroupLayout_->removeWidget(widget);
            propertyWidgetGroupLayout_->addWidget(newWidget, layoutPosition, 0);
#endif  // QT_VERSION >= 5.2
            delete widget;

            // Replace the item in propertyWidgets_;
            *wit = newWidget;

            connect(newWidget, &PropertyWidgetQt::updateSemantics, this,
                    &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

            newWidget->setNestedDepth(this->getNestedDepth());
            newWidget->setParentPropertyWidget(this, getBaseContainer());
            newWidget->initState();

        } else {
            LogWarn("Could not change semantic for property: " << prop->getClassIdentifier());
        }
    }
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::onDidAddProperty(Property* prop, size_t index) {
    setUpdatesEnabled(false);
    std::vector<Property*>::iterator insertPoint = properties_.begin() + index;
    if (insertPoint != properties_.end()) ++insertPoint;

    properties_.insert(insertPoint, prop);

    auto factory = InviwoApplication::getPtr()->getPropertyWidgetFactory();
    if (auto propertyWidget = static_cast<PropertyWidgetQt*>(factory->create(prop).release())) {
        const int insertPos = static_cast<int>(index) + 1;

        if (auto collapsibleWidget = dynamic_cast<CollapsibleGroupBoxWidgetQt*>(propertyWidget)) {
            collapsibleWidget->setNestedDepth(this->getNestedDepth() + 1);
            // make the collapsible widget go all the way to the right border
            propertyWidgetGroupLayout_->addWidget(propertyWidget, insertPos, 0, 1, -1);

        } else {  // not a collapsible widget
            propertyWidget->setNestedDepth(this->getNestedDepth());
            // property widget should only be added to the left column of the layout
            propertyWidgetGroupLayout_->addWidget(propertyWidget, insertPos, 0);
        }

        auto widgetInsertPoint = propertyWidgets_.begin() + index;
        if (widgetInsertPoint != propertyWidgets_.end()) ++widgetInsertPoint;

        propertyWidgets_.insert(widgetInsertPoint, propertyWidget);
        connect(propertyWidget, &PropertyWidgetQt::updateSemantics, this,
                &CollapsibleGroupBoxWidgetQt::updatePropertyWidgetSemantics);

        propertyWidget->setParentPropertyWidget(this, getBaseContainer());
        propertyWidget->initState();

    } else {
        LogWarn("Could not find a widget for property: " << prop->getClassIdentifier());
    }
    setUpdatesEnabled(true);
}

void CollapsibleGroupBoxWidgetQt::onWillRemoveProperty(Property* /*prop*/, size_t index) {
    PropertyWidgetQt* propertyWidget = propertyWidgets_[index];

    propertyWidgetGroupLayout_->removeWidget(propertyWidget);
    propertyWidgets_.erase(propertyWidgets_.begin() + index);
    properties_.erase(properties_.begin() + index);
    delete propertyWidget;
}

void CollapsibleGroupBoxWidgetQt::onProcessorIdentifierChange(Processor* processor) {
    displayName_ = processor->getIdentifier();
    label_->setText(processor->getIdentifier());
}

void CollapsibleGroupBoxWidgetQt::setPropertyOwner(PropertyOwner* propertyOwner) {
    propertyOwner_ = propertyOwner;
}

PropertyOwner* CollapsibleGroupBoxWidgetQt::getPropertyOwner() const { return propertyOwner_; }

const std::vector<PropertyWidgetQt*>& CollapsibleGroupBoxWidgetQt::getPropertyWidgets() {
    return propertyWidgets_;
}

void CollapsibleGroupBoxWidgetQt::setShowIfEmpty(bool val) { showIfEmpty_ = val; }
}  // namespace inviwo
<|endoftext|>