]> SALOME platform Git repositories - modules/hydro.git/commitdiff
Salome HOME
HYDROOperations: initial development and tests
authormpv <mpv@opencascade.com>
Thu, 18 Jul 2013 07:37:31 +0000 (07:37 +0000)
committermpv <mpv@opencascade.com>
Thu, 18 Jul 2013 07:37:31 +0000 (07:37 +0000)
16 files changed:
CMakeLists.txt
src/HYDROData/HYDROData_Image.cxx
src/HYDROData/HYDROData_Image.h
src/HYDROData/HYDROData_Object.cxx
src/HYDROData/HYDROData_Object.h
src/HYDROOperations/CMakeLists.txt [new file with mode: 0644]
src/HYDROOperations/HYDROOperations.h [new file with mode: 0644]
src/HYDROOperations/HYDROOperations_BSpline.cxx [new file with mode: 0644]
src/HYDROOperations/HYDROOperations_BSpline.h [new file with mode: 0644]
src/HYDROOperations/HYDROOperations_Factory.cxx [new file with mode: 0644]
src/HYDROOperations/HYDROOperations_Factory.h [new file with mode: 0644]
src/HYDROOperations/test_HYDROOperations_BSpline.cxx [new file with mode: 0644]
src/HYDROOperations/test_HYDROOperations_BSpline.h [new file with mode: 0644]
src/HYDROOperations/test_HYDROOperations_Factory.cxx [new file with mode: 0644]
src/HYDROOperations/test_HYDROOperations_Factory.h [new file with mode: 0644]
src/HYDROOperations/test_HYDROOperations_Main.cxx [new file with mode: 0644]

index e50f8bb6f8e72764e7b36147e46fea1c3a30ccfd..bcda5f39a68c4ada8d6dc614bee7e4b5c70efeb6 100644 (file)
@@ -22,5 +22,6 @@ find_package(SalomeCAS)
 find_package(SalomeCPPUNIT)
 
 add_subdirectory (src/HYDROData)
+add_subdirectory (src/HYDROOperations)
 
 enable_testing()
index 62e1ba5311df9861433fd9a91be4aa2c33ad1edb..0d1da6761f1b9e1e8fd86725d2a2ac75adf4c3a7 100644 (file)
@@ -2,11 +2,17 @@
 #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)
 
@@ -36,47 +42,21 @@ void HYDROData_Image::SetImage(const QImage& theImage)
   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;
 }
 
@@ -184,3 +164,48 @@ void HYDROData_Image::ClearReferences()
 {
   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);
+}
index 38e48c19cb16816a1c1a183290b190dbba1b51c1..167d8b54767833d670de08c32ebe3f96804392c4 100644 (file)
@@ -85,6 +85,42 @@ public:
    */
   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;
index 488b03ef973faf061417d121b977fce416089cfa..46b4acc49e49863b96a74bb65cfa1c7778ac0a70 100644 (file)
@@ -1,6 +1,7 @@
 #include <HYDROData_Object.h>
 
 #include <TDataStd_Name.hxx>
+#include <TDataStd_ByteArray.hxx>
 #include <TDF_CopyLabel.hxx>
 
 IMPLEMENT_STANDARD_HANDLE(HYDROData_Object,MMgt_TShared)
@@ -55,3 +56,41 @@ void HYDROData_Object::SetLabel(TDF_Label theLabel)
 {
   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;
+}
index 852c06795d82bbd3ba6b34e9c974ceceda661f4f..763153a354c764adb884fa391c5a2bb695046780 100644 (file)
@@ -89,6 +89,23 @@ protected:
    * 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.
diff --git a/src/HYDROOperations/CMakeLists.txt b/src/HYDROOperations/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a6a8ac0
--- /dev/null
@@ -0,0 +1,53 @@
+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)
diff --git a/src/HYDROOperations/HYDROOperations.h b/src/HYDROOperations/HYDROOperations.h
new file mode 100644 (file)
index 0000000..feeaec5
--- /dev/null
@@ -0,0 +1,18 @@
+#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
diff --git a/src/HYDROOperations/HYDROOperations_BSpline.cxx b/src/HYDROOperations/HYDROOperations_BSpline.cxx
new file mode 100644 (file)
index 0000000..376490d
--- /dev/null
@@ -0,0 +1,58 @@
+#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;
+}
diff --git a/src/HYDROOperations/HYDROOperations_BSpline.h b/src/HYDROOperations/HYDROOperations_BSpline.h
new file mode 100644 (file)
index 0000000..20b9c85
--- /dev/null
@@ -0,0 +1,36 @@
+#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
diff --git a/src/HYDROOperations/HYDROOperations_Factory.cxx b/src/HYDROOperations/HYDROOperations_Factory.cxx
new file mode 100644 (file)
index 0000000..974155b
--- /dev/null
@@ -0,0 +1,123 @@
+#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;
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/HYDROOperations/HYDROOperations_Factory.h b/src/HYDROOperations/HYDROOperations_Factory.h
new file mode 100644 (file)
index 0000000..0416e3b
--- /dev/null
@@ -0,0 +1,98 @@
+#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
diff --git a/src/HYDROOperations/test_HYDROOperations_BSpline.cxx b/src/HYDROOperations/test_HYDROOperations_BSpline.cxx
new file mode 100644 (file)
index 0000000..b17590e
--- /dev/null
@@ -0,0 +1,64 @@
+#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
+    }
+  }
+}
diff --git a/src/HYDROOperations/test_HYDROOperations_BSpline.h b/src/HYDROOperations/test_HYDROOperations_BSpline.h
new file mode 100644 (file)
index 0000000..6eb1105
--- /dev/null
@@ -0,0 +1,26 @@
+#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");
diff --git a/src/HYDROOperations/test_HYDROOperations_Factory.cxx b/src/HYDROOperations/test_HYDROOperations_Factory.cxx
new file mode 100644 (file)
index 0000000..0695cc3
--- /dev/null
@@ -0,0 +1,61 @@
+#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();
+}
diff --git a/src/HYDROOperations/test_HYDROOperations_Factory.h b/src/HYDROOperations/test_HYDROOperations_Factory.h
new file mode 100644 (file)
index 0000000..fa7e917
--- /dev/null
@@ -0,0 +1,25 @@
+#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");
diff --git a/src/HYDROOperations/test_HYDROOperations_Main.cxx b/src/HYDROOperations/test_HYDROOperations_Main.cxx
new file mode 100644 (file)
index 0000000..9590083
--- /dev/null
@@ -0,0 +1,54 @@
+#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;
+}