find_package(SalomeCPPUNIT)
add_subdirectory (src/HYDROData)
+add_subdirectory (src/HYDROOperations)
enable_testing()
#include <HYDROData_Iterator.h>
#include <TDataStd_RealArray.hxx>
-#include <TDataStd_IntegerArray.hxx>
#include <TDataStd_ByteArray.hxx>
+#include <TDataStd_IntegerArray.hxx>
#include <TDataStd_ReferenceList.hxx>
+#include <TDataStd_Name.hxx>
+#include <TDataStd_UAttribute.hxx>
#include <TDF_ListIteratorOfLabelList.hxx>
+// tage of the child of my label that contains information about the operator
+static const int TAG_OPERATOR = 1;
+static const Standard_GUID GUID_MUST_BE_UPDATED("80f2bb81-3873-4631-8ddd-940d2119f000");
+
IMPLEMENT_STANDARD_HANDLE(HYDROData_Image, HYDROData_Object)
IMPLEMENT_STANDARD_RTTIEXT(HYDROData_Image, HYDROData_Object)
aParams->SetValue(3, theImage.bytesPerLine());
aParams->SetValue(4, (int)(theImage.format()));
// store data of image in byte array
- Handle(TDataStd_ByteArray) aData;
- int aLen = theImage.byteCount();
- if (!myLab.FindAttribute(TDataStd_ByteArray::GetID(), aData)) {
- aData = TDataStd_ByteArray::Set(myLab, 1, aLen);
- }
- // copy bytes one by one
- const uchar* aBits = theImage.bits();
- if (aData->Length() != aLen) {
- Handle(TColStd_HArray1OfByte) aNewData = new TColStd_HArray1OfByte(1, aLen);
- for(int a = 0; a < aLen; a++)
- aNewData->SetValue(a + 1, aBits[a]);
- aData->ChangeArray(aNewData);
- } else {
- for(int a = 0; a < aLen; a++)
- aData->SetValue(a + 1, aBits[a]);
- }
-
+ const char* aData = (const char*)(theImage.bits());
+ SaveByteArray(0, aData, theImage.byteCount());
}
QImage HYDROData_Image::Image()
{
Handle(TDataStd_IntegerArray) aParams;
- Handle(TDataStd_ByteArray) aData;
- if (!myLab.FindAttribute(TDataStd_IntegerArray::GetID(), aParams) ||
- !myLab.FindAttribute(TDataStd_ByteArray::GetID(), aData))
+ if (!myLab.FindAttribute(TDataStd_IntegerArray::GetID(), aParams))
+ return QImage(); // return empty image if there is no array
+ int aLen = 0;
+ uchar* anArray = (uchar*)ByteArray(0, aLen);
+ if (!aLen)
return QImage(); // return empty image if there is no array
- /*
- // make uchar array one by one
- int aLen = aData->Upper();
- uchar* anArray = new uchar[aLen];
- for(int a = 0; a < aLen; a++)
- anArray[a] = aData->Value(a + 1);
- // recreate image from integer parameters and array of bytes
- QImage aResult(anArray, aParams->Value(1), aParams->Value(2),
- aParams->Value(3), QImage::Format(aParams->Value(4)));
- delete [] anArray; <- this is wrong, because QImage references to this array
- */
- uchar* anArray = (uchar*)(void*)(&(aData->InternalArray()->ChangeArray1().ChangeValue(1)));
QImage aResult(anArray, aParams->Value(1), aParams->Value(2),
aParams->Value(3), QImage::Format(aParams->Value(4)));
-
return aResult;
}
{
myLab.ForgetAttribute(TDataStd_ReferenceList::GetID());
}
+
+void HYDROData_Image::SetOperatorName(const QString theOpName)
+{
+ TDataStd_Name::Set(myLab.FindChild(TAG_OPERATOR),
+ TCollection_ExtendedString(theOpName.toLatin1().constData()));
+}
+
+QString HYDROData_Image::OperatorName()
+{
+ Handle(TDataStd_Name) aName;
+ if (myLab.FindChild(TAG_OPERATOR).
+ FindAttribute(TDataStd_Name::GetID(), aName)) {
+ TCollection_AsciiString aStr(aName->Get());
+ return QString(aStr.ToCString());
+ }
+ return QString();
+}
+
+void HYDROData_Image::SetArgs(const QByteArray& theArgs)
+{
+ SaveByteArray(TAG_OPERATOR, theArgs.constData(), theArgs.length());
+}
+
+QByteArray HYDROData_Image::Args()
+{
+ int aLen = 0;
+ const char* aData = ByteArray(TAG_OPERATOR, aLen);
+ if (!aLen)
+ return QByteArray();
+ return QByteArray(aData, aLen);
+}
+
+void HYDROData_Image::MustBeUpdated(bool theFlag)
+{
+ if (theFlag) {
+ TDataStd_UAttribute::Set(myLab, GUID_MUST_BE_UPDATED);
+ } else {
+ myLab.ForgetAttribute(GUID_MUST_BE_UPDATED);
+ }
+}
+
+bool HYDROData_Image::MustBeUpdated()
+{
+ return myLab.IsAttribute(GUID_MUST_BE_UPDATED);
+}
*/
HYDRODATA_EXPORT void ClearReferences();
+ /**
+ * Stores the operator name
+ * \param theOpName name of the operator that must be executed for image update
+ */
+ HYDRODATA_EXPORT void SetOperatorName(const QString theOpName);
+
+ /**
+ * Returns the operator name
+ * \returns the name of the operator that must be executed for image update
+ */
+ HYDRODATA_EXPORT QString OperatorName();
+
+ /**
+ * Stores the operator arguments
+ * \param theArgs array that stores the operator arguments, needed for execution
+ */
+ HYDRODATA_EXPORT void SetArgs(const QByteArray& theArgs);
+
+ /**
+ * Returns the operator arguments
+ * \returns array that stores the operator arguments, needed for execution
+ */
+ HYDRODATA_EXPORT QByteArray Args();
+
+ /**
+ * Sets the "MustBeUpdated" flag: if image is depended on updated features.
+ * \param theFlag is true for images that must be updated, false for up-to-date
+ */
+ HYDRODATA_EXPORT void MustBeUpdated(bool theFlag);
+
+ /**
+ * Returns the "MustBeUpdated" flag: is image must be recomputed or not
+ * \returns false if image is up to date
+ */
+ HYDRODATA_EXPORT bool MustBeUpdated();
+
protected:
friend class HYDROData_Iterator;
#include <HYDROData_Object.h>
#include <TDataStd_Name.hxx>
+#include <TDataStd_ByteArray.hxx>
#include <TDF_CopyLabel.hxx>
IMPLEMENT_STANDARD_HANDLE(HYDROData_Object,MMgt_TShared)
{
myLab = theLabel;
}
+
+void HYDROData_Object::SaveByteArray(const int theTag,
+ const char* theData, const int theLen)
+{
+ TDF_Label aLab = theTag == 0 ? myLab : myLab.FindChild(theTag);
+ // array is empty, remove the attribute
+ if (theLen <= 0) {
+ aLab.ForgetAttribute(TDataStd_ByteArray::GetID());
+ return;
+ }
+ // store data of image in byte array
+ Handle(TDataStd_ByteArray) aData;
+ if (!aLab.FindAttribute(TDataStd_ByteArray::GetID(), aData)) {
+ aData = TDataStd_ByteArray::Set(aLab, 1, theLen);
+ }
+ // copy bytes one by one
+ if (aData->Length() != theLen) {
+ Handle(TColStd_HArray1OfByte) aNewData = new TColStd_HArray1OfByte(1, theLen);
+ for(int a = 0; a < theLen; a++)
+ aNewData->SetValue(a + 1, theData[a]);
+ aData->ChangeArray(aNewData);
+ } else {
+ for(int a = 0; a < theLen; a++)
+ aData->SetValue(a + 1, theData[a]);
+ }
+}
+
+const char* HYDROData_Object::ByteArray(const int theTag, int& theLen)
+{
+ TDF_Label aLab = theTag == 0 ? myLab : myLab.FindChild(theTag);
+ Handle(TDataStd_ByteArray) aData;
+ if (!aLab.FindAttribute(TDataStd_ByteArray::GetID(), aData))
+ return NULL; // return empty image if there is no array
+ theLen = aData->Length();
+ if (theLen)
+ return (const char*)(&(aData->InternalArray()->ChangeArray1().ChangeValue(1)));
+ return NULL;
+}
* Returns the label of this object.
*/
TDF_Label& Label() {return myLab;}
+
+ /**
+ * Internal method that used to store the byte array attribute
+ * \param theTag tag of a label to store attribute (for 0 this is myLab)
+ * \param theData pointer to bytes array
+ * \param theLen number of bytes in byte array that must be stored
+ */
+ void SaveByteArray(const int theTag, const char* theData, const int theLen);
+
+ /**
+ * Internal method that used to retreive the content of byte array attribute
+ * \param theTag tag of a label that keeps the attribute (for 0 this is myLab)
+ * \param theLen number of bytes in byte array
+ * \returns pointer to the internal data structure wit harray content,
+ * or NULL if array size is zero
+ */
+ const char* ByteArray(const int theTag, int& theLen);
protected:
/// Array of pointers to the properties of this object; index in this array is returned by \a AddProperty.
--- /dev/null
+include(../../CMake/Common.cmake)
+
+set(PROJECT_HEADERS
+ HYDROOperations.h
+ HYDROOperations_Factory.h
+ HYDROOperations_BSpline.h
+)
+
+set(PROJECT_SOURCES
+ HYDROOperations_Factory.cxx
+ HYDROOperations_BSpline.cxx
+)
+
+add_definitions(
+ -DHYDROOPERATIONS_EXPORTS
+ ${CAS_DEFINITIONS}
+ ${QT_DEFINITIONS}
+ ${GUI_CXXFLAGS}
+)
+
+include_directories(
+ ${CAS_INCLUDE_DIRS}
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../HYDROData
+)
+
+add_library(HYDROOperations SHARED ${PROJECT_SOURCES} ${PROJECT_HEADERS})
+target_link_libraries(HYDROOperations ${CAS_MODELER} ${ImageComposer} HYDROData)
+
+set(PROJECT_LIBRARIES HYDROOperations)
+
+# tests
+if(CPPUNIT_IS_OK)
+
+ set(TEST_HEADERS
+ test_HYDROOperations_BSpline.h
+ test_HYDROOperations_Factory.h
+ )
+
+ set(TEST_SOURCES
+ test_HYDROOperations_Main.cxx
+ test_HYDROOperations_BSpline.cxx
+ test_HYDROOperations_Factory.cxx
+ )
+
+ set(TEST_EXE test_HYDROOperations)
+ include(../../CMake/CPPUnitTests.cmake)
+ target_link_libraries(test_HYDROOperations ${CAS_MODELER} ${QT_LIBRARIES} ${CPPUNIT_LIBS} HYDROOperations)
+
+endif(CPPUNIT_IS_OK)
+
+include(../../CMake/CommonInstall.cmake)
--- /dev/null
+#ifndef HYDROOPERATIONS_H
+#define HYDROOPERATIONS_H
+
+#if defined HYDROOPERATIONS_EXPORTS
+#if defined WIN32
+#define HYDROOPERATIONS_EXPORT __declspec( dllexport )
+#else
+#define HYDROOPERATIONS_EXPORT
+#endif
+#else
+#if defined WIN32
+#define HYDROOPERATIONS_EXPORT __declspec( dllimport )
+#else
+#define HYDROOPERATIONS_EXPORT
+#endif
+#endif
+
+#endif
--- /dev/null
+#include<HYDROOperations_BSpline.h>
+
+#include<TColgp_HArray1OfPnt.hxx>
+#include<GeomAPI_Interpolate.hxx>
+#include<GeomConvert_BSplineCurveToBezierCurve.hxx>
+#include<Geom_BezierCurve.hxx>
+
+HYDROOperations_BSpline::HYDROOperations_BSpline(
+ QList<double>& thePoints)
+{
+ // fill array for algorithm by the received coordinates
+ int aLen = thePoints.size() / 2;
+ Handle(TColgp_HArray1OfPnt) aHCurvePoints = new TColgp_HArray1OfPnt (1, aLen);
+ QList<double>::iterator aListIter = thePoints.begin();
+ for (int ind = 1; ind <= aLen; ind++) {
+ gp_Pnt aPnt(gp::Origin());
+ aPnt.SetX(*aListIter);
+ aListIter++;
+ aPnt.SetY(*aListIter);
+ aListIter++;
+ aHCurvePoints->SetValue(ind, aPnt);
+ }
+ // compute BSpline
+ GeomAPI_Interpolate aGBC(aHCurvePoints, Standard_False, gp::Resolution());
+ aGBC.Perform();
+ if (aGBC.IsDone()) {
+ myCurve = aGBC.Curve();
+ }
+}
+
+QPainterPath HYDROOperations_BSpline::ComputePath() const
+{
+ QPainterPath aResult;
+ if (myCurve.IsNull()) // returns an empty Path if original curve is invalid
+ return aResult;
+ GeomConvert_BSplineCurveToBezierCurve aConverter(myCurve);
+ int a, aNumArcs = aConverter.NbArcs();
+ for(a = 1; a <= aNumArcs; a++) {
+ Handle(Geom_BezierCurve) anArc = aConverter.Arc(a);
+ if (a == 1) { // set a start point
+ gp_Pnt aStart = anArc->StartPoint();
+ aResult.moveTo(aStart.X(), aStart.Y());
+ }
+ gp_Pnt anEnd = anArc->EndPoint();
+ if (anArc->NbPoles() == 3) { // quadric segment in the path (pole 1 is start, pole 3 is end)
+ gp_Pnt aPole = anArc->Pole(2);
+ aResult.quadTo(aPole.X(), aPole.Y(), anEnd.X(), anEnd.Y());
+ } else if (anArc->NbPoles() == 4) { // cubic segment (usually this is used)
+ gp_Pnt aPole1 = anArc->Pole(2);
+ gp_Pnt aPole2 = anArc->Pole(3);
+ aResult.cubicTo(
+ aPole1.X(), aPole1.Y(), aPole2.X(), aPole2.Y(), anEnd.X(), anEnd.Y());
+ } else { // error, another number of poles is not supported
+ return QPainterPath();
+ }
+ }
+ return aResult;
+}
--- /dev/null
+#ifndef HYDROOperations_BSpline_HeaderFile
+#define HYDROOperations_BSpline_HeaderFile
+
+#include <HYDROOperations.h>
+#include <QList>
+#include <QPainterPath>
+#include <Geom_BSplineCurve.hxx>
+
+/**\class HYDROOperations_BSpline
+ *
+ * \brief Allows to work with splines: create, convert to Qt ToolPath.
+ *
+ * Uses GEOM module for creation of BSplines, OCCT algorithms for
+ * manipulation and conversion.
+ */
+
+class HYDROOperations_BSpline
+{
+public:
+
+ //! Creates a spline by list of coordinates: pairs X and Y
+ //! \param thePoints coordinates in format X1, Y1, X2, Y2, etc. must be even number of elements
+ HYDROOPERATIONS_EXPORT HYDROOperations_BSpline(QList<double>& thePoints);
+
+ //! Returns the BSpline curve passing through the points
+ //! \returns Null if Computation of BSpline was failed
+ Handle(Geom_BSplineCurve) Curve() const {return myCurve;}
+
+ //! Performs conversion from BSpline curve to QPainterPath made from Bezier curves
+ //! \returns computed PainterPath, not stored in this class, so calling of this method is not fast
+ QPainterPath ComputePath() const;
+private:
+ Handle(Geom_BSplineCurve) myCurve; ///< resulting BSpline, null if something is wrong
+};
+
+#endif
--- /dev/null
+#include<HYDROOperations_Factory.h>
+
+#include<HYDROData_Document.h>
+#include<HYDROData_Iterator.h>
+
+#include<ImageComposer_ColorMaskOperator.h>
+#include<ImageComposer_CropOperator.h>
+#include<ImageComposer_CutOperator.h>
+#include<ImageComposer_FuseOperator.h>
+#include<ImageComposer_Image.h>
+
+// global instance
+HYDROOperations_Factory* FACTORY = 0;
+
+HYDROOperations_Factory* HYDROOperations_Factory::Factory()
+{
+ if (!FACTORY) {
+ FACTORY = new HYDROOperations_Factory;
+ // default operations
+ REGISTER_HYDRO_OPERATION(ImageComposer_ColorMaskOperator)
+ REGISTER_HYDRO_OPERATION(ImageComposer_CropOperator)
+ REGISTER_HYDRO_OPERATION(ImageComposer_CutOperator)
+ REGISTER_HYDRO_OPERATION(ImageComposer_FuseOperator)
+ }
+ return FACTORY;
+}
+
+ImageComposer_Operator* HYDROOperations_Factory::Operator(const QString theName) const
+{
+ if (myOps.contains(theName)) {
+ return myOps[theName];
+ }
+ return NULL;
+}
+
+void HYDROOperations_Factory::Register(
+ ImageComposer_Operator* theOperator)
+{
+ FACTORY->myOps[QString(typeid(*theOperator).name())] = theOperator;
+}
+
+HYDROOperations_Factory::HYDROOperations_Factory()
+{
+}
+
+ImageComposer_Operator* HYDROOperations_Factory::Operator(
+ Handle(HYDROData_Image) theImage) const
+{
+ // retreive operator instance by name
+ ImageComposer_Operator* anOp = Operator(theImage->OperatorName());
+ if (!anOp)
+ return anOp;
+ // fill arguments of the operator from theImage
+ theImage->Args();
+ anOp->setBinArgs(theImage->Args());
+ return anOp;
+}
+
+Handle(HYDROData_Image) HYDROOperations_Factory::CreateImage(
+ Handle(HYDROData_Document) theDoc, const ImageComposer_Operator* theOperator)
+{
+ // create an object
+ Handle(HYDROData_Image) anImage =
+ Handle(HYDROData_Image)::DownCast(theDoc->CreateObject(KIND_IMAGE));
+ // get data from operation
+ if (theOperator) {
+ anImage->SetOperatorName(QString(typeid(*theOperator).name()));
+ anImage->SetArgs(theOperator->getBinArgs());
+ }
+ return anImage;
+}
+
+void HYDROOperations_Factory::UpdateImage(
+ Handle_HYDROData_Document theDoc, Handle(HYDROData_Image) theImage)
+{
+ // fill by arguments and process the operation
+ ImageComposer_Operator* anOp = Operator(theImage);
+ if (anOp) { // update image only if there is an operation
+ ImageComposer_Image anImage1; // first referenced image
+ if (theImage->NbReferences()) {
+ Handle(HYDROData_Image) anImage = theImage->Reference(0);
+ anImage1 = anImage->Image();
+ anImage1.setTransform(anImage->Trsf());
+ }
+ ImageComposer_Image anImage2; // second referenced image
+ if (theImage->NbReferences() > 1) {
+ Handle(HYDROData_Image) anImage = theImage->Reference(1);
+ anImage2 = anImage->Image();
+ anImage2.setTransform(anImage->Trsf());
+ }
+ ImageComposer_Image aResImg = anOp->process(anImage1, anImage2);
+ theImage->SetImage(aResImg);
+ }
+ // change the states of this and all depended images
+ theImage->MustBeUpdated(true);
+ SetMustBeUpdatedImages(theDoc);
+ theImage->MustBeUpdated(false);
+}
+
+void HYDROOperations_Factory::SetMustBeUpdatedImages(
+ Handle_HYDROData_Document theDoc) const
+{
+ bool aChanged = true;
+ // iterate until there is no changes because images on all level of dependency must be updated
+ while(aChanged) {
+ aChanged = false;
+ HYDROData_Iterator anIter(theDoc, KIND_IMAGE);
+ for(; anIter.More(); anIter.Next()) {
+ Handle(HYDROData_Image) anImage =
+ Handle(HYDROData_Image)::DownCast(anIter.Current());
+ if (!anImage->MustBeUpdated()) {
+ int a, aNBRefs = anImage->NbReferences();
+ for(a = 0; a < aNBRefs; a++) {
+ if (anImage->Reference(a)->MustBeUpdated()) {
+ // image references to updated => also must be updated
+ anImage->MustBeUpdated(true);
+ aChanged = true;
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#ifndef HYDROOperations_Factory_HeaderFile
+#define HYDROOperations_Factory_HeaderFile
+
+#include <HYDROOperations.h>
+#include <HYDROData_Image.h>
+#include <QMap>
+
+class ImageComposer_Operator;
+class Handle_HYDROData_Document;
+
+/**\class HYDROOperations_Factory
+ *
+ * \brief This class provides the unified management of operations on images.
+ *
+ * Object is created as one global instance and allows to register and launch
+ * all registered operations in general way. To register a new operation just
+ * call REGISTER_HYDRO_OPERATION(operation_name) in some method implementation.
+ * This macro will call constructor of this operation (it must inherit
+ * ImageComposer_Operator) and factory will set up arguments and call this
+ * operator by demand.
+ */
+
+class HYDROOperations_Factory
+{
+public:
+
+ //! Returns the global factory
+ HYDROOPERATIONS_EXPORT static HYDROOperations_Factory* Factory();
+
+ /**
+ * Registers the operator by the name, used by REGISTER_HYDRO_OPERATION macro
+ * \param theOperator new instance of the operator that will be used for
+ * processing of operation with such kind
+ */
+ HYDROOPERATIONS_EXPORT static void Register(
+ ImageComposer_Operator* theOperator);
+
+ /**
+ * Creates a new Image object in the data structure by the operator data.
+ * \param theDoc document where it must be created
+ * \param theOperator base operator for this Image: will be used in "Update" to recompute the image
+ * \returns created object related to the data structure
+ */
+ HYDROOPERATIONS_EXPORT Handle(HYDROData_Image) CreateImage(
+ Handle_HYDROData_Document theDoc, const ImageComposer_Operator* theOperator);
+
+ /**
+ * Updates an Image object in the data structure. If it is changed,
+ * sets "MustBeUpdated" flag to other depended images.
+ * \param theDoc document of this image (needed to update other images flags)
+ * \param theImage the updated image
+ */
+ HYDROOPERATIONS_EXPORT void UpdateImage(
+ Handle_HYDROData_Document theDoc, Handle(HYDROData_Image) theImage);
+
+ /**
+ * Returns the operator, initialized by the properties of theImage
+ * \param theImage data structures object, that contains all arguments
+ * required for creation of operation
+ * \returns NULL if operator type is unknown
+ */
+ HYDROOPERATIONS_EXPORT ImageComposer_Operator* Operator(
+ Handle(HYDROData_Image) theImage) const;
+
+
+protected:
+
+ //! Not public constructor that creates only one, global instance of this factory.
+ HYDROOperations_Factory();
+
+ /**
+ * Returns the appropriate operator by the name
+ * \param theName name of the operator, equals to the operation_name constructor
+ * \returns NULL if operator with such name is not registered yet
+ */
+ ImageComposer_Operator* Operator(const QString theName) const;
+
+ /**
+ * Enables "MustBeUpdated" flag for Images that are depended on "MustBeUpdated" images.
+ * \param theDoc document where this operation is performed
+ */
+ void SetMustBeUpdatedImages(Handle_HYDROData_Document theDoc) const;
+
+private:
+ //! Map that stores all operators, isentified by strings
+ typedef QMap<QString, ImageComposer_Operator*> FactoryOperators;
+
+ FactoryOperators myOps; ///< all operators stored by a factory
+};
+
+/**
+ * Macro that is used for registered operators, see C++ of this class to see
+ * example of hte registration.
+ */
+#define REGISTER_HYDRO_OPERATION(operation_name) \
+ HYDROOperations_Factory::Factory()->Register(new operation_name);
+
+#endif
--- /dev/null
+#include<test_HYDROOperations_BSpline.h>
+
+#include <HYDROOperations_BSpline.h>
+#include <gp_Pnt.hxx>
+#include <QTransform>
+
+void test_HYDROOperations_BSpline::testCurve()
+{
+ // prepare points: function of sin(x)
+ QList<double> aPoints;
+ double x;
+ for(x = 0; x < 6.28; x += 0.1)
+ aPoints<<x<<sin(x);
+ // compute BSpline
+ HYDROOperations_BSpline aBSpline(aPoints);
+ Handle(Geom_BSplineCurve) aBS = aBSpline.Curve();
+ CPPUNIT_ASSERT(!aBS.IsNull());
+ CPPUNIT_ASSERT(!aBS->IsClosed());
+ CPPUNIT_ASSERT_EQUAL(aBS->Continuity(), GeomAbs_C2);
+ // check that values of BSpline are not far from original "sin" function
+ // in all points of the curve
+ for(x = 0; x < 6.29; x += 0.001) {
+ double aDiff = aBS->Value(x).Y() - sin(aBS->Value(x).X());
+ if (aDiff < 0) aDiff = -aDiff;
+ CPPUNIT_ASSERT(aDiff < 3.e-6); // this number is found manually
+ }
+}
+
+void test_HYDROOperations_BSpline::testPath()
+{
+ // prepare points: function of sin(x)
+ static const double aScale = 10000000.;
+ QList<double> aPoints;
+ double x;
+ for(x = 0; x < 6.28; x += 0.1)
+ aPoints<<x*aScale<<sin(x) * aScale;
+ // convert to QPainterPath
+ HYDROOperations_BSpline aBSpline(aPoints);
+ CPPUNIT_ASSERT(!aBSpline.Curve().IsNull());
+ QPainterPath aPath = aBSpline.ComputePath();
+ CPPUNIT_ASSERT(!aPath.isEmpty());
+
+ /*
+ QImage aPic(1300, 600, QImage::Format_RGB32);
+ QPainter aPainter(&aPic);
+ aPainter.setBrush(QBrush(Qt::white));
+ aPainter.drawPath(aPath);
+ aPic.save("pic.bmp");
+ */
+
+ // check that values of Path are not far from original "sin" function
+ // in all points of the curve
+ QList<QPolygonF> aPolyF = aPath.toSubpathPolygons(QTransform());
+ QList<QPolygonF>::iterator aFIter = aPolyF.begin();
+ for(; aFIter != aPolyF.end();aFIter++) {
+ QPolygon aPoly = aFIter->toPolygon();
+ QPolygon::iterator aPoints = aPoly.begin();
+ for(; aPoints != aPoly.end(); aPoints++) {
+ double aDiff = aPoints->y() / aScale - sin(aPoints->x() / aScale);
+ if (aDiff < 0) aDiff = -aDiff;
+ CPPUNIT_ASSERT(aDiff < 4.e-6); // this number is found manually
+ }
+ }
+}
--- /dev/null
+#include <cppunit/extensions/HelperMacros.h>
+
+class test_HYDROOperations_BSpline : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(test_HYDROOperations_BSpline);
+ CPPUNIT_TEST(testCurve);
+ CPPUNIT_TEST(testPath);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+
+public:
+
+ void setUp() {}
+
+ void tearDown() {}
+
+ // checks generation of BSpline curve by points
+ void testCurve();
+
+ // checks generation of QPainterPath
+ void testPath();
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_HYDROOperations_BSpline);
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_HYDROOperations_BSpline, "HYDROOperations_BSpline");
--- /dev/null
+#include <test_HYDROOperations_Factory.h>
+
+#include <HYDROData_Document.h>
+#include <HYDROData_Image.h>
+#include <HYDROOperations_Factory.h>
+
+#include <QPainter>
+#include <ImageComposer_CropOperator.h>
+
+void test_HYDROOperations_Factory::testCreate()
+{
+ Handle(HYDROData_Document) aDoc = HYDROData_Document::Document(1);
+
+ HYDROOperations_Factory* aFactory = HYDROOperations_Factory::Factory();
+ CPPUNIT_ASSERT(aFactory);
+ Handle(HYDROData_Image) anImage = aFactory->CreateImage(aDoc, NULL);
+ CPPUNIT_ASSERT(!anImage.IsNull());
+ CPPUNIT_ASSERT(anImage->Image().isNull());
+
+ aDoc->Close();
+}
+
+static QImage TestImage() {
+ QImage aPic(50, 40, QImage::Format_RGB32);
+ aPic.fill(Qt::white);
+ QPainter aPainter(&aPic);
+ aPainter.drawEllipse(6, 7, 38, 30);
+ aPainter.drawLine(0, 40, 10, 0);
+ aPainter.drawLine(10, 0, 25, 35);
+ aPainter.drawLine(25, 35, 40, 0);
+ aPainter.drawLine(40, 0, 50, 40);
+ return aPic;
+}
+
+void test_HYDROOperations_Factory::testCrop()
+{
+ Handle(HYDROData_Document) aDoc = HYDROData_Document::Document(1);
+ HYDROOperations_Factory* aFactory = HYDROOperations_Factory::Factory();
+ Handle(HYDROData_Image) anImage = aFactory->CreateImage(aDoc, NULL);
+ // prepare the original image and crop-path
+ QImage aPic = TestImage();
+ anImage->SetImage(aPic);
+ QPainterPath aPath(QPointF(25, 0));
+ aPath.lineTo(0, 20);
+ aPath.lineTo(25, 40);
+ aPath.lineTo(50, 20);
+ aPath.closeSubpath();
+ // prepare Composer Operation
+ ImageComposer_CropOperator aCropOp;
+ aCropOp.setArgs(Qt::red, aPath);
+ // create crop - image
+ Handle(HYDROData_Image) aCrop = aFactory->CreateImage(aDoc, &aCropOp);
+ CPPUNIT_ASSERT(!aCrop.IsNull());
+ aCrop->AppendReference(anImage);
+ aFactory->UpdateImage(aDoc, aCrop);
+ // check crop operation was performed
+ CPPUNIT_ASSERT(!aCrop->Image().isNull());
+ CPPUNIT_ASSERT(aCrop->Image() != aPic);
+
+ aDoc->Close();
+}
--- /dev/null
+#include <cppunit/extensions/HelperMacros.h>
+
+class test_HYDROOperations_Factory : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(test_HYDROOperations_Factory);
+ CPPUNIT_TEST(testCreate);
+ CPPUNIT_TEST(testCrop);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+
+public:
+
+ void setUp() {}
+
+ void tearDown() {}
+
+ // checks creation of images using null operators
+ void testCreate();
+
+ // checks creation of images using crop operator
+ void testCrop();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_HYDROOperations_Factory);
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(test_HYDROOperations_Factory, "HYDROOperations_Factory");
--- /dev/null
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/CompilerOutputter.h>
+#include <cppunit/TestResult.h>
+#include <cppunit/TestResultCollector.h>
+#include <cppunit/TestRunner.h>
+#include <cppunit/TextTestProgressListener.h>
+#include <stdexcept>
+#include <QApplication>
+
+int
+ main( int argc, char* argv[] )
+{
+ // to perform "drawing" qt tests
+ QApplication app(argc, argv);
+
+ std::string testPath = (argc > 1) ? std::string(argv[1]) : "";
+
+ // Create the event manager and test controller
+ CppUnit::TestResult controller;
+
+ // Add a listener that colllects test result
+ CppUnit::TestResultCollector result;
+ controller.addListener( &result );
+
+ // Add a listener that print dots as test run.
+ CppUnit::TextTestProgressListener progress;
+ controller.addListener( &progress );
+
+ CppUnit::TestFactoryRegistry& registry =
+ CppUnit::TestFactoryRegistry::getRegistry();
+ // Add the top suite to the test runner
+ CppUnit::TestRunner runner;
+ runner.addTest( registry.makeTest() );
+ try
+ {
+ std::cout << "Running " << testPath;
+ runner.run( controller, testPath );
+
+ std::cerr << std::endl;
+
+ // Print test in a compiler compatible format.
+ CppUnit::CompilerOutputter outputter( &result, std::cerr );
+ outputter.write();
+ }
+ catch ( std::invalid_argument &e ) // Test path not resolved
+ {
+ std::cerr << std::endl
+ << "ERROR: " << e.what()
+ << std::endl;
+ return 0;
+ }
+
+ return result.wasSuccessful() ? 0 : 1;
+}