146 lines
4.2 KiB
C++
146 lines
4.2 KiB
C++
/*
|
|
* RenderItemDistanceMetric.h
|
|
*
|
|
* Created on: Feb 16, 2009
|
|
* Author: struktured
|
|
*/
|
|
|
|
#ifndef RenderItemDISTANCEMETRIC_H_
|
|
#define RenderItemDISTANCEMETRIC_H_
|
|
|
|
#include "Common.hpp"
|
|
#include "Renderable.hpp"
|
|
#include <limits>
|
|
#include <functional>
|
|
#include <map>
|
|
|
|
|
|
/// Compares two render items and returns zero if they are virtually equivalent and large values
|
|
/// when they are dissimilar. If two render items cannot be compared, NOT_COMPARABLE_VALUE is returned.
|
|
class RenderItemDistanceMetric : public std::binary_function<const RenderItem*, const RenderItem*, double> {
|
|
public:
|
|
const static double NOT_COMPARABLE_VALUE;
|
|
virtual double operator()(const RenderItem * r1, const RenderItem * r2) const = 0;
|
|
virtual TypeIdPair typeIdPair() const = 0;
|
|
};
|
|
|
|
// A base class to construct render item distance metrics. Just specify your two concrete
|
|
// render item types as template parameters and override the computeDistance() function.
|
|
template <class R1, class R2>
|
|
class RenderItemDistance : public RenderItemDistanceMetric {
|
|
|
|
protected:
|
|
// Override to create your own distance fmetric for your specified custom types.
|
|
virtual double computeDistance(const R1 * r1, const R2 * r2) const = 0;
|
|
|
|
public:
|
|
|
|
inline virtual double operator()(const RenderItem * r1, const RenderItem * r2) const {
|
|
if (supported(r1, r2))
|
|
return computeDistance(dynamic_cast<const R1*>(r1), dynamic_cast<const R2*>(r2));
|
|
else if (supported(r2,r1))
|
|
return computeDistance(dynamic_cast<const R1*>(r2), dynamic_cast<const R2*>(r1));
|
|
else
|
|
return NOT_COMPARABLE_VALUE;
|
|
}
|
|
|
|
// Returns true if and only if r1 and r2 are the same type as or derived from R1, R2 respectively
|
|
inline bool supported(const RenderItem * r1, const RenderItem * r2) const {
|
|
return dynamic_cast<const R1*>(r1) && dynamic_cast<const R2*>(r2);
|
|
//return typeid(r1) == typeid(const R1 *) && typeid(r2) == typeid(const R2 *);
|
|
}
|
|
|
|
inline TypeIdPair typeIdPair() const {
|
|
return TypeIdPair(typeid(const R1*).name(), typeid(const R2*).name());
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class RTIRenderItemDistance : public RenderItemDistance<RenderItem, RenderItem> {
|
|
public:
|
|
|
|
RTIRenderItemDistance() {}
|
|
virtual ~RTIRenderItemDistance() {}
|
|
|
|
protected:
|
|
virtual inline double computeDistance(const RenderItem * lhs, const RenderItem * rhs) const {
|
|
if (typeid(*lhs) == typeid(*rhs)) {
|
|
//std::cerr << typeid(*lhs).name() << " and " << typeid(*rhs).name() << "are comparable" << std::endl;
|
|
|
|
return 0.0;
|
|
}
|
|
else {
|
|
//std::cerr << typeid(*lhs).name() << " and " << typeid(*rhs).name() << "not comparable" << std::endl;
|
|
return NOT_COMPARABLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class ShapeXYDistance : public RenderItemDistance<Shape, Shape> {
|
|
|
|
public:
|
|
|
|
ShapeXYDistance() {}
|
|
virtual ~ShapeXYDistance() {}
|
|
|
|
protected:
|
|
|
|
virtual inline double computeDistance(const Shape * lhs, const Shape * rhs) const {
|
|
return (meanSquaredError(lhs->x, rhs->x) + meanSquaredError(lhs->y, rhs->y)) / 2;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class MasterRenderItemDistance : public RenderItemDistance<RenderItem, RenderItem> {
|
|
|
|
typedef std::map<TypeIdPair, RenderItemDistanceMetric*> DistanceMetricMap;
|
|
public:
|
|
|
|
MasterRenderItemDistance() {}
|
|
virtual ~MasterRenderItemDistance() {}
|
|
|
|
inline void addMetric(RenderItemDistanceMetric * fun) {
|
|
_distanceMetricMap[fun->typeIdPair()] = fun;
|
|
}
|
|
|
|
protected:
|
|
virtual inline double computeDistance(const RenderItem * lhs, const RenderItem * rhs) const {
|
|
|
|
RenderItemDistanceMetric * metric;
|
|
|
|
TypeIdPair pair(typeid(lhs), typeid(rhs));
|
|
|
|
|
|
// If specialized metric exists, use it to get higher granularity
|
|
// of correctness
|
|
if (_distanceMetricMap.count(pair)) {
|
|
metric = _distanceMetricMap[pair];
|
|
} else if (_distanceMetricMap.count(pair = TypeIdPair(typeid(rhs), typeid(lhs)))) {
|
|
metric = _distanceMetricMap[pair];
|
|
} else { // Failing that, use rtti && shape distance if its a shape type
|
|
|
|
const double rttiError = _rttiDistance(lhs,rhs);
|
|
|
|
/// @bug This is a non elegant approach to supporting shape distance
|
|
if (rttiError == 0 && _shapeXYDistance.supported(lhs,rhs))
|
|
return _shapeXYDistance(lhs, rhs);
|
|
else return rttiError;
|
|
}
|
|
|
|
return (*metric)(lhs, rhs);
|
|
}
|
|
|
|
private:
|
|
mutable RTIRenderItemDistance _rttiDistance;
|
|
mutable ShapeXYDistance _shapeXYDistance;
|
|
mutable DistanceMetricMap _distanceMetricMap;
|
|
};
|
|
|
|
#endif /* RenderItemDISTANCEMETRIC_H_ */
|