Dynamic Loading and Unloading Of Raster Images Depending On Zoom Scale

By Gopinath Taget

Lets say you want to control the loading and unloading of the Raster images when the user is zooming or panning. The Images should load or unload on two factors:
1) The zoom scale.
2) The Image is in visible area or not.

This can be achieved by adding an editor reactor that checks for the command “ZOOM” and “PAN”. If the user has invoked either of the command then do the following:

1) Scan for all the Raster Images in the drawing.
2) Check if a Raster Image is fully, partially or non visible in the current view.
3) If the Raster Image is fully or partially visible, check for ratio (Image diagonal /View diagonal). If this ratio lies with in the specified limit, then load the Image otherwise unload it. Also unload the image if it does not lie in the current view.

To check for visibility of the Raster Image in the current view:
1) Build a transformation matrix for the DCS coordinate system.
2) Get the view centre using VIEWCTR sysvar. This lies in the current UCS. Project the point onto the DCS XY plane.
3) Get the view height using VIEWSIZE and calculate the view width using the window aspect ratio "SCREENSIZE".
4) Calculate the extents in DCS XY plane using view centre and height/width.
5) To check if an entity lies fully in the current view, get the bounding box of the entity and project the maximum and minimum points of the extents onto DCS XY plane. Check if the projected points lie fully in the bounding box defined by DCS extents. If they do the entity is fully visible in the current view else check if the DCS extents lies fully in the entities extents bounding box. If yes then it means that the image fully covers the current view. If not, go to next step.
6) Create a polyline that forms rectangle with DCS extents as the corners. Test for the intersection of the polyline and the projected entity in DCS XY plane. If they intersect, the entity is partially visible. If not then the entity is not visible at all.
There are two major functions in the code provided below. First one is fCheckEntitiesInDispArea(). This function takes an object ID array as in parameter. It checks for visibility of the entities specified in the array and populates three object ID arrays and they contain entities that are fully visible, partially visible and not visible at all, respectively. The second function is fTest(), which actually iterates the AcDbRasterImage entities in the model space and then tests for the visibility, checks for the diagonal size of the image and loads or unloads depending on the size.

The snippets provided below has the complete code to do all the above. In the code, I defines a command "test" and also implements an editor reactor that reacts to "ZOOM" and “PAN" command. The program essentially unloads any the raster image which is smaller than 1/5 size and bigger than 10 times the size of the current view and also if they are not lying in the current view. The command “setratio” allows the user to change the minimum and maximum Image/View diagonal ratio.

Finally, this blog post will be useful in case you need to react to Real-time Zoom also:

 

///////////////////////////////////////////////////////////////////////////    //global variable to hold the max and minimum valid size for the raster    double gnMaxSize = 10.0;   //Raster should not ten times the screen size    double gnMinSize = 0.20; //Raster should not less than 1/5th of screen size    ///////////////////////////////////////////////////////////////////////////         //globals    //editor reactor    AsdkEdReactor *pEdReact = NULL;         //========================================================    //========================================================    void fTest()    {     Acad::ErrorStatus mEs;     AcDbBlockTable *pBT;     AcDbBlockTableRecord *pBTR;     AcDbBlockTableRecordIterator *pBTIter;          acdbHostApplicationServices()->workingDatabase()->      getBlockTable(pBT,AcDb::kForRead);     pBT->getAt(ACDB_MODEL_SPACE,pBTR,AcDb::kForRead);     pBTR->newIterator(pBTIter);     pBT->close();     pBTR->close();          //array to hold the entities     AcDbObjectIdArray aNoDispEntArray;     AcDbObjectIdArray mFullDispEntArray;     AcDbObjectIdArray mPartialDispArray;     AcDbEntity *pEnt;          AcDbRasterImage *pRastImg;          //populate the aNoDispEntArray with all      //entites that are RasterImage     while (!(pBTIter->done()))     {      pBTIter->getEntity(pEnt,AcDb::kForRead);      pRastImg = AcDbRasterImage::cast(pEnt);           if(NULL != pRastImg)      {       aNoDispEntArray.append(pRastImg->objectId());           }      pEnt->close();      pBTIter->step();     }          //check for display of entities in the current view     fCheckEntitiesInDispArea(aNoDispEntArray,      mFullDispEntArray,mPartialDispArray);          acutPrintf(      _T("nNo of Images not lying in the current View : %d"),      aNoDispEntArray.length());     acutPrintf(      _T("nNo of Images lying fully in the current View : %d"),      mFullDispEntArray.length());     acutPrintf(      _T("nNo of Images lying partially in the curre
nt View : %d"),      mPartialDispArray.length());          //unload the images depending on their visibility     long mCtr; //number of entities     AcDbRasterImageDef *pImgDef;     for(mCtr = 0;mCtr < aNoDispEntArray.length();mCtr++)     {      mEs = acdbOpenObject(pRastImg,       aNoDispEntArray.at(mCtr),AcDb::kForRead);      if(Acad::eOk == acdbOpenObject(pImgDef,       pRastImg->imageDefId(),AcDb::kForRead))      {       if(Adesk::kTrue == pImgDef->isLoaded())       {        pImgDef->upgradeOpen();        pImgDef->unload();       }       pImgDef->close();      }      pRastImg->close();     }          //load or unload depending upon the VIEWSIZE      //for fully visible or partially visible entities     //get the view center     struct resbuf rs;     double nHeight;     double nWidth;     double nScreenDiag;          //get the view height     acedGetVar(_T("VIEWSIZE"),&rs);     nHeight = rs.resval.rreal;     //get the screen size     acedGetVar(_T("SCREENSIZE"),&rs);     nWidth = nHeight*(rs.resval.rpoint[0] / rs.resval.rpoint[1]);     //get the dialgonal length     nScreenDiag = sqrt(pow(nHeight,2) + pow(nWidth,2));          //append the full display entities     //with the partial displayed entities     //as we want similar behaviuor for both     for(mCtr = 0;mCtr < mPartialDispArray.length();mCtr++)     {      mFullDispEntArray.append(mPartialDispArray.at(mCtr));     }          //hold Object ID from the array     AcDbObjectId aObjID;     double nDiagRatio;          for(mCtr = 0;mCtr < mFullDispEntArray.length();mCtr++)     {      AcDbExtents mImgExts;           //try opening the object for read      if(Acad::eOk != acdbOpenObject(pRastImg,       mFullDispEntArray.at(mCtr),AcDb::kForRead))        continue;      aObjID = pRastImg->imageDefId();      pRastImg->getGeomExtents(mImgExts);      pRastImg->close();           //open the image definition      if(Acad::eOk
!= acdbOpenObject(pImgDef,aObjID,       AcDb::kForRead))       continue;           //the ratio is equal to raster size/screen size      nDiagRatio = (mImgExts.maxPoint().       distanceTo(mImgExts.minPoint())/nScreenDiag);           //print the values on screen       //char buf[255];//wb      ACHAR buf[255];      aObjID.handle().getIntoAsciiBuffer(buf);      acutPrintf(_T("nImage handle: %s , Diagonal Ratio: %f"),       buf,nDiagRatio);      //           //if the Image diagonal is less than       //gnMinSize or greater gnMaxSize, then unload      if((nDiagRatio >= gnMinSize) &&        (nDiagRatio isLoaded())       {        pImgDef->upgradeOpen();           pImgDef->load(); //image is sufficently zoomed       }      }      else      {       if(Adesk::kTrue == pImgDef->isLoaded())       {        pImgDef->upgradeOpen();        //image is too small or too large to display        pImgDef->unload();        }      }           pImgDef->close();     }          //clean up     delete pBTIter;    }         ////////////////////////////////////////////////////////////////    //Description: fCheckEntitiesInDispArea    ////////////////////////////////////////////////////////////////    void fCheckEntitiesInDispArea(AcDbObjectIdArray &aNoDispEntArray,     AcDbObjectIdArray &aFullDispEntArray,     AcDbObjectIdArray &aPartDispEntArray)    {     //get the display properties     CDcsProp mDcsPty;     //create a pline to check for the interference     AcDbPolyline *paPline = new AcDbPolyline(4);          fGetDCSBoundary(&mDcsPty,paPline);          long mCtr; //number of entitie     AcDbObjectId aObjID;     AcDbObjectIdArray mTempEntArray;          for(mCtr = 0;mCtr < aNoDispEntArray.length();mCtr++)     {      aObjID = aNoDispEntArray.at(mCtr);           //check if entity lies completely in the current display area      if(Adesk::kTrue== fLiesCompleteyInView(aObjID,mDcsPty))      {       aFullDispEntArray.append(aObjID);      }      else    &#1
60; {       //check if it lies partially       if(Adesk::kTrue== fLiesPartiallyInView(aObjID,        paPline,mDcsPty))       {        aPartDispEntArray.append(aObjID);       }       else        {         //completely out of display        mTempEntArray.append(aObjID);       }      }          }          aNoDispEntArray = mTempEntArray;     //clean up     delete paPline;    }         //////////////////////////////////////////////////////////////    //Description: fGetDCSBoundary    //////////////////////////////////////////////////////////////    void fGetDCSBoundary(CDcsProp *paDcsPty,AcDbPolyline *paPline)    {     AcGeMatrix3d mMatDCS2WCS;     AcGePoint3d mPtMax;        AcGePoint3d mPtMin;          //returns DCS2WCS matrix and the extents in DCS coords     fGetViewCornersInDcs(mMatDCS2WCS,mPtMax,mPtMin);          //get the projection plane     AcGePlane pPlnDCSXY = fGetXYPlane(mMatDCS2WCS);     //create a box     paPline->addVertexAt(0,AcGePoint2d(mPtMin.x,mPtMin.y));     paPline->addVertexAt(1,AcGePoint2d(mPtMax.x,mPtMin.y));     paPline->addVertexAt(2,AcGePoint2d(mPtMax.x,mPtMax.y));     paPline->addVertexAt(3,AcGePoint2d(mPtMin.x,mPtMax.y));     paPline->setClosed(Adesk::kTrue);     paPline->transformBy(mMatDCS2WCS);          //add the pline to MS     //fAddToMS((AcDbEntity *)pPolyline);          //prepare the DCS poperties     paDcsPty->m_matMatrix = mMatDCS2WCS;     paDcsPty->fCalculate();     paDcsPty->m_maxPt = mPtMax;     paDcsPty->m_minPt = mPtMin;    }         //////////////////////////////////////////////////////////////    //Description: fLiesPartiallyInView    //////////////////////////////////////////////////////////////    Adesk::Boolean fLiesPartiallyInView(AcDbObjectId aObjID,     AcDbPolyline *paPline,const CDcsProp aDcsPty)    {     //area, so check if lies partially     AcGePoint3dArray mInterPoints;     AcDbEntity *pEnt;          //try opening the object for read     if(Acad::eOk != acdbOpenObject(pEnt,aObjID,      AcDb::kForRead)) return Adesk::kFalse;          //check for intersection     paPline->intersectWith(pEnt,AcDb::kOnBothOperands,      aDcsPty.m_PlnXY ,mInterPoints);          //close the entity     pEnt->close();          //how many points are intersecting?     if (0 != mInterPoints.length())      return Adesk::kTrue;  //intersects     else      return Adesk::kFalse; //does not intersect    }         /////////////////////////////////////////////////////////    //Description: fLiesCompleteyInView    /////////////////////////////////////////////////////////    Adesk::Boolean fLiesCompleteyInView(AcDbObjectId aObjID,     const CDcsProp aDcsPty)    {     AcDbEntity *pEnt;          //try opening the object for read     if(Acad::eOk != acdbOpenObject(      pEnt,aObjID,AcDb::kForRead)) return Adesk::kFalse;          //check if the entity lies in the display area completely     AcDbExtents mEntExts;     pEnt->getGeomExtents(mEntExts);          //close the entity     pEnt->close();          AcGePoint3d mPtExtMax;        AcGePoint3d mPtExtMin;          mPtExtMax = mEntExts.maxPoint();     mPtExtMin = mEntExts.minPoint();          mPtExtMax = mPtExtMax.project(aDcsPty.m_PlnXY,      aDcsPty.m_PlnXY.normal());     mPtExtMin = mPtExtMin.project(aDcsPty.m_PlnXY,      aDcsPty.m_PlnXY.normal());          //transform the extents     mPtExtMax.transformBy(aDcsPty.m_matMatrix.inverse());     mPtExtMin.transformBy(aDcsPty.m_matMatrix.inverse());          //lies in the display area     if((aDcsPty.m_minPt.x

Here is the code for the reactor:

//—————————————————————

void AsdkEdReactor::commandEnded(const ACHAR* cmdStr)

{

 // TODO: implement this function.

 if ((!_tcscmp(cmdStr,_T("ZOOM"))||!_tcscmp(cmdStr,_T("PAN"))))

{

  fTest();

}

}


Comments

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading