/*===========================================================================*\
* *
* OpenFlipper *
* Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen *
* www.openflipper.org *
* *
*--------------------------------------------------------------------------- *
* This file is part of OpenFlipper. *
* *
* OpenFlipper 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, either version 3 of *
* the License, or (at your option) any later version with the *
* following exceptions: *
* *
* If other files instantiate templates or use macros *
* or inline functions from this file, or you compile this file and *
* link it with other files to produce an executable, this file does *
* not by itself cause the resulting executable to be covered by the *
* GNU Lesser General Public License. This exception does not however *
* invalidate any other reasons why the executable file might be *
* covered by the GNU Lesser General Public License. *
* *
* OpenFlipper 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 LesserGeneral Public *
* License along with OpenFlipper. If not, *
* see . *
* *
\*===========================================================================*/
/*===========================================================================*\
* *
* $Revision: 11950 $ *
* $LastChangedBy: moebius $ *
* $Date: 2011-07-07 10:01:14 +0200 (Do, 07 Jul 2011) $ *
* *
\*===========================================================================*/
//=============================================================================
//
// CLASS DecimaterPlugin - IMPLEMENTATION
//
//=============================================================================
//== INCLUDES =================================================================
#include
#include "DecimaterPlugin.hh"
#include
#include
#include
#include
#include
#define DECIMATER "DecimaterData"
//== IMPLEMENTATION ==========================================================
DecimaterPlugin::DecimaterPlugin() :
tool_(0),
toolIcon_(0)
{
}
void DecimaterPlugin::initializePlugin()
{
tool_ = new DecimaterToolbarWidget();
QSize size(100, 100);
tool_->resize(size);
// connect signals->slots
connect(tool_->pbDecimate,SIGNAL(clicked() ),this,SLOT(slot_decimate()));
connect(tool_->roundness,SIGNAL(valueChanged(double) ),this,SLOT(updateRoundness(double)) );
connect(tool_->roundnessSlider,SIGNAL(valueChanged(int) ),this,SLOT(updateRoundness(int)) );
connect(tool_->aspectRatio,SIGNAL(valueChanged(double) ),this,SLOT(updateAspectRatio(double)) );
connect(tool_->aspectRatioSlider,SIGNAL(valueChanged(int) ),this,SLOT(updateAspectRatio(int)) );
connect(tool_->distance,SIGNAL(valueChanged(double) ),this,SLOT(updateDistance()) );
connect(tool_->edgeLength,SIGNAL(valueChanged(double) ),this,SLOT(updateEdgeLength()) );
connect(tool_->normalDeviation,SIGNAL(valueChanged(int) ),this,SLOT(updateNormalDev()) );
connect(tool_->normalDeviationSlider,SIGNAL(valueChanged(int) ),this,SLOT(updateNormalDev()) );
connect(tool_->verticesCount,SIGNAL(valueChanged(int) ),this,SLOT(updateVertices()) );
connect(tool_->verticesCountSlider,SIGNAL(valueChanged(int) ),this,SLOT(updateVertices()) );
connect(tool_->trianglesCount,SIGNAL(valueChanged(int) ),this,SLOT(updateTriangles()) );
connect(tool_->trianglesCountSlider,SIGNAL(valueChanged(int) ),this,SLOT(updateTriangles()) );
// Force update if the Toolbox gets visible
connect(tool_, SIGNAL(showing()), this, SLOT( slotUpdateNumVertices() ) );
connect(tool_, SIGNAL(showing()), this, SLOT( slotUpdateNumTriangles() ) );
toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"decimater.png");
emit addToolbox( tr("Decimater") , tool_, toolIcon_ );
}
/** \brief Initialization of the plugin when it is loaded by the core
*
*/
void DecimaterPlugin::pluginsInitialized() {
emit setSlotDescription("decimate(int,QVariantMap)",tr("Decimate a given object"),
QString(tr("objectId,constraints")).split(","),
QString(tr("ID of an object; Object that can has one or more constraint properties (decimation_order,distance,edge_length,normal_deviation,roundness,aspect_ratio,independent_sets,vertices,triangles)")).split(";"));
}
//-----------------------------------------------------------------------------
/** \brief sync between values of roundness slider and spinbox in the toolbox
*
* @param _value new roundness value
*/
void DecimaterPlugin::updateRoundness(int _value)
{
tool_->roundness->setValue( (double) _value / 100.0 );
tool_->cbRoundness->setChecked (true);
}
//-----------------------------------------------------------------------------
/** \brief sync between values of roundness slider and spinbox in the toolbox
*
* @param _value new roundness value
*/
void DecimaterPlugin::updateRoundness(double _value)
{
tool_->roundnessSlider->setValue( (int) (_value * 100) );
tool_->cbRoundness->setChecked (true);
}
//-----------------------------------------------------------------------------
/** \brief sync between values of aspect ratio slider and spinbox in the toolbox
*
* @param _value new aspect ratio value
*/
void DecimaterPlugin::updateAspectRatio(int _value)
{
tool_->aspectRatio->setValue( (double) _value / 100.0 );
tool_->cbAspectRatio->setChecked (true);
}
//-----------------------------------------------------------------------------
/** \brief sync between values of aspect ratio slider and spinbox in the toolbox
*
* @param _value new aspect ratio value
*/
void DecimaterPlugin::updateAspectRatio(double _value)
{
tool_->aspectRatioSlider->setValue( (int) (_value * 100) );
tool_->cbAspectRatio->setChecked (true);
}
//-----------------------------------------------------------------------------
/** \brief Decimation called by toolbox
*
*/
void DecimaterPlugin::slot_decimate()
{
for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DATA_TRIANGLE_MESH) ;
o_it != PluginFunctions::objectsEnd(); ++o_it) {
//initialize
TriMeshObject* object = PluginFunctions::triMeshObject(*o_it);
if ( object == 0 )
emit log(LOGWARN , tr("Unable to get object"));
DecimaterInfo* decimater = dynamic_cast< DecimaterInfo* > ( o_it->objectData(DECIMATER) );
TriMesh* mesh = PluginFunctions::triMesh(*o_it);
if (decimater == 0){
decimater = new DecimaterInfo();
o_it->setObjectData(DECIMATER, decimater);
}
// constraint handles for decimation
ModAspectRatioH hModAspectRatio;
ModEdgeLengthH hModEdgeLength;
ModHausdorffH hModHausdorff;
ModIndependentH hModIndependent;
ModNormalDeviationH hModNormalDeviation;
ModNormalFlippingH hModNormalFlipping;
ModQuadricH hModQuadric;
ModRoundnessH hModRoundness;
// Create decimater
DecimaterType decimater_object( *mesh );
// Remove old constraints
if(decimater->distance()) {
decimater->removeDistanceConstraint();
decimater_object.remove(hModHausdorff);
}
if(decimater->normalDeviation()) {
decimater->removeNormalDeviationConstraint();
decimater_object.remove(hModNormalDeviation);
}
if(decimater->normalFlipping()) {
decimater->removeNormalFlippingConstraint();
decimater_object.remove(hModNormalFlipping);
}
if(decimater->roundness()) {
decimater->removeRoundnessConstraint();
decimater_object.remove(hModRoundness);
}
if(decimater->aspectRatio()) {
decimater->removeAspectRatioConstraint();
decimater_object.remove(hModAspectRatio);
}
if(decimater->edgeLength()) {
decimater->removeEdgeLengthConstraint();
decimater_object.remove(hModEdgeLength);
}
if(decimater->independentSets()) {
decimater->removeIndependentSetsConstraint();
decimater_object.remove(hModIndependent);
}
// set priority module: quadric, normal deviation or edge length
if (tool_->rbByDistance->isChecked()) {
decimater->setDecimationOrder(DecimaterInfo::DISTANCE);
decimater_object.add( hModQuadric );
decimater_object.module( hModQuadric ).unset_max_err();
} else if (tool_->rbByNormalDeviation->isChecked()) {
decimater->setDecimationOrder(DecimaterInfo::NORMALDEV);
decimater_object.add(hModNormalDeviation);
decimater_object.module(hModNormalDeviation).set_binary(false);
} else if (tool_->rbByEdgeLength->isChecked()) {
decimater->setDecimationOrder(DecimaterInfo::EDGELENGTH);
decimater_object.add(hModEdgeLength);
decimater_object.module(hModEdgeLength).set_binary(false);
}
// and set new constraints
if ( tool_->cbDistance->isChecked() ) {
if ( decimater_object.add( hModHausdorff ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setDistanceConstraint( tool_->distance->value() );
decimater_object.module( hModHausdorff ).set_tolerance( decimater->distanceValue() );
}
}
if ( tool_->cbNormalDev->isChecked() ) {
if ( decimater_object.add( hModNormalDeviation ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setNormalDeviationConstraint( tool_->normalDeviation->value() );
decimater_object.module( hModNormalDeviation ).set_normal_deviation( decimater->normalDeviationValue() );
}
} else {
if ( decimater_object.add( hModNormalFlipping ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setNormalFlippingConstraint();
// decimater_object.module( hModNormalFlipping ).set_max_normal_deviation( decimater->normalDeviationValue() ); ?
}
}
if ( tool_->cbRoundness->isChecked() ) {
if ( decimater_object.add( hModRoundness ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setRoundnessConstraint( tool_->roundness->value() );
decimater_object.module( hModRoundness ).set_min_roundness( decimater->roundnessValue(), true );
}
}
if ( tool_->cbAspectRatio->isChecked() ) {
if ( decimater_object.add( hModAspectRatio ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setAspectRatioConstraint( tool_->aspectRatio->value() );
decimater_object.module( hModAspectRatio ).set_aspect_ratio( decimater->aspectRatioValue() );
}
}
if ( tool_->cbEdgeLength->isChecked() ) {
if ( decimater_object.add( hModEdgeLength ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setEdgeLengthConstraint( tool_->edgeLength->value() );
decimater_object.module( hModEdgeLength ).set_edge_length( decimater->edgeLengthValue() );
}
}
if ( tool_->cbIndependentSets->isChecked() ) {
if ( decimater_object.add( hModIndependent ) || tool_->rbConstraintsOnly->isChecked() ) {
decimater->setIndependentSetsConstraint();
}
}
// Initialize the decimater
if( ! decimater_object.initialize() ){
emit log(LOGWARN, tr("Decimater could not be initialized"));
continue;
}
//decimate
if ( tool_->rbVertices->isChecked() )
decimater_object.decimate_to(tool_->verticesCount->value());
else if (tool_->rbTriangles->isChecked() )
decimater_object.decimate_to_faces(0, tool_->trianglesCount->value());
else // constraints only
decimater_object.decimate_to_faces(0, 1);
object->mesh()->garbage_collection();
object->mesh()->update_normals();
object->update();
// Create backup
emit createBackup(o_it->id(), "Decimation");
emit updatedObject( o_it->id() , UPDATE_TOPOLOGY );
}
emit updateView();
}
//-----------------------------------------------------------------------------
/** \brief Decimation called by Scripting
*
* @param _objID id of an object
* @param _constraints A string containing a comma separated list of constraints (distance,normal_deviation,roundness,triangles)
*/
void DecimaterPlugin::decimate(int _objID, QVariantMap _constraints) {
BaseObjectData* baseObjectData;
if ( ! PluginFunctions::getObject(_objID,baseObjectData) ) {
emit log(LOGERR,tr("Unable to get Object"));
return;
}
if ( baseObjectData->dataType() == DATA_TRIANGLE_MESH ) {
TriMeshObject* object = PluginFunctions::triMeshObject(baseObjectData);
if ( object == 0 ) {
emit log(LOGWARN , tr("Unable to get object ( Only Triangle Meshes supported)"));
return;
}
DecimaterInfo* decimater = dynamic_cast< DecimaterInfo* > ( object->objectData(DECIMATER) );
TriMesh* mesh = PluginFunctions::triMesh(baseObjectData);
if (decimater == 0){
decimater = new DecimaterInfo();
object->setObjectData(DECIMATER, decimater);
}
// constraint handles for decimation
ModAspectRatioH hModAspectRatio;
ModEdgeLengthH hModEdgeLength;
ModHausdorffH hModHausdorff;
ModIndependentH hModIndependent;
ModNormalDeviationH hModNormalDeviation;
ModNormalFlippingH hModNormalFlipping;
ModQuadricH hModQuadric;
ModRoundnessH hModRoundness;
// Create decimater
DecimaterType decimater_object( *mesh );
// Remove old constraints
if(decimater->distance()) {
decimater->removeDistanceConstraint();
decimater_object.remove(hModHausdorff);
}
if(decimater->normalDeviation()) {
decimater->removeNormalDeviationConstraint();
decimater_object.remove(hModNormalDeviation);
}
if(decimater->normalFlipping()) {
decimater->removeNormalFlippingConstraint();
decimater_object.remove(hModNormalFlipping);
}
if(decimater->roundness()) {
decimater->removeRoundnessConstraint();
decimater_object.remove(hModRoundness);
}
if(decimater->aspectRatio()) {
decimater->removeAspectRatioConstraint();
decimater_object.remove(hModAspectRatio);
}
if(decimater->edgeLength()) {
decimater->removeEdgeLengthConstraint();
decimater_object.remove(hModEdgeLength);
}
if(decimater->independentSets()) {
decimater->removeIndependentSetsConstraint();
decimater_object.remove(hModIndependent);
}
// set priority module: quadric, normal deviation or edge length
if ( _constraints.contains("decimation_order") ){
bool ok;
int value = _constraints["decimation_order"].toInt(&ok);
if (ok) {
switch (value) {
case 0:
decimater->setDecimationOrder(DecimaterInfo::DISTANCE);
decimater_object.add( hModQuadric );
decimater_object.module( hModQuadric ).unset_max_err();
break;
case 1:
decimater->setDecimationOrder(DecimaterInfo::NORMALDEV);
decimater_object.add(hModNormalDeviation);
decimater_object.module(hModNormalDeviation).set_binary(false);
break;
case 2:
decimater->setDecimationOrder(DecimaterInfo::EDGELENGTH);
decimater_object.add(hModEdgeLength);
decimater_object.module(hModEdgeLength).set_binary(false);
break;
default:
emit log(LOGERR,tr("Invalid Decimation Order"));
return;
}
}
} else {
emit log(LOGERR,tr("No Decimation Order set"));
return;
}
// stock options (triangle and vertices count) constraint
bool verticesCount = false;
bool trianglesCount = false;
int vertices = 0;
int triangles = 0;
if ( _constraints.contains("vertices") ){
bool ok;
int value = _constraints["vertices"].toInt(&ok);
if (ok){
verticesCount = true;
vertices = value;
}
} else if ( _constraints.contains("triangles") ){
bool ok;
int value = _constraints["triangles"].toInt(&ok);
if (ok){
trianglesCount = true;
triangles = value;
}
}
//distance constraint
if ( _constraints.contains("distance") ){
bool ok;
double value = _constraints["distance"].toDouble(&ok);
if (ok) {
if ( decimater_object.add( hModHausdorff ) || (!verticesCount && !trianglesCount) ) {
decimater->setDistanceConstraint( value );
decimater_object.module( hModHausdorff ).set_tolerance( decimater->distanceValue() );
}
}
}
//normal deviation constraint
if ( _constraints.contains("normal_deviation") ){
bool ok;
int value = _constraints["normal_deviation"].toInt(&ok);
if (ok) {
if ( decimater_object.add( hModNormalDeviation ) || (!verticesCount && !trianglesCount) ) {
decimater->setNormalDeviationConstraint( value );
decimater_object.module( hModNormalDeviation ).set_normal_deviation( decimater->normalDeviationValue() );
}
}
} else { // flipping constraint
if ( decimater_object.add( hModNormalFlipping ) || (!verticesCount && !trianglesCount) ) {
decimater->setNormalFlippingConstraint();
// decimater_object.module( hModNormalFlipping ).set_max_normal_deviation( decimater->normalDeviationValue() ); ?
}
}
//roundness constraint
if ( _constraints.contains("roundness") ){
bool ok;
double value = _constraints["roundness"].toDouble(&ok);
if (ok) {
if ( decimater_object.add( hModRoundness ) || (!verticesCount && !trianglesCount) ) {
decimater->setRoundnessConstraint( value );
decimater_object.module( hModRoundness ).set_min_roundness( decimater->roundnessValue(), true );
}
}
}
//aspect ratio constraint
if ( _constraints.contains("aspect_ratio") ){
bool ok;
double value = _constraints["aspect_ratio"].toDouble(&ok);
if (ok) {
if ( decimater_object.add( hModAspectRatio ) || (!verticesCount && !trianglesCount) ) {
decimater->setAspectRatioConstraint( value );
decimater_object.module( hModAspectRatio ).set_aspect_ratio( decimater->aspectRatioValue() );
}
}
}
//edge length constraint
if ( _constraints.contains("edge_length") ){
bool ok;
double value = _constraints["edge_length"].toDouble(&ok);
if (ok) {
if ( decimater_object.add( hModEdgeLength ) || (!verticesCount && !trianglesCount) ) {
decimater->setEdgeLengthConstraint( value );
decimater_object.module( hModEdgeLength ).set_edge_length( decimater->edgeLengthValue() );
}
}
}
//independent sets constraint
if ( _constraints.contains("independent_sets") ){
bool value = _constraints["independent_sets"].toBool();
if (value) {
if ( decimater_object.add( hModIndependent ) || (!verticesCount && !trianglesCount) ) {
decimater->setIndependentSetsConstraint();
}
}
}
//init the decimater
if( ! decimater_object.initialize() ){
emit log(LOGWARN, tr("Decimater could not be initialized"));
return;
}
//decimate
if ( verticesCount )
decimater_object.decimate_to( vertices ); // do decimation (vertices)
else if (trianglesCount )
decimater_object.decimate_to_faces(0, triangles); // do decimation (triangles)
else
decimater_object.decimate_to_faces(0, 1); // do decimation
object->mesh()->garbage_collection();
object->mesh()->update_normals();
object->update();
// Create backup
emit createBackup(_objID, "Decimation");
// Create QVariantMap parameter string
QString param = "(" + (_constraints.contains("decimation_order") ? tr("decimation_order = %1").arg(_constraints["decimation_order"].toString()) : "") +
", " + (_constraints.contains("distance") ? tr("distance = %1").arg(_constraints["distance"].toString()) : "") +
", " + (_constraints.contains("normal_deviation") ? tr("normal_deviation = %1").arg(_constraints["normal_deviation"].toString()) : "") +
", " + (_constraints.contains("edge_length") ? tr("edge_length = %1").arg(_constraints["edge_length"].toString()) : "") +
", " + (_constraints.contains("roundness") ? tr("roundness = %1").arg(_constraints["roundness"].toString()) : "") +
", " + (_constraints.contains("aspect_ratio") ? tr("aspect_ratio = %1").arg(_constraints["aspect_ratio"].toString()) : "") +
", " + (_constraints.contains("independent_sets") ? tr("independent_sets = %1").arg(_constraints["independent_sets"].toString()) : "") +
", " + (_constraints.contains("triangles") ? tr("triangles = %1").arg(_constraints["triangles"].toString()) : "") +
", " + (_constraints.contains("vertices") ? tr("vertices = %1").arg(_constraints["vertices"].toString()) : "") + ")";
emit scriptInfo( "decimate(" + QString::number(_objID) + ", " + param + ")" );
emit updatedObject( baseObjectData->id() , UPDATE_TOPOLOGY);
} else {
emit log(LOGERR,tr("Unsupported object type for decimater"));
return;
}
emit updateView();
}
//-----------------------------------------------------------------------------
void DecimaterPlugin::slotUpdateNumVertices()
{
// Only update if tool is visible
if ( !tool_->isVisible() ) {
return;
}
int max = 0;
int div = 0;
bool ok;
emit functionExists( "infomeshobject" , "vertexCount(int)", ok ) ;
if (!ok)
{
tool_->currentNumVertices->setText ("");
return;
}
for ( PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS,DataType(DATA_TRIANGLE_MESH)) ;
o_it != PluginFunctions::objectsEnd(); ++o_it) {
max = std::max( RPC::callFunctionValue ("infomeshobject" , "vertexCount",o_it->id()) , max );
div++;
}
if (div <= 0)
tool_->currentNumVertices->setText ("");
else {
tool_->verticesCount->blockSignals(true);
tool_->verticesCountSlider->blockSignals(true);
tool_->currentNumVertices->setText (QString::number(max));
tool_->verticesCount->setMaximum(max);
tool_->verticesCountSlider->setMaximum(max);
if ( tool_->verticesCount->value() < 2 )
tool_->verticesCount->setValue( max / 2 );
tool_->verticesCount->blockSignals(false);
tool_->verticesCountSlider->blockSignals(false);
}
}
//-----------------------------------------------------------------------------
/** \brief gets and sets the current maximum number of triangles
*
*/
void DecimaterPlugin::slotUpdateNumTriangles() {
// only update if the tool is visible
if (!tool_->isVisible())
return;
uint max = 0;
int meshN = 0;
for (PluginFunctions::ObjectIterator o_it(PluginFunctions::TARGET_OBJECTS, DataType(DATA_TRIANGLE_MESH));
o_it != PluginFunctions::objectsEnd(); ++o_it) {
TriMesh* mesh = PluginFunctions::triMesh(o_it->id());
max = std::max(mesh->n_faces(), max);
meshN++;
}
tool_->trianglesCount->blockSignals(true);
tool_->trianglesCountSlider->blockSignals(true);
tool_->trianglesCount->setMinimum(1);
tool_->trianglesCount->setMaximum(max);
tool_->trianglesCountSlider->setMinimum(1);
tool_->trianglesCountSlider->setMaximum(max);
if (tool_->trianglesCount->value() < 2)
tool_->trianglesCount->setValue(max/2);
tool_->trianglesCount->blockSignals(false);
tool_->trianglesCountSlider->blockSignals(false);
}
//-----------------------------------------------------------------------------
void DecimaterPlugin::slotObjectSelectionChanged(int /*_identifier*/)
{
slotUpdateNumVertices ();
slotUpdateNumTriangles ();
}
//-----------------------------------------------------------------------------
void DecimaterPlugin::slotObjectUpdated(int /*_identifier*/ , const UpdateType& _type )
{
if ( _type.contains(UPDATE_TOPOLOGY) ) {
slotUpdateNumVertices ();
slotUpdateNumTriangles ();
}
}
//-----------------------------------------------------------------------------
// activate checkbox if value has changed
void DecimaterPlugin::updateVertices()
{
tool_->rbVertices->setChecked (true);
}
//-----------------------------------------------------------------------------
// activate checkbox if value has changed
void DecimaterPlugin::updateTriangles()
{
tool_->rbTriangles->setChecked (true);
}
//-----------------------------------------------------------------------------
// activate checkbox if value has changed
void DecimaterPlugin::updateNormalDev()
{
tool_->cbNormalDev->setChecked (true);
}
//-----------------------------------------------------------------------------
// activate checkbox if value has changed
void DecimaterPlugin::updateEdgeLength()
{
tool_->cbEdgeLength->setChecked (true);
}
//-----------------------------------------------------------------------------
// activate checkbox if value has changed
void DecimaterPlugin::updateDistance()
{
tool_->cbDistance->setChecked (true);
}
//-----------------------------------------------------------------------------
Q_EXPORT_PLUGIN2(DecimaterPlugin , DecimaterPlugin );