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

#include "MOF_Data_Type.h"
#include "MOF_Error.h"
#include "MOF_String.h"
#include "MOF_Yacc.h"

const char* MOF_Data_Type::to_string(int data_type)
{
    switch (data_type)
    {
        case TOK_UINT8:
            return "uint8";

        case TOK_SINT8:
            return "sint8";

        case TOK_UINT16:
            return "uint16";

        case TOK_SINT16:
            return "sint16";

        case TOK_UINT32:
            return "uint32";

        case TOK_SINT32:
            return "sint32";

        case TOK_UINT64:
            return "uint64";

        case TOK_SINT64:
            return "sint64";

        case TOK_REAL32:
            return "real32";

        case TOK_REAL64:
            return "real64";

        case TOK_CHAR16:
            return "char16";

        case TOK_STRING:
            return "string";

        case TOK_BOOLEAN:
            return "boolean";

        case TOK_DATETIME:
            return "datetime";

        default:
            MOF_ASSERT(false);
            return 0;
    }

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

#include "MOF_Data_Type.h"
#include "MOF_Error.h"
#include "MOF_String.h"
#include "MOF_Yacc.h"

const char* MOF_Data_Type::to_string(int data_type)
{
    switch (data_type)
    {
        case TOK_UINT8:
            return "uint8";

        case TOK_SINT8:
            return "sint8";

        case TOK_UINT16:
            return "uint16";

        case TOK_SINT16:
            return "sint16";

        case TOK_UINT32:
            return "uint32";

        case TOK_SINT32:
            return "sint32";

        case TOK_UINT64:
            return "uint64";

        case TOK_SINT64:
            return "sint64";

        case TOK_REAL32:
            return "real32";

        case TOK_REAL64:
            return "real64";

        case TOK_CHAR16:
            return "char16";

        case TOK_STRING:
            return "string";

        case TOK_BOOLEAN:
            return "boolean";

        case TOK_DATETIME:
            return "datetime";

        case TOK_INSTANCE:
            return "instance";

        default:
            MOF_ASSERT(false);
            return 0;
    }

    /* Unreachable */
    return 0;
}
<|endoftext|>


<commit_before>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2012, John Haddon. All rights reserved.
//  Copyright (c) 2013, 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 John Haddon 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 "GafferScene/GlobalsProcessor.h"

using namespace Gaffer;
using namespace GafferScene;

GAFFER_GRAPHCOMPONENT_DEFINE_TYPE( GlobalsProcessor );

GlobalsProcessor::GlobalsProcessor( const std::string &name )
	:	SceneProcessor( name )
{
	// Fast pass-throughs for everything except the globals
	outPlug()->boundPlug()->setInput( inPlug()->boundPlug() );
	outPlug()->transformPlug()->setInput( inPlug()->transformPlug() );
	outPlug()->attributesPlug()->setInput( inPlug()->attributesPlug() );
	outPlug()->objectPlug()->setInput( inPlug()->objectPlug() );
	outPlug()->childNamesPlug()->setInput( inPlug()->childNamesPlug() );
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
	outPlug()->setPlug()->setInput( inPlug()->setPlug() );
}

GlobalsProcessor::~GlobalsProcessor()
{
}

void GlobalsProcessor::affects( const Plug *input, AffectedPlugsContainer &outputs ) const
{
	SceneProcessor::affects( input, outputs );

	if( input == inPlug()->globalsPlug() )
	{
		outputs.push_back( outPlug()->globalsPlug() );
	}
}

void GlobalsProcessor::hashGlobals( const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	SceneProcessor::hashGlobals( context, parent, h );
	inPlug()->globalsPlug()->hash( h );
	hashProcessedGlobals( context, h );
}

IECore::ConstCompoundObjectPtr GlobalsProcessor::computeGlobals( const Gaffer::Context *context, const ScenePlug *parent ) const
{
	IECore::ConstCompoundObjectPtr globals = inPlug()->globalsPlug()->getValue();
	return computeProcessedGlobals( context, globals );
}
<commit_msg>GlobalsProcessor : Add pass-through for `childBoundsPlug()`<commit_after>//////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2012, John Haddon. All rights reserved.
//  Copyright (c) 2013, 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 John Haddon 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 "GafferScene/GlobalsProcessor.h"

using namespace Gaffer;
using namespace GafferScene;

GAFFER_GRAPHCOMPONENT_DEFINE_TYPE( GlobalsProcessor );

GlobalsProcessor::GlobalsProcessor( const std::string &name )
	:	SceneProcessor( name )
{
	// Fast pass-throughs for everything except the globals
	outPlug()->boundPlug()->setInput( inPlug()->boundPlug() );
	outPlug()->transformPlug()->setInput( inPlug()->transformPlug() );
	outPlug()->attributesPlug()->setInput( inPlug()->attributesPlug() );
	outPlug()->objectPlug()->setInput( inPlug()->objectPlug() );
	outPlug()->childNamesPlug()->setInput( inPlug()->childNamesPlug() );
	outPlug()->setNamesPlug()->setInput( inPlug()->setNamesPlug() );
	outPlug()->setPlug()->setInput( inPlug()->setPlug() );
	outPlug()->childBoundsPlug()->setInput( inPlug()->childBoundsPlug() );
}

GlobalsProcessor::~GlobalsProcessor()
{
}

void GlobalsProcessor::affects( const Plug *input, AffectedPlugsContainer &outputs ) const
{
	SceneProcessor::affects( input, outputs );

	if( input == inPlug()->globalsPlug() )
	{
		outputs.push_back( outPlug()->globalsPlug() );
	}
}

void GlobalsProcessor::hashGlobals( const Gaffer::Context *context, const ScenePlug *parent, IECore::MurmurHash &h ) const
{
	SceneProcessor::hashGlobals( context, parent, h );
	inPlug()->globalsPlug()->hash( h );
	hashProcessedGlobals( context, h );
}

IECore::ConstCompoundObjectPtr GlobalsProcessor::computeGlobals( const Gaffer::Context *context, const ScenePlug *parent ) const
{
	IECore::ConstCompoundObjectPtr globals = inPlug()->globalsPlug()->getValue();
	return computeProcessedGlobals( context, globals );
}
<|endoftext|>


<commit_before><commit_msg>some fixes<commit_after><|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkTestingMacros.h>
#include <mitkTestFixture.h>
#include "mitkIOUtil.h"
#include <cmath>

#include <mitkGIFGreyLevelSizeZone.h>

class mitkGIFGreyLevelSizeZoneTestSuite : public mitk::TestFixture
{
  CPPUNIT_TEST_SUITE(mitkGIFGreyLevelSizeZoneTestSuite );

  MITK_TEST(ImageDescription_PhantomTest_3D);
  MITK_TEST(ImageDescription_PhantomTest_2D);

  CPPUNIT_TEST_SUITE_END();

private:
  mitk::Image::Pointer m_IBSI_Phantom_Image_Small;
  mitk::Image::Pointer m_IBSI_Phantom_Image_Large;
  mitk::Image::Pointer m_IBSI_Phantom_Mask_Small;
  mitk::Image::Pointer m_IBSI_Phantom_Mask_Large;

public:

  void setUp(void) override
  {
    m_IBSI_Phantom_Image_Small = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Small.nrrd"));
    m_IBSI_Phantom_Image_Large = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Large.nrrd"));
    m_IBSI_Phantom_Mask_Small = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Small.nrrd"));
    m_IBSI_Phantom_Mask_Large = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Large.nrrd"));
  }

  void ImageDescription_PhantomTest_3D()
  {
    mitk::GIFGreyLevelSizeZone::Pointer featureCalculator = mitk::GIFGreyLevelSizeZone::New();

    featureCalculator->SetUseBinsize(true);
    featureCalculator->SetBinsize(1.0);
    featureCalculator->SetUseMinimumIntensity(true);
    featureCalculator->SetUseMaximumIntensity(true);
    featureCalculator->SetMinimumIntensity(0.5);
    featureCalculator->SetMaximumIntensity(6.5);

    auto featureList = featureCalculator->CalculateFeatures(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large);

    std::map<std::string, double> results;
    for (auto valuePair : featureList)
    {
      MITK_INFO << valuePair.first << " : " << valuePair.second;
      results[valuePair.first] = valuePair.second;
    }
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 18 features.", std::size_t(18), featureList.size());

    // These values are obtained with IBSI
    // Standard accuracy is 0.01
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone Emphasis with Large IBSI Phantom Image", 0.255, results["Grey Level Size Zone::Small Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone Emphasis with Large IBSI Phantom Image", 550, results["Grey Level Size Zone::Large Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Low Grey Level Emphasis with Large IBSI Phantom Image", 0.253, results["Grey Level Size Zone::Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::High Grey Level Emphasis with Large IBSI Phantom Image", 15.6, results["Grey Level Size Zone::High Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 0.0256, results["Grey Level Size Zone::Small Zone Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone High Grey Level Emphasis with Large IBSI Phantom Image", 2.76, results["Grey Level Size Zone::Small Zone High Grey Level Emphasis"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 503, results["Grey Level Size Zone::Large Zone Low Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone High Grey Level Emphasis with Large IBSI Phantom Image", 1495, results["Grey Level Size Zone::Large Zone High Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Non-Uniformity with Large IBSI Phantom Image", 1.4, results["Grey Level Size Zone::Grey Level Non-Uniformity"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Non-Uniformity Normalized with Large IBSI Phantom Image", 0.28, results["Grey Level Size Zone::Grey Level Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Non-Uniformity with Large IBSI Phantom Image", 1, results["Grey Level Size Zone::Zone Size Non-Uniformity"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Non-Uniformity Normalized with Large IBSI Phantom Image", 0.2, results["Grey Level Size Zone::Zone Size Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Percentage with Large IBSI Phantom Image", 0.0676, results["Grey Level Size Zone::Zone Percentage"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Variance with Large IBSI Phantom Image", 2.64, results["Grey Level Size Zone::Grey Level Variance"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Variance with Large IBSI Phantom Image", 331,   results["Grey Level Size Zone::Zone Size Variance"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Entropy with Large IBSI Phantom Image", 2.32, results["Grey Level Size Zone::Zone Size Entropy"], 0.01);
    //CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone:: with Large IBSI Phantom Image", 0.045, results["Grey Level Size Zone::"], 0.001);

    // These values are obtained by manually running the tool
    // Values might be wrong.
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Mean with Large IBSI Phantom Image", 3.6, results["Grey Level Size Zone::Grey Level Mean"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Mean with Large IBSI Phantom Image", 14.8, results["Grey Level Size Zone::Zone Size Mean"], 0.001);
  }

  void ImageDescription_PhantomTest_2D()
  {
    mitk::GIFGreyLevelSizeZone::Pointer featureCalculator = mitk::GIFGreyLevelSizeZone::New();

    featureCalculator->SetUseBinsize(true);
    featureCalculator->SetBinsize(1.0);
    featureCalculator->SetUseMinimumIntensity(true);
    featureCalculator->SetUseMaximumIntensity(true);
    featureCalculator->SetMinimumIntensity(0.5);
    featureCalculator->SetMaximumIntensity(6.5);

    auto featureList = featureCalculator->CalculateFeaturesSlicewise(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large, 2);

    std::map<std::string, double> results;
    for (auto valuePair : featureList)
    {
      MITK_INFO << valuePair.first << " : " << valuePair.second;
      results[valuePair.first] = valuePair.second;
    }
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 108 features.", std::size_t(108), featureList.size());

    // These values are obtained with IBSI
    // Standard accuracy is 0.01
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone Emphasis with Large IBSI Phantom Image", 0.363, results["SliceWise Mean Grey Level Size Zone::Small Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone Emphasis with Large IBSI Phantom Image", 43.9, results["SliceWise Mean Grey Level Size Zone::Large Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Low Grey Level Emphasis with Large IBSI Phantom Image", 0.371, results["SliceWise Mean Grey Level Size Zone::Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::High Grey Level Emphasis with Large IBSI Phantom Image", 16.4, results["SliceWise Mean Grey Level Size Zone::High Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 0.0259, results["SliceWise Mean Grey Level Size Zone::Small Zone Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone High Grey Level Emphasis with Large IBSI Phantom Image", 10.3, results["SliceWise Mean Grey Level Size Zone::Small Zone High Grey Level Emphasis"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 40.4, results["SliceWise Mean Grey Level Size Zone::Large Zone Low Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone High Grey Level Emphasis with Large IBSI Phantom Image", 113, results["SliceWise Mean Grey Level Size Zone::Large Zone High Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity with Large IBSI Phantom Image", 1.41, results["SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity Normalized with Large IBSI Phantom Image", 0.323, results["SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity with Large IBSI Phantom Image", 1.49, results["SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity Normalized with Large IBSI Phantom Image", 0.333, results["SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Percentage with Large IBSI Phantom Image", 0.24, results["SliceWise Mean Grey Level Size Zone::Zone Percentage"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Variance with Large IBSI Phantom Image", 3.97, results["SliceWise Mean Grey Level Size Zone::Grey Level Variance"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Variance with Large IBSI Phantom Image", 21, results["SliceWise Mean Grey Level Size Zone::Zone Size Variance"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Entropy with Large IBSI Phantom Image", 1.93, results["SliceWise Mean Grey Level Size Zone::Zone Size Entropy"], 0.01);
    //CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone:: with Large IBSI Phantom Image", 0.045, results["SliceWise Mean Grey Level Size Zone::"], 0.001);

    // These values are obtained by manually running the tool
    // Values might be wrong.
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Mean with Large IBSI Phantom Image", 3.6, results["SliceWise Mean Grey Level Size Zone::Grey Level Mean"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Mean with Large IBSI Phantom Image", 14.8, results["SliceWise Mean Grey Level Size Zone::Zone Size Mean"], 0.001);
  }

};

MITK_TEST_SUITE_REGISTRATION(mitkGIFGreyLevelSizeZone )<commit_msg>Updated test tolerances<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include <mitkTestingMacros.h>
#include <mitkTestFixture.h>
#include "mitkIOUtil.h"
#include <cmath>

#include <mitkGIFGreyLevelSizeZone.h>

class mitkGIFGreyLevelSizeZoneTestSuite : public mitk::TestFixture
{
  CPPUNIT_TEST_SUITE(mitkGIFGreyLevelSizeZoneTestSuite );

  MITK_TEST(ImageDescription_PhantomTest_3D);
  MITK_TEST(ImageDescription_PhantomTest_2D);

  CPPUNIT_TEST_SUITE_END();

private:
  mitk::Image::Pointer m_IBSI_Phantom_Image_Small;
  mitk::Image::Pointer m_IBSI_Phantom_Image_Large;
  mitk::Image::Pointer m_IBSI_Phantom_Mask_Small;
  mitk::Image::Pointer m_IBSI_Phantom_Mask_Large;

public:

  void setUp(void) override
  {
    m_IBSI_Phantom_Image_Small = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Small.nrrd"));
    m_IBSI_Phantom_Image_Large = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Image_Large.nrrd"));
    m_IBSI_Phantom_Mask_Small = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Small.nrrd"));
    m_IBSI_Phantom_Mask_Large = mitk::IOUtil::LoadImage(GetTestDataFilePath("Radiomics/IBSI_Phantom_Mask_Large.nrrd"));
  }

  void ImageDescription_PhantomTest_3D()
  {
    mitk::GIFGreyLevelSizeZone::Pointer featureCalculator = mitk::GIFGreyLevelSizeZone::New();

    featureCalculator->SetUseBinsize(true);
    featureCalculator->SetBinsize(1.0);
    featureCalculator->SetUseMinimumIntensity(true);
    featureCalculator->SetUseMaximumIntensity(true);
    featureCalculator->SetMinimumIntensity(0.5);
    featureCalculator->SetMaximumIntensity(6.5);

    auto featureList = featureCalculator->CalculateFeatures(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large);

    std::map<std::string, double> results;
    for (auto valuePair : featureList)
    {
      MITK_INFO << valuePair.first << " : " << valuePair.second;
      results[valuePair.first] = valuePair.second;
    }
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 18 features.", std::size_t(18), featureList.size());

    // These values are obtained with IBSI
    // Standard accuracy is 0.01
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone Emphasis with Large IBSI Phantom Image", 0.255, results["Grey Level Size Zone::Small Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone Emphasis with Large IBSI Phantom Image", 550, results["Grey Level Size Zone::Large Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Low Grey Level Emphasis with Large IBSI Phantom Image", 0.253, results["Grey Level Size Zone::Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::High Grey Level Emphasis with Large IBSI Phantom Image", 15.6, results["Grey Level Size Zone::High Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 0.0256, results["Grey Level Size Zone::Small Zone Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Small Zone High Grey Level Emphasis with Large IBSI Phantom Image", 2.76, results["Grey Level Size Zone::Small Zone High Grey Level Emphasis"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 503, results["Grey Level Size Zone::Large Zone Low Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Large Zone High Grey Level Emphasis with Large IBSI Phantom Image", 1495, results["Grey Level Size Zone::Large Zone High Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Non-Uniformity with Large IBSI Phantom Image", 1.4, results["Grey Level Size Zone::Grey Level Non-Uniformity"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Non-Uniformity Normalized with Large IBSI Phantom Image", 0.28, results["Grey Level Size Zone::Grey Level Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Non-Uniformity with Large IBSI Phantom Image", 1, results["Grey Level Size Zone::Zone Size Non-Uniformity"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Non-Uniformity Normalized with Large IBSI Phantom Image", 0.2, results["Grey Level Size Zone::Zone Size Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Percentage with Large IBSI Phantom Image", 0.0676, results["Grey Level Size Zone::Zone Percentage"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Variance with Large IBSI Phantom Image", 2.64, results["Grey Level Size Zone::Grey Level Variance"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Variance with Large IBSI Phantom Image", 331,   results["Grey Level Size Zone::Zone Size Variance"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Entropy with Large IBSI Phantom Image", 2.32, results["Grey Level Size Zone::Zone Size Entropy"], 0.01);
    //CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone:: with Large IBSI Phantom Image", 0.045, results["Grey Level Size Zone::"], 0.001);

    // These values are obtained by manually running the tool
    // Values might be wrong.
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Grey Level Mean with Large IBSI Phantom Image", 3.6, results["Grey Level Size Zone::Grey Level Mean"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Grey Level Size Zone::Zone Size Mean with Large IBSI Phantom Image", 14.8, results["Grey Level Size Zone::Zone Size Mean"], 0.001);
  }

  void ImageDescription_PhantomTest_2D()
  {
    mitk::GIFGreyLevelSizeZone::Pointer featureCalculator = mitk::GIFGreyLevelSizeZone::New();

    featureCalculator->SetUseBinsize(true);
    featureCalculator->SetBinsize(1.0);
    featureCalculator->SetUseMinimumIntensity(true);
    featureCalculator->SetUseMaximumIntensity(true);
    featureCalculator->SetMinimumIntensity(0.5);
    featureCalculator->SetMaximumIntensity(6.5);

    auto featureList = featureCalculator->CalculateFeaturesSlicewise(m_IBSI_Phantom_Image_Large, m_IBSI_Phantom_Mask_Large, 2);

    std::map<std::string, double> results;
    for (auto valuePair : featureList)
    {
      MITK_INFO << valuePair.first << " : " << valuePair.second;
      results[valuePair.first] = valuePair.second;
    }
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Image Diagnostics should calculate 108 features.", std::size_t(108), featureList.size());

    // These values are obtained with IBSI
    // Standard accuracy is 0.01
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone Emphasis with Large IBSI Phantom Image", 0.363, results["SliceWise Mean Grey Level Size Zone::Small Zone Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone Emphasis with Large IBSI Phantom Image", 43.9, results["SliceWise Mean Grey Level Size Zone::Large Zone Emphasis"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Low Grey Level Emphasis with Large IBSI Phantom Image", 0.371, results["SliceWise Mean Grey Level Size Zone::Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::High Grey Level Emphasis with Large IBSI Phantom Image", 16.4, results["SliceWise Mean Grey Level Size Zone::High Grey Level Emphasis"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 0.0259, results["SliceWise Mean Grey Level Size Zone::Small Zone Low Grey Level Emphasis"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Small Zone High Grey Level Emphasis with Large IBSI Phantom Image", 10.3, results["SliceWise Mean Grey Level Size Zone::Small Zone High Grey Level Emphasis"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone Low Grey Level Emphasis with Large IBSI Phantom Image", 40.4, results["SliceWise Mean Grey Level Size Zone::Large Zone Low Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Large Zone High Grey Level Emphasis with Large IBSI Phantom Image", 113, results["SliceWise Mean Grey Level Size Zone::Large Zone High Grey Level Emphasis"], 1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity with Large IBSI Phantom Image", 1.41, results["SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity Normalized with Large IBSI Phantom Image", 0.323, results["SliceWise Mean Grey Level Size Zone::Grey Level Non-Uniformity Normalized"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity with Large IBSI Phantom Image", 1.49, results["SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity Normalized with Large IBSI Phantom Image", 0.333, results["SliceWise Mean Grey Level Size Zone::Zone Size Non-Uniformity Normalized"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Percentage with Large IBSI Phantom Image", 0.24, results["SliceWise Mean Grey Level Size Zone::Zone Percentage"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Variance with Large IBSI Phantom Image", 3.97, results["SliceWise Mean Grey Level Size Zone::Grey Level Variance"], 0.01);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Variance with Large IBSI Phantom Image", 21, results["SliceWise Mean Grey Level Size Zone::Zone Size Variance"], 0.1);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Entropy with Large IBSI Phantom Image", 1.93, results["SliceWise Mean Grey Level Size Zone::Zone Size Entropy"], 0.01);
    //CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone:: with Large IBSI Phantom Image", 0.045, results["SliceWise Mean Grey Level Size Zone::"], 0.001);

    // These values are obtained by manually running the tool
    // Values might be wrong.
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Grey Level Mean with Large IBSI Phantom Image", 3.526, results["SliceWise Mean Grey Level Size Zone::Grey Level Mean"], 0.001);
    CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("SliceWise Mean Grey Level Size Zone::Zone Size Mean with Large IBSI Phantom Image", 4.59524, results["SliceWise Mean Grey Level Size Zone::Zone Size Mean"], 0.001);
  }

};

MITK_TEST_SUITE_REGISTRATION(mitkGIFGreyLevelSizeZone )<|endoftext|>


<commit_before>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_border_color(MGLSampler * self, PyObject * value) {
    PyObject * border_color = PySequence_Fast(value, "not iterable");
    if (!border_color) {
        return -1;
    }
    float color[4] = {};
    switch (PySequence_Fast_GET_SIZE(border_color)) {
        case 4:
            color[3] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 3));
        case 3:
            color[2] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 2));
        case 2:
            color[1] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 1));
        case 1:
            color[0] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border_color, 0));
        default:
            // TODO: error
            return -1;
    }

	self->context->gl.SamplerParameterfv(self->sampler_obj, GL_TEXTURE_BORDER_COLOR, color);
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
	float anisotropy = (float)PyFloat_AsDouble(value);
    if (anisotropy < 1.0) {
        anisotropy = 1.0;
    }
    // if (anisotropy > max_anisotropy) {
    //     anisotropy = max_anisotropy;
    // }
    // SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
    return 0;
}

int MGLSampler_set_lod_range(MGLSampler * self, PyObject * value) {
    PyObject * lod_range = PySequence_Fast(value, "not iterable");
    if (!lod_range) {
        return -1;
    }

    int min_lod;
    int max_lod;
    float lod_bias = 0.0f;
    switch (PySequence_Fast_GET_SIZE(lod_range)) {
        case 3:
            lod_bias = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(lod_range, 2));
        case 2:
            min_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 0));
            max_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 1));
        default:
            // TODO: error
            return -1;
    }

    // SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, max_lod);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_LOD_BIAS, lod_bias);
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"border_color", 0, (setter)MGLSampler_set_border_color, 0, 0},
    {"lod_range", 0, (setter)MGLSampler_set_lod_range, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<commit_msg>sampler properties<commit_after>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_border(MGLSampler * self, PyObject * value) {
    PyObject * border = PySequence_Fast(value, "not iterable");
    if (!border) {
        return -1;
    }

    float color[4] = {};

    switch (PySequence_Fast_GET_SIZE(border)) {
        case 4:
            color[3] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border, 3));
        case 3:
            color[2] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border, 2));
        case 2:
            color[1] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border, 1));
        case 1:
            color[0] = (float)PyFloat_AsDouble(PySequence_Fast_GET_ITEM(border, 0));
        default:
            Py_DECREF(border);
            // TODO: error
            return -1;
    }

    Py_DECREF(border);

	self->context->gl.SamplerParameterfv(self->sampler_obj, GL_TEXTURE_BORDER_COLOR, color);
    Py_XSETREF(SLOT(self->wrapper, PyObject, Sampler_class_border), float_tuple(color[0], color[1], color[2], color[3]));
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
	float anisotropy = (float)PyFloat_AsDouble(value);
    if (anisotropy < 1.0) {
        anisotropy = 1.0;
    }
    // TODO: clamp with max value
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
    Py_XSETREF(SLOT(self->wrapper, PyObject, Sampler_class_anisotropy), PyFloat_FromDouble(anisotropy));
    return 0;
}

int MGLSampler_set_lod_range(MGLSampler * self, PyObject * value) {
    PyObject * lod_range = PySequence_Fast(value, "not iterable");
    if (!lod_range) {
        return -1;
    }

    if (PySequence_Fast_GET_SIZE(lod_range) != 2) {
        Py_DECREF(lod_range);
        // TODO: error
        return -1;
    }

    int min_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 0));
    int max_lod = PyLong_AsLong(PySequence_Fast_GET_ITEM(lod_range, 1));
    Py_DECREF(lod_range);

	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, max_lod);
    Py_XSETREF(SLOT(self->wrapper, PyObject, Sampler_class_lod_range), int_tuple(min_lod, max_lod));
    return 0;
}

int MGLSampler_set_lod_bias(MGLSampler * self, PyObject * value) {
	float lod_bias = (float)PyFloat_AsDouble(value);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_LOD_BIAS, lod_bias);
    Py_XSETREF(SLOT(self->wrapper, PyObject, Sampler_class_lod_bias), PyFloat_FromDouble(lod_bias));
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"border", 0, (setter)MGLSampler_set_border, 0, 0},
    {"lod_range", 0, (setter)MGLSampler_set_lod_range, 0, 0},
    {"lod_bias", 0, (setter)MGLSampler_set_lod_bias, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<|endoftext|>


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

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "mitkContourModelLiveWireInteractor.h"

#include "mitkInteractionPositionEvent.h"
#include "mitkToolManager.h"

#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"

#include <mitkInteractionConst.h>

#include "mitkIOUtil.h"

mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor()
{
  m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();
  m_LiveWireFilter->SetUseCostFunction(true);
  m_NextActiveVertexDown.Fill(0);
  m_NextActiveVertexUp.Fill(0);
}

mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}

void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions()
{
  CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick);
  CONNECT_CONDITION("mouseMove", IsHovering);

  CONNECT_FUNCTION("movePoint", OnMovePoint);
  CONNECT_FUNCTION("deletePoint", OnDeletePoint);
  CONNECT_FUNCTION("addPoint", OnAddPoint)
  CONNECT_FUNCTION("finish", OnFinishEditing);
}

bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent)
{
  auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent);

  if (isVertexSelected)
  {
    auto* contour = dynamic_cast<mitk::ContourModel*>(this->GetDataNode()->GetData());
    const auto* positionEvent =
      dynamic_cast<const mitk::InteractionPositionEvent*>(interactionEvent);
    mitk::Point3D click = positionEvent->GetPositionInWorld();
    const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());

    auto controlVertices = contour->GetControlVertices(timeStep);
    const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
    const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
    this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep);
    m_NextActiveVertexUp = nextPoint->Coordinates;
    m_NextActiveVertexDown = previousPoint->Coordinates;

    // clear previous void positions
    this->m_LiveWireFilter->ClearRepulsivePoints();

    // all points in lower and upper part should be marked as repulsive points to not be changed
    this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep);
    this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep);

    // clear container with void points between neighboring control points
    m_ContourBeingModified.clear();
  }
  return isVertexSelected;
}

void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg)
{
  if (this->m_WorkingSlice != _arg)
  {
    this->m_WorkingSlice = _arg;
    this->m_LiveWireFilter->SetInput(this->m_WorkingSlice);
  }
}

void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent)
{
  Superclass::OnAddPoint(sm, interactionEvent);
}

void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
  const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());

  auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
  if (contour == nullptr)
  {
    MITK_ERROR << "Invalid Contour!";
    return;
  }

  if (contour->GetSelectedVertex())
  {
    mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
    newContour->Expand(contour->GetTimeSteps());
    newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

    newContour->Concatenate(m_ContourLeft, timeStep);

    // recompute contour between neighbored two active control points
    this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
    this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp);
    this->m_LiveWireFilter->Update();

    mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput();
    assert(liveWireContour);

    if (liveWireContour->IsEmpty(timeStep))
      return;

    liveWireContour->RemoveVertexAt(0, timeStep);
    liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep);

    // insert new live wire computed points
    newContour->Concatenate(liveWireContour, timeStep);

    // insert right side of original contour
    newContour->Concatenate(this->m_ContourRight, timeStep);

    newContour->SetClosed(contour->IsClosed(timeStep), timeStep);

    // instead of leaving a single point, delete all points
    if (newContour->GetNumberOfVertices(timeStep) <= 2)
    {
      newContour->Clear(timeStep);
    }

    this->GetDataNode()->SetData(newContour);

    mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow());
  }
}

void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
  const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
  if (!positionEvent)
    return;

  const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
  mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();

  auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
  if (contour == nullptr)
  {
    MITK_ERROR << "invalid contour";
    return;
  }

  std::cout << currentPosition << std::endl;

  mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New();
  editingContour->Expand(contour->GetTimeSteps());
  editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

  // recompute left live wire, i.e. the contour between previous active vertex and selected vertex
  this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
  this->m_LiveWireFilter->SetEndPoint(currentPosition);

  // remove void positions between previous active vertex and next active vertex.
  if (!m_ContourBeingModified.empty())
  {
    std::vector<itk::Index<2>>::const_iterator iter = m_ContourBeingModified.begin();
    for (; iter != m_ContourBeingModified.end(); iter++)
    {
      this->m_LiveWireFilter->RemoveRepulsivePoint((*iter));
    }
  }

  // update to get the left livewire. Remember that the points in the rest of the contour are already
  // set as void positions in the filter
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput();
  assert(leftLiveWire);

  if (!leftLiveWire->IsEmpty(timeStep))
    leftLiveWire->RemoveVertexAt(0, timeStep);

  editingContour->Concatenate(leftLiveWire, timeStep);

  // the new index of the selected vertex
  unsigned int selectedVertexIndex =
    this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1;

  // at this point the container has to be empty
  m_ContourBeingModified.clear();

  // add points from left live wire contour
  auto iter = leftLiveWire->IteratorBegin(timeStep);
  for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++)
  {
    itk::Index<2> idx;
    this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
    this->m_LiveWireFilter->AddRepulsivePoint(idx);

    // add indices
    m_ContourBeingModified.push_back(idx);
  }

  // recompute right live wire, i.e. the contour between selected vertex and next active vertex
  this->m_LiveWireFilter->SetStartPoint(currentPosition);
  this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp);

  // update filter with all contour points set as void but the right live wire portion to be calculated now
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput();
  assert(rightLiveWire);

  if (!leftLiveWire->IsEmpty(timeStep))
    leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep);

  if (!rightLiveWire->IsEmpty(timeStep))
    rightLiveWire->RemoveVertexAt(0, timeStep);

  editingContour->Concatenate(rightLiveWire, timeStep);

  mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
  newContour->Expand(contour->GetTimeSteps());
  newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

  // concatenate left original contour
  newContour->Concatenate(this->m_ContourLeft, timeStep);

  newContour->Concatenate(editingContour, timeStep, true);

  // set last inserted vertex as selected
  newContour->SelectVertexAt(selectedVertexIndex, timeStep);

  // set as control point
  newContour->SetSelectedVertexAsControlPoint(true);

  // concatenate right original contour
  newContour->Concatenate(this->m_ContourRight, timeStep);

  newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
  this->GetDataNode()->SetData(newContour);

  mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}

void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour,
                                                                          const mitk::ContourModel::VertexType *nextPoint,
                                                                          const mitk::ContourModel::VertexType *previousPoint,
                                                                          int timeStep)
{
  m_ContourLeft = mitk::ContourModel::New();
  m_ContourRight = mitk::ContourModel::New();

  auto it = srcContour->IteratorBegin();
  // part between nextPoint and end of Countour
  bool upperPart = false;
  // part between start of countour and previousPoint
  bool lowerPart = true;

  // edge cases when point right before first control vertex is selected or first control vertex is selected
  if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it))
  {
    upperPart = true;
    lowerPart = false;
    m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep);
  }
  // if first control vertex is selected, move to next point before adding vertices to m_ContourRight
  // otherwise, second line appears when moving the vertex
  if (srcContour->GetSelectedVertex() == (*it))
  {
    while (*it != nextPoint)
    {
      it++;
    }
  }

  for (; it != srcContour->IteratorEnd(timeStep); it++)
  {
    // everything in lower part should be added to m_CountoutLeft
    if (lowerPart)
    {
      m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
    }
    // start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight
    if (*it == previousPoint)
    {
      lowerPart = false;
      upperPart = false;
    }
    // start of upperPart
    if (*it == nextPoint)
    {
      upperPart = true;
    }
    // everything in upper part should be added to m_CountoutRight
    if (upperPart)
    {
      m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
    }
  }
}

void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude,
                                                              mitk::ContourModel *contour,
                                                              int timeStep)
{
  auto it = contour->IteratorBegin();
  for (; it != contour->IteratorEnd(timeStep); it++)
  {
    if (*it != pointToExclude)
    {
      itk::Index<2> idx;
      this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx);
      this->m_LiveWireFilter->AddRepulsivePoint(idx);
    }
  }
}

void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *)
{

}
<commit_msg>Remove output from changing livewire points<commit_after>/*============================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center (DKFZ)
All rights reserved.

Use of this source code is governed by a 3-clause BSD license that can be
found in the LICENSE file.

============================================================================*/

#include "mitkContourModelLiveWireInteractor.h"

#include "mitkInteractionPositionEvent.h"
#include "mitkToolManager.h"

#include "mitkBaseRenderer.h"
#include "mitkRenderingManager.h"

#include <mitkInteractionConst.h>

#include "mitkIOUtil.h"

mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor() : ContourModelInteractor()
{
  m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New();
  m_LiveWireFilter->SetUseCostFunction(true);
  m_NextActiveVertexDown.Fill(0);
  m_NextActiveVertexUp.Fill(0);
}

mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor()
{
}

void mitk::ContourModelLiveWireInteractor::ConnectActionsAndFunctions()
{
  CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick);
  CONNECT_CONDITION("mouseMove", IsHovering);

  CONNECT_FUNCTION("movePoint", OnMovePoint);
  CONNECT_FUNCTION("deletePoint", OnDeletePoint);
  CONNECT_FUNCTION("addPoint", OnAddPoint)
  CONNECT_FUNCTION("finish", OnFinishEditing);
}

bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent)
{
  auto isVertexSelected = Superclass::OnCheckPointClick(interactionEvent);

  if (isVertexSelected)
  {
    auto* contour = dynamic_cast<mitk::ContourModel*>(this->GetDataNode()->GetData());
    const auto* positionEvent =
      dynamic_cast<const mitk::InteractionPositionEvent*>(interactionEvent);
    mitk::Point3D click = positionEvent->GetPositionInWorld();
    const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());

    auto controlVertices = contour->GetControlVertices(timeStep);
    const mitk::ContourModel::VertexType* nextPoint = contour->GetNextControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
    const mitk::ContourModel::VertexType* previousPoint = contour->GetPreviousControlVertexAt(click, mitk::ContourModelLiveWireInteractor::eps, timeStep);
    this->SplitContourFromSelectedVertex(contour, nextPoint, previousPoint, timeStep);
    m_NextActiveVertexUp = nextPoint->Coordinates;
    m_NextActiveVertexDown = previousPoint->Coordinates;

    // clear previous void positions
    this->m_LiveWireFilter->ClearRepulsivePoints();

    // all points in lower and upper part should be marked as repulsive points to not be changed
    this->SetRepulsivePoints(previousPoint, m_ContourLeft, timeStep);
    this->SetRepulsivePoints(nextPoint, m_ContourRight, timeStep);

    // clear container with void points between neighboring control points
    m_ContourBeingModified.clear();
  }
  return isVertexSelected;
}

void mitk::ContourModelLiveWireInteractor::SetWorkingImage(mitk::Image *_arg)
{
  if (this->m_WorkingSlice != _arg)
  {
    this->m_WorkingSlice = _arg;
    this->m_LiveWireFilter->SetInput(this->m_WorkingSlice);
  }
}

void mitk::ContourModelLiveWireInteractor::OnAddPoint(StateMachineAction* sm, InteractionEvent* interactionEvent)
{
  Superclass::OnAddPoint(sm, interactionEvent);
}

void mitk::ContourModelLiveWireInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
  const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());

  auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
  if (contour == nullptr)
  {
    MITK_ERROR << "Invalid Contour!";
    return;
  }

  if (contour->GetSelectedVertex())
  {
    mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
    newContour->Expand(contour->GetTimeSteps());
    newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

    newContour->Concatenate(m_ContourLeft, timeStep);

    // recompute contour between neighbored two active control points
    this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
    this->m_LiveWireFilter->SetEndPoint(this->m_NextActiveVertexUp);
    this->m_LiveWireFilter->Update();

    mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput();
    assert(liveWireContour);

    if (liveWireContour->IsEmpty(timeStep))
      return;

    liveWireContour->RemoveVertexAt(0, timeStep);
    liveWireContour->RemoveVertexAt(liveWireContour->GetNumberOfVertices(timeStep) - 1, timeStep);

    // insert new live wire computed points
    newContour->Concatenate(liveWireContour, timeStep);

    // insert right side of original contour
    newContour->Concatenate(this->m_ContourRight, timeStep);

    newContour->SetClosed(contour->IsClosed(timeStep), timeStep);

    // instead of leaving a single point, delete all points
    if (newContour->GetNumberOfVertices(timeStep) <= 2)
    {
      newContour->Clear(timeStep);
    }

    this->GetDataNode()->SetData(newContour);

    mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow());
  }
}

void mitk::ContourModelLiveWireInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent)
{
  const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent);
  if (!positionEvent)
    return;

  const auto timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData());
  mitk::Point3D currentPosition = positionEvent->GetPositionInWorld();

  auto *contour = dynamic_cast<mitk::ContourModel *>(this->GetDataNode()->GetData());
  if (contour == nullptr)
  {
    MITK_ERROR << "invalid contour";
    return;
  }

  mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New();
  editingContour->Expand(contour->GetTimeSteps());
  editingContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

  // recompute left live wire, i.e. the contour between previous active vertex and selected vertex
  this->m_LiveWireFilter->SetStartPoint(this->m_NextActiveVertexDown);
  this->m_LiveWireFilter->SetEndPoint(currentPosition);

  // remove void positions between previous active vertex and next active vertex.
  if (!m_ContourBeingModified.empty())
  {
    std::vector<itk::Index<2>>::const_iterator iter = m_ContourBeingModified.begin();
    for (; iter != m_ContourBeingModified.end(); iter++)
    {
      this->m_LiveWireFilter->RemoveRepulsivePoint((*iter));
    }
  }

  // update to get the left livewire. Remember that the points in the rest of the contour are already
  // set as void positions in the filter
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput();
  assert(leftLiveWire);

  if (!leftLiveWire->IsEmpty(timeStep))
    leftLiveWire->RemoveVertexAt(0, timeStep);

  editingContour->Concatenate(leftLiveWire, timeStep);

  // the new index of the selected vertex
  unsigned int selectedVertexIndex =
    this->m_ContourLeft->GetNumberOfVertices(timeStep) + leftLiveWire->GetNumberOfVertices(timeStep) - 1;

  // at this point the container has to be empty
  m_ContourBeingModified.clear();

  // add points from left live wire contour
  auto iter = leftLiveWire->IteratorBegin(timeStep);
  for (; iter != leftLiveWire->IteratorEnd(timeStep); iter++)
  {
    itk::Index<2> idx;
    this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx);
    this->m_LiveWireFilter->AddRepulsivePoint(idx);

    // add indices
    m_ContourBeingModified.push_back(idx);
  }

  // recompute right live wire, i.e. the contour between selected vertex and next active vertex
  this->m_LiveWireFilter->SetStartPoint(currentPosition);
  this->m_LiveWireFilter->SetEndPoint(m_NextActiveVertexUp);

  // update filter with all contour points set as void but the right live wire portion to be calculated now
  this->m_LiveWireFilter->Update();

  mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput();
  assert(rightLiveWire);

  if (!leftLiveWire->IsEmpty(timeStep))
    leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices() - 1, timeStep);

  if (!rightLiveWire->IsEmpty(timeStep))
    rightLiveWire->RemoveVertexAt(0, timeStep);

  editingContour->Concatenate(rightLiveWire, timeStep);

  mitk::ContourModel::Pointer newContour = mitk::ContourModel::New();
  newContour->Expand(contour->GetTimeSteps());
  newContour->SetTimeGeometry(contour->GetTimeGeometry()->Clone());

  // concatenate left original contour
  newContour->Concatenate(this->m_ContourLeft, timeStep);

  newContour->Concatenate(editingContour, timeStep, true);

  // set last inserted vertex as selected
  newContour->SelectVertexAt(selectedVertexIndex, timeStep);

  // set as control point
  newContour->SetSelectedVertexAsControlPoint(true);

  // concatenate right original contour
  newContour->Concatenate(this->m_ContourRight, timeStep);

  newContour->SetClosed(contour->IsClosed(timeStep), timeStep);
  this->GetDataNode()->SetData(newContour);

  mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow());
}

void mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel *srcContour,
                                                                          const mitk::ContourModel::VertexType *nextPoint,
                                                                          const mitk::ContourModel::VertexType *previousPoint,
                                                                          int timeStep)
{
  m_ContourLeft = mitk::ContourModel::New();
  m_ContourRight = mitk::ContourModel::New();

  auto it = srcContour->IteratorBegin();
  // part between nextPoint and end of Countour
  bool upperPart = false;
  // part between start of countour and previousPoint
  bool lowerPart = true;

  // edge cases when point right before first control vertex is selected or first control vertex is selected
  if (nextPoint == (*it) || srcContour->GetSelectedVertex() == (*it))
  {
    upperPart = true;
    lowerPart = false;
    m_ContourLeft->AddVertex(previousPoint->Coordinates, previousPoint->IsControlPoint, timeStep);
  }
  // if first control vertex is selected, move to next point before adding vertices to m_ContourRight
  // otherwise, second line appears when moving the vertex
  if (srcContour->GetSelectedVertex() == (*it))
  {
    while (*it != nextPoint)
    {
      it++;
    }
  }

  for (; it != srcContour->IteratorEnd(timeStep); it++)
  {
    // everything in lower part should be added to m_CountoutLeft
    if (lowerPart)
    {
      m_ContourLeft->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
    }
    // start of "restricted area" where no vertex should be added to m_CountoutLeft or m_CountoutRight
    if (*it == previousPoint)
    {
      lowerPart = false;
      upperPart = false;
    }
    // start of upperPart
    if (*it == nextPoint)
    {
      upperPart = true;
    }
    // everything in upper part should be added to m_CountoutRight
    if (upperPart)
    {
      m_ContourRight->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timeStep);
    }
  }
}

void mitk::ContourModelLiveWireInteractor::SetRepulsivePoints(const mitk::ContourModel::VertexType *pointToExclude,
                                                              mitk::ContourModel *contour,
                                                              int timeStep)
{
  auto it = contour->IteratorBegin();
  for (; it != contour->IteratorEnd(timeStep); it++)
  {
    if (*it != pointToExclude)
    {
      itk::Index<2> idx;
      this->m_WorkingSlice->GetGeometry()->WorldToIndex((*it)->Coordinates, idx);
      this->m_LiveWireFilter->AddRepulsivePoint(idx);
    }
  }
}

void mitk::ContourModelLiveWireInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *)
{

}
<|endoftext|>


<commit_before>/*
 * enlargeshrinkplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Antia Puentes <apuentes@igalia.com>
 *
 * This file is part of the Gallery Enlarge/Shrink Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "enlargeshrink.h"

#include <math.h>

#define FILTER_NAME_ENLARGESHRINK "com.igalia.enlargeshrink"
#define FORCE_OPTION "force"
#define FORCE_FACTOR 3.2

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

// Maximum "width" of a pixel, measured diagonally
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880168872420969808
#endif

#define FIXEDPOINT_FRACTIONBITS 8
#define FIXEDPOINT_MULTIPLIER (1 << FIXEDPOINT_FRACTIONBITS)

static double fraction(double n)
{
    return n - floor(n);
}

static int fixedfraction(double n)
{
    return (int) (fraction(n) * FIXEDPOINT_MULTIPLIER);
}

static QRgb weighpixel(QRgb rgb, int percentage)
{
    return qRgb((qRed(rgb)   * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2),
                (qGreen(rgb) * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2),
                (qBlue(rgb)  * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2));
}

/*
 * Gets a pixel at a floating-point coordinate, thus causing it to take into
 * accountance neighbour pixels if one of the coordinates have a fractional part
 *
 * The function will calculate the pixel value by calculating all areas the
 * pixel occupies in each neighbouring pixel.
 */
static QRgb getPixel(const QImage &img, double x, double y)
{
    int fx = fixedfraction(x);
    int fy = fixedfraction(y);

    int px = (int) x;
    int py = (int) y;

    int right = 1;
    if (px >= img.width() - 1) {
        right = 0;
    }

    int below = 1;
    if (py >= img.height() - 1) {
        below = 0;
    }

    QRgb p1 = weighpixel(img.pixel(px, py), (FIXEDPOINT_MULTIPLIER - fx) * (FIXEDPOINT_MULTIPLIER - fy));
    QRgb p2 = weighpixel(img.pixel(px + right, py), fx * (FIXEDPOINT_MULTIPLIER - fy));
    QRgb p3 = weighpixel(img.pixel(px, py + below), (FIXEDPOINT_MULTIPLIER - fx) * fy);
    QRgb p4 = weighpixel(img.pixel(px + right, py + below), fx * fy);

    return qRgb((qRed(p1) + qRed(p2) + qRed(p3) + qRed(p4)),
                (qGreen(p1) + qGreen(p2) + qGreen(p3) + qGreen(p4)),
                (qBlue(p1) + qBlue(p2) + qBlue(p3) + qBlue(p4)));

}

/**
 * Returns the enlarge/shrink effect for a given "n".
 * The function should be continous and should return 0 for x=0 and return 1 for
 * x = 1 if not, abrupt changes might appear in the image. The function only
 * needs to be valid for the interval [0..1].
 * \a amplitude should be in the interval [-0.3125, 0.3125], if it is outside
 * this interval the function might return negative values (which might give
 * interesting effects but not an enlarge/shrink effect)
 * A positive \a amplitude will give a "punch", negative a "pinch"
 *
 * Keeping the function linear (return x;) will in theory not change the image,
 * but due to rounding errors one might expect minor distortion.
 */
static double distort(double n, double amplitude)
{
    return n - sin(M_PI * n) * amplitude;
}

EnlargeShrink::EnlargeShrink() :
    m_Radius(0), m_Center(-1, -1), m_Force(0)
{
}

EnlargeShrink::~EnlargeShrink()
{
}

const QString EnlargeShrink::name() const
{
    return FILTER_NAME_ENLARGESHRINK;
}

const QStringList EnlargeShrink::supportedOptions() const
{
    QStringList supportedOptions;
    supportedOptions << QuillImageFilter::Radius
                     << QuillImageFilter::Center
                     << FORCE_OPTION;
    return supportedOptions;
}

bool EnlargeShrink::setOption(const QString &option, const QVariant &value)
{
    bool ok = false;

    if (option == QuillImageFilter::Radius) {
        double radius = value.toDouble(&ok);
        if (ok) {
            m_Radius = radius;
        }

    } else if (option == QuillImageFilter::Center) {
        QPoint center = value.toPoint();
        if (!center.isNull()) {
            m_Center = center;
            ok = true;
        }

    } else if (option == FORCE_OPTION) {
        double force = value.toDouble(&ok);
        ok = ok && force <= 1.0 && force >= -1.0;
        if (ok) {
            // Divide by the FORCE_FACTOR to get appropiated values for
            // the Amplitude used by the "distort" function
            m_Force = force/FORCE_FACTOR;
        }
    }

    return ok;
}

QVariant EnlargeShrink::option(const QString &option) const
{
    QVariant value;

    if (option == QuillImageFilter::Radius) {
        value = m_Radius;
    } else if (option == QuillImageFilter::Center) {
        value = m_Center;
    } else if (option == FORCE_OPTION) {
        value = m_Force * FORCE_FACTOR;
    }

    return value;
}

QuillImage EnlargeShrink::apply(const QuillImage& image) const
{
    if (image.fullImageSize().isEmpty()) {
        return QImage();
    }

    QuillImage out;
    enlargeShrink(image, &out);
    return out;
}

double EnlargeShrink::getRadius(const QuillImage &img) const
{
    double radius = m_Radius;
    if (!img.isFragment() || (img.width() == 170 && img.height() == 170)) {
        if (img.fullImageSize().width() < img.fullImageSize().height()) {
            radius = radius * img.width() / img.fullImageSize().width();
        } else {
            radius = radius * img.height() / img.fullImageSize().height();
        }
    }
    return radius;
}

QPoint EnlargeShrink::getCenter(const QuillImage &img) const
{
    QPoint center = m_Center;
    if (!img.isFragment() || (img.width() == 170 && img.height() == 170)) {
        center.setX(center.x() * img.width()  / img.fullImageSize().width());
        center.setY(center.y() * img.height() / img.fullImageSize().height());
    }
    return center;
}

bool EnlargeShrink::enlargeShrink(const QuillImage &img,
                                  QuillImage *outputImg) const
{
    *outputImg = img;

    double radius = getRadius(img);
    QPoint center = getCenter(img);

    for (int y = 0; y < img.height(); y++) {
        for (int x = 0; x < img.width(); x++) {
            int dx = x - center.x();
            int dy = y - center.y();
            double distance = sqrt(dx * dx + dy * dy);
            if (distance <= radius + M_SQRT2) {
                // Evaluate the area inside the radius + this M_SQRT2
                // (to reduce aliasing effects)
                double n = distance / radius;
                if (n > 0.0 && n < 1.0) {
                    n = distort(n, m_Force);
                }
                // Normalize the distance vector< and find the length after
                // distortion
                if (dx != 0 || dy != 0) {
                    double mag = n * radius/distance;
                    dx = dx * mag;
                    dy = dy * mag;
                }
                double tx = center.x() + dx;
                double ty = center.y() + dy;
                // Crop off overflows
                if (tx > img.width()  || tx < 0) tx = x;
                if (ty > img.height() || ty < 0) ty = y;

                outputImg->setPixel(x, y, getPixel(img, tx, ty));
            }
        }
    }

    return true;
}
<commit_msg>Fixed an overflow<commit_after>/*
 * enlargeshrinkplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Antia Puentes <apuentes@igalia.com>
 *
 * This file is part of the Gallery Enlarge/Shrink Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "enlargeshrink.h"

#include <math.h>

#define FILTER_NAME_ENLARGESHRINK "com.igalia.enlargeshrink"
#define FORCE_OPTION "force"
#define FORCE_FACTOR 3.2

#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif

// Maximum "width" of a pixel, measured diagonally
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880168872420969808
#endif

#define FIXEDPOINT_FRACTIONBITS 8
#define FIXEDPOINT_MULTIPLIER (1 << FIXEDPOINT_FRACTIONBITS)

static double fraction(double n)
{
    return n - floor(n);
}

static int fixedfraction(double n)
{
    return (int) (fraction(n) * FIXEDPOINT_MULTIPLIER);
}

static QRgb weighpixel(QRgb rgb, int percentage)
{
    return qRgb((qRed(rgb)   * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2),
                (qGreen(rgb) * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2),
                (qBlue(rgb)  * percentage) >> (FIXEDPOINT_FRACTIONBITS * 2));
}

/*
 * Gets a pixel at a floating-point coordinate, thus causing it to take into
 * accountance neighbour pixels if one of the coordinates have a fractional part
 *
 * The function will calculate the pixel value by calculating all areas the
 * pixel occupies in each neighbouring pixel.
 */
static QRgb getPixel(const QImage &img, double x, double y)
{
    int fx = fixedfraction(x);
    int fy = fixedfraction(y);

    int px = (int) x;
    int py = (int) y;

    int right = 1;
    if (px >= img.width() - 1) {
        right = 0;
    }

    int below = 1;
    if (py >= img.height() - 1) {
        below = 0;
    }

    QRgb p1 = weighpixel(img.pixel(px, py), (FIXEDPOINT_MULTIPLIER - fx) * (FIXEDPOINT_MULTIPLIER - fy));
    QRgb p2 = weighpixel(img.pixel(px + right, py), fx * (FIXEDPOINT_MULTIPLIER - fy));
    QRgb p3 = weighpixel(img.pixel(px, py + below), (FIXEDPOINT_MULTIPLIER - fx) * fy);
    QRgb p4 = weighpixel(img.pixel(px + right, py + below), fx * fy);

    return qRgb((qRed(p1) + qRed(p2) + qRed(p3) + qRed(p4)),
                (qGreen(p1) + qGreen(p2) + qGreen(p3) + qGreen(p4)),
                (qBlue(p1) + qBlue(p2) + qBlue(p3) + qBlue(p4)));

}

/**
 * Returns the enlarge/shrink effect for a given "n".
 * The function should be continous and should return 0 for x=0 and return 1 for
 * x = 1 if not, abrupt changes might appear in the image. The function only
 * needs to be valid for the interval [0..1].
 * \a amplitude should be in the interval [-0.3125, 0.3125], if it is outside
 * this interval the function might return negative values (which might give
 * interesting effects but not an enlarge/shrink effect)
 * A positive \a amplitude will give a "punch", negative a "pinch"
 *
 * Keeping the function linear (return x;) will in theory not change the image,
 * but due to rounding errors one might expect minor distortion.
 */
static double distort(double n, double amplitude)
{
    return n - sin(M_PI * n) * amplitude;
}

EnlargeShrink::EnlargeShrink() :
    m_Radius(0), m_Center(-1, -1), m_Force(0)
{
}

EnlargeShrink::~EnlargeShrink()
{
}

const QString EnlargeShrink::name() const
{
    return FILTER_NAME_ENLARGESHRINK;
}

const QStringList EnlargeShrink::supportedOptions() const
{
    QStringList supportedOptions;
    supportedOptions << QuillImageFilter::Radius
                     << QuillImageFilter::Center
                     << FORCE_OPTION;
    return supportedOptions;
}

bool EnlargeShrink::setOption(const QString &option, const QVariant &value)
{
    bool ok = false;

    if (option == QuillImageFilter::Radius) {
        double radius = value.toDouble(&ok);
        if (ok) {
            m_Radius = radius;
        }

    } else if (option == QuillImageFilter::Center) {
        QPoint center = value.toPoint();
        if (!center.isNull()) {
            m_Center = center;
            ok = true;
        }

    } else if (option == FORCE_OPTION) {
        double force = value.toDouble(&ok);
        ok = ok && force <= 1.0 && force >= -1.0;
        if (ok) {
            // Divide by the FORCE_FACTOR to get appropiated values for
            // the Amplitude used by the "distort" function
            m_Force = force/FORCE_FACTOR;
        }
    }

    return ok;
}

QVariant EnlargeShrink::option(const QString &option) const
{
    QVariant value;

    if (option == QuillImageFilter::Radius) {
        value = m_Radius;
    } else if (option == QuillImageFilter::Center) {
        value = m_Center;
    } else if (option == FORCE_OPTION) {
        value = m_Force * FORCE_FACTOR;
    }

    return value;
}

QuillImage EnlargeShrink::apply(const QuillImage& image) const
{
    if (image.fullImageSize().isEmpty()) {
        return QImage();
    }

    QuillImage out;
    enlargeShrink(image, &out);
    return out;
}

double EnlargeShrink::getRadius(const QuillImage &img) const
{
    double radius = m_Radius;
    if (!img.isFragment() || (img.width() == 170 && img.height() == 170)) {
        if (img.fullImageSize().width() < img.fullImageSize().height()) {
            radius = radius * img.width() / img.fullImageSize().width();
        } else {
            radius = radius * img.height() / img.fullImageSize().height();
        }
    }
    return radius;
}

QPoint EnlargeShrink::getCenter(const QuillImage &img) const
{
    QPoint center = m_Center;
    if (!img.isFragment() || (img.width() == 170 && img.height() == 170)) {
        center.setX(center.x() * img.width()  / img.fullImageSize().width());
        center.setY(center.y() * img.height() / img.fullImageSize().height());
    }
    return center;
}

bool EnlargeShrink::enlargeShrink(const QuillImage &img,
                                  QuillImage *outputImg) const
{
    *outputImg = img;

    double radius = getRadius(img);
    QPoint center = getCenter(img);

    for (int y = 0; y < img.height(); y++) {
        for (int x = 0; x < img.width(); x++) {
            int dx = x - center.x();
            int dy = y - center.y();
            double distance = sqrt(dx * dx + dy * dy);
            if (distance <= radius + M_SQRT2) {
                // Evaluate the area inside the radius + this M_SQRT2
                // (to reduce aliasing effects)
                double n = distance / radius;
                if (n > 0.0 && n < 1.0) {
                    n = distort(n, m_Force);
                }
                // Normalize the distance vector< and find the length after
                // distortion
                if (dx != 0 || dy != 0) {
                    double mag = n * radius/distance;
                    dx = dx * mag;
                    dy = dy * mag;
                }
                double tx = center.x() + dx;
                double ty = center.y() + dy;
                // Crop off overflows
                if (tx >= img.width()  || tx < 0) tx = x;
                if (ty >= img.height() || ty < 0) ty = y;

                outputImg->setPixel(x, y, getPixel(img, tx, ty));
            }
        }
    }

    return true;
}
<|endoftext|>