/* * RenderItemDistanceMetric.h * * Created on: Feb 16, 2009 * Author: struktured */ #ifndef RenderItemDISTANCEMETRIC_H_ #define RenderItemDISTANCEMETRIC_H_ #include "Common.hpp" #include "Renderable.hpp" #include #include #include /// 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 { 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 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(r1), dynamic_cast(r2)); else if (supported(r2,r1)) return computeDistance(dynamic_cast(r2), dynamic_cast(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(r1) && dynamic_cast(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 { 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 { 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 { typedef std::map 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_ */