July 14, 2013

Quản lý giao dịch Transaction (Phần 3) - Ví dụ

Ví dụ về giao dịch lồng nhau

Ví dụ dưới đây bao gồm 3 giao dịch lồng nhau. Chuỗi sự kiện như sau:
 
Để tạo một giao dịch lồng mới
  1. Tạo một đa giác mới và đưa nó vào trong Cơ sở dữ liệu.
  1. Bắt đầu giao dịch 1:
  • Lựa chọn đa giác và lấy con trỏ tới nó. Mở để đọc.
  • Tạo một khối đặc (extruded solid) từ đa giác.
  • Tạo một khối trụ ở giữa đa giác mở rộng.
  1. Bắt đầu giao dịch 2: Trừ khối trụ từ khối đặc (Tạo một lỗ hổng ở giữa khối đặc).

  2. Bắt đầu giao dịch 3:
  • Cắt một nửa khuôn mẫu dọc theo mặt phẳng X/Z và di chuyển nó quanh trục X để bạn có thể nhìn thấy hai mảnh.
  • Hủy bỏ giao dịch được không nhỉ? Câu trả lời là có.
  1. Bắt đầu giao dịch 3 (bắt đầu lai): Cắt khuôn mẫu dọc theo mặt phẳng Y/Z và di chuyển nó quanh trục Y.
  2. Kết thúc giao dịch 3.
  3. Kết thúc giao dịch 2.

Chú ý: Nếu bạn muốn hủy bỏ tại điểm này, Giao dịch 2 và 3 cũng bị hủy đồng thời. Nếu bạn hủy bỏ một giao dịch vòng ngoài, mọi giao dịch lồng bên trong cũng bị hủy theo, thậm chí nếu chúng đã kết thúc thành công trước đó.
  1. Kết thúc giao dịch 1.
Dưới đây là mã nguồn của ví dụ:

void
transactCommand()
{
Adesk::Boolean interrupted;
Acad::ErrorStatus es = Acad::eOk;
AcDbObjectId savedCylinderId,savedExtrusionId;

// Create a poly and post it to the database.
//
acutPrintf("\nCreating a poly...Please supply the"
" required input.");
if ((es = createAndPostPoly()) != Acad::eOk)
return;

// Start a transaction.
//
AcTransaction *pTrans
= actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n###### Started transaction one."
" ######\n");

// Select the poly and extrude it.
//
AcDbObject *pObj = NULL;
AsdkPoly *pPoly = NULL;
AcDb3dSolid *pSolid = NULL;
AcDbObjectId objId;
ads_name ename;
ads_point pickpt;

for (;;) {
switch (acedEntSel("\nSelect a polygon: ",
ename, pickpt))
{
case RTNORM:
acdbGetObjectId(objId, ename);

if ((es = pTrans->getObject(pObj, objId,
AcDb::kForRead)) != Acad::eOk)
{
acutPrintf("\nFailed to obtain an object"
" through transaction.");
actrTransactionManager->abortTransaction();
return;
}

assert(pObj != NULL);
pPoly = AsdkPoly::cast(pObj);

if (pPoly == NULL) {
acutPrintf("\nNot a polygon. Try again");
continue;
}
break;
case RTNONE:
case RTCAN:
actrTransactionManager->abortTransaction();
return;
default:
continue;
}
break;
}

// Now that a poly is created, convert it to a region
// and extrude it.
//
acutPrintf("\nWe will be extruding the poly.");
AcGePoint2d c2d = pPoly->center();
ads_point pt;
pt[0] = c2d[0]; pt[1] = c2d[1]; pt[2] = pPoly->elevation();
acdbEcs2Ucs(pt,pt,asDblArray(pPoly->normal()),Adesk::kFalse);
double height;

if (acedGetDist(pt, "\nEnter Extrusion height: ",
&height) != RTNORM)
{
actrTransactionManager->abortTransaction();
return;
}

if ((es = extrudePoly(pPoly, height,savedExtrusionId)) !=
Acad::eOk) {
actrTransactionManager->abortTransaction();
return;
}

// Create a cylinder at the center of the polygon of
// the same height as the extruded poly.
//
double radius = (pPoly->startPoint()
- pPoly->center()).length() * 0.5;
pSolid = new AcDb3dSolid;
assert(pSolid != NULL);

pSolid->createFrustum(height, radius, radius, radius);
AcGeMatrix3d mat(AcGeMatrix3d::translation(
pPoly->elevation()*pPoly->normal())*
AcGeMatrix3d::planeToWorld(pPoly->normal()));
pSolid->transformBy(mat);

// Move it up again by half the height along
// the normal.
//
AcGeVector3d x(1, 0, 0), y(0, 1, 0), z(0, 0, 1);
AcGePoint3d moveBy(pPoly->normal()[0] * height * 0.5,
pPoly->normal()[1] * height * 0.5,
pPoly->normal()[2] * height * 0.5);
mat.setCoordSystem(moveBy, x, y, z);
pSolid->transformBy(mat);
addToDb(pSolid, savedCylinderId);
actrTransactionManager
->addNewlyCreatedDBRObject(pSolid);
pSolid->draw();
acutPrintf("\nCreated a cylinder at the center of"
" the poly.");

// Start another transaction. Ask the user to select
// the extruded solid followed by selecting the
// cylinder. Make a hole in the extruded solid by
// subtracting the cylinder from it.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n###### Started transaction two."
" ######\n");
AcDb3dSolid *pExtrusion, *pCylinder;

if ((es = getASolid("\nSelect the extrusion: ", pTrans,
AcDb::kForWrite, savedExtrusionId, pExtrusion))
!= Acad::eOk)
{
actrTransactionManager->abortTransaction();
actrTransactionManager->abortTransaction();
return;
}

assert(pExtrusion != NULL);

if ((es = getASolid("\nSelect the cylinder: ", pTrans,
AcDb::kForWrite, savedCylinderId, pCylinder))
!= Acad::eOk)
{
actrTransactionManager->abortTransaction();
actrTransactionManager->abortTransaction();
return;
}

assert(pCylinder != NULL);
pExtrusion->booleanOper(AcDb::kBoolSubtract, pCylinder);
pExtrusion->draw();
acutPrintf("\nSubtracted the cylinder from the"
" extrusion.");

// At this point, the cylinder is a NULL solid. Erase it.
//
assert(pCylinder->isNull());
pCylinder->erase();

// Start another transaction and slice the resulting
// solid into two halves.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n##### Started transaction three."
" ######\n");

AcGeVector3d vec, normal;
AcGePoint3d sp,center;
pPoly->getStartPoint(sp);
pPoly->getCenter(center);
vec = sp - center;
normal = pPoly->normal().crossProduct(vec);
normal.normalize();
AcGePlane sectionPlane(center, normal);
AcDb3dSolid *pOtherHalf = NULL;
pExtrusion->getSlice(sectionPlane, Adesk::kTrue,
pOtherHalf);
assert(pOtherHalf != NULL);

// Move the other half three times the vector length
// along the vector.
//
moveBy.set(vec[0] * 3.0, vec[1] * 3.0, vec[2] * 3.0);
mat.setCoordSystem(moveBy, x, y, z);
pOtherHalf->transformBy(mat);
AcDbObjectId otherHalfId;
addToDb(pOtherHalf, otherHalfId);

actrTransactionManager
->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();
acutPrintf("\nSliced the resulting solid into half"
" and moved one piece.");

// Now abort transaction three, to return to the hole in
// the extrusion.
//
Adesk::Boolean yes = Adesk::kTrue;
if (getYOrN("\nLet's abort transaction three, yes?"
" [Y] : ", Adesk::kTrue, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction"
" three. $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to the un-sliced solid.");
pExtrusion->draw();
char option[256];
acedGetKword("\nHit any key to continue.", option);
} else {
acutPrintf("\n\n>>>>>> Ending transaction three."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}

// Start another transaction (three again). This time,
// slice the solid along a plane that is perpendicular
// to the plane we used last time: that is the slice
// we really wanted.
//
pTrans = actrTransactionManager->startTransaction();
assert(pTrans != NULL);
acutPrintf("\n\n##### Started transaction three."
" ######\n");
moveBy.set(normal[0] * 3.0, normal[1] * 3.0,
normal[2] * 3.0);
normal = vec;
normal.normalize();
sectionPlane.set(center, normal);
pOtherHalf = NULL;

pExtrusion->getSlice(sectionPlane, Adesk::kTrue,
pOtherHalf);
assert(pOtherHalf != NULL);
mat.setCoordSystem(moveBy, x, y, z);
pOtherHalf->transformBy(mat);
addToDb(pOtherHalf, otherHalfId);
actrTransactionManager
->addNewlyCreatedDBRObject(pOtherHalf);
pOtherHalf->draw();
pExtrusion->draw();

acutPrintf("\nSliced the resulting solid into half"
" along a plane");
acutPrintf("\nperpendicular to the old one and moved"
" one piece.");

// Now, give the user the option to end all the transactions.
//
yes = Adesk::kFalse;

if (getYOrN("\nAbort transaction three? : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction"
" three. $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to the un-sliced solid.");
} else {
acutPrintf("\n\n>>>>>> Ending transaction three."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}

yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction two? : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction two."
" $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to separate extrusion and"
" cylinder.");
} else {
acutPrintf("\n\n>>>>>> Ending transaction two."
" <<<<<<\n");
actrTransactionManager->endTransaction();
}

yes = Adesk::kFalse;
if (getYOrN("\nAbort transaction one? : ",
Adesk::kFalse, yes,interrupted) == Acad::eOk
&& yes == Adesk::kTrue)
{
acutPrintf("\n\n$$$$$$ Aborting transaction one."
" $$$$$$\n");
actrTransactionManager->abortTransaction();
acutPrintf("\nBack to just the Poly.");
} else {
actrTransactionManager->endTransaction();
acutPrintf("\n\n>>>>>> Ending transaction one."
" <<<<<<\n");
}
}

static Acad::ErrorStatus
createAndPostPoly()
{
int nSides = 0;
while (nSides < 3) {
acedInitGet(INP_NNEG, "");
switch (acedGetInt("\nEnter number of sides: ",
&nSides))
{
case RTNORM:
if (nSides < 3)
acutPrintf("\nNeed at least 3 sides.");
break;
default:
return Acad::eInvalidInput;
}
}

ads_point center, startPt, normal;
if (acedGetPoint(NULL, "\nLocate center of polygon: ",
center) != RTNORM)
{
return Acad::eInvalidInput;
}

startPt[0] = center[0];
startPt[1] = center[1];
startPt[2] = center[2];

while (asPnt3d(startPt) == asPnt3d(center)) {
switch (acedGetPoint(center,
"\nLocate start point of polygon: ", startPt)) {
case RTNORM:
if (asPnt3d(center) == asPnt3d(startPt))
acutPrintf("\nPick a point different"
" from the center.");
break;
default:
return Acad::eInvalidInput;
}
}

// Set the normal to the plane of the polygon to be
// the same as the Z direction of the current UCS,
// (0, 0, 1) since we also got the center and
// start point in the current UCS. (acedGetPoint()
// returns in the current UCS.)
normal[X] = 0.0;
normal[Y] = 0.0;
normal[Z] = 1.0;
acdbUcs2Wcs(normal, normal, Adesk::kTrue);
acdbUcs2Ecs(center, center, normal, Adesk::kFalse);
acdbUcs2Ecs(startPt, startPt, normal, Adesk::kFalse);
double elev = center[2];

AcGePoint2d cen = asPnt2d(center),
start = asPnt2d(startPt);
AcGeVector3d norm = asVec3d(normal);
AsdkPoly *pPoly = new AsdkPoly;
if (pPoly==NULL)
return Acad::eOutOfMemory;
Acad::ErrorStatus es;

if ((es=pPoly->set(cen, start, nSides, norm,
"transactPoly",elev))!=Acad::eOk)
return es;

pPoly->setDatabaseDefaults(
acdbHostApplicationServices()->workingDatabase());
postToDb(pPoly);
return Acad::eOk;
}
// Extrudes the poly to a given height.
//
static Acad::ErrorStatus
extrudePoly(AsdkPoly* pPoly, double height, AcDbObjectId& savedExtrusionId)
{
Acad::ErrorStatus es = Acad::eOk;

// Explode to a set of lines.
//
AcDbVoidPtrArray lines;
pPoly->explode(lines);

// Create a region from the set of lines.
//
AcDbVoidPtrArray regions;
AcDbRegion::createFromCurves(lines, regions);
assert(regions.length() == 1);
AcDbRegion *pRegion
= AcDbRegion::cast((AcRxObject*)regions[0]);
assert(pRegion != NULL);

// Extrude the region to create a solid.
//
AcDb3dSolid *pSolid = new AcDb3dSolid;
assert(pSolid != NULL);
pSolid->extrude(pRegion, height, 0.0);

for (int i = 0; i < lines.length(); i++) {
delete (AcRxObject*)lines[i];
}

for (i = 0; i < regions.length(); i++) {
delete (AcRxObject*)regions[i];
}

// Now we have a solid. Add it to database, then
// associate the solid with a transaction. After
// this, transaction management is in charge of
// maintaining it.
//
pSolid->setPropertiesFrom(pPoly);
addToDb(pSolid, savedExtrusionId);
actrTransactionManager
->addNewlyCreatedDBRObject(pSolid);
pSolid->draw();
return Acad::eOk;
}

static Acad::ErrorStatus
getASolid(char* prompt,
AcTransaction* pTransaction,
AcDb::OpenMode mode,
AcDbObjectId checkWithThisId,
AcDb3dSolid*& pSolid)
{
AcDbObject *pObj = NULL;
AcDbObjectId objId;
ads_name ename;
ads_point pickpt;

for (;;) {
switch (acedEntSel(prompt, ename, pickpt)) {
case RTNORM:
AOK(acdbGetObjectId(objId, ename));
if (objId != checkWithThisId) {
acutPrintf("\n Select the proper"
" solid.");
continue;
}
AOK(pTransaction->getObject(pObj, objId, mode));
assert(pObj != NULL);
pSolid = AcDb3dSolid::cast(pObj);
if (pSolid == NULL) {
acutPrintf("\nNot a solid. Try again");
AOK(pObj->close());
continue;
}
break;
case RTNONE:
case RTCAN:
return Acad::eInvalidInput;
default:
continue;
}
break;
}
return Acad::eOk;
}

Quản lý giao dịch Transcation (Phần 2)

Giao dịch lồng nhau


Các giao dịch có thể lồng vào nhau, bạn có thể bắt đầu một giao dịch bên trong một giao dịch khác và kết thúc hoặc hủy bỏ giao dịch gần đây.

Trình quản lý giao dịch duy trì các giao dịch trong một khối, trong đó những giao dịch gần nhất nằm trên đỉnh của khối. Khi bắt đầu một giao dịch mới sử dụng AcTransactionManager::startTransaction(), thì giao dịch đó được thêm vào tại đỉnh của khối và con trỏ đến nó được trả về (một thể hiện của AcTransaction). Khi một ai đó gọi AcTransactionManager::endTransaction() hoặc AcTransactionManager::abortTransaction(), thì giao dịch trên cùng của khối sẽ kết thúc hoặc bị hủy bỏ. 


Khi các con trỏ đối tượng được lấy nhờ vào mã ID, chúng luôn luôn liên hệ với giao dịch gần nhất. Bạn có thể lấy được giao dịch gần đây bằng cách sử dụng AcTransactionManager::topTransaction(), sau đó là AcTransaction::getObject() hoặc AcTransactionManager::getObject() để lấy được con trỏ đến một đối tượng. Trình quản lý giao dịch tự động liên kết với con trỏ đối tượng được lấy với giao dịch gần đây. Bạn có thể dùng AcTransaction::getObject() chỉ với giao dịch gần đây nhất

Khi các giao dịch bắt đầu lồng vào nhau, con trỏ đối tượng được lấy trong giao dịch ngoài cùng cũng dùng được cho các thao tác ở giao dịch trong nhất. Nếu giao dịch gần đây bị hủy bỏ, mọi quá trình hoàn thành trên tất cả các đối tượng (kết hợp với hoặc giao dịch này, hoặc giao dịch bên ngoài) từ lúc bắt đầu của giao dịch gần đây, được hủy bỏ và đối tượng quay trở lại trạng thái ban đầu trước khi giao dịch. Con trỏ đối tượng lấy được trong giao dịch gần đây sẽ mất hiệu lực khi nó bị hủy bỏ.

Nếu giao dịch trong cùng kết thúc thành công bằng cách gọi hàm AcTransactionManager::endTransaction(), các đối tượng có con trỏ lấy được trong giao dịch này sẽ liên kết với giao dịch bên ngoài và sử dụng được cho các quá trình tiếp theo. Quá trình như thế cứ tiếp diễn cho đến khi giao dịch ngoài cùng (đầu tiên) kết thúc, cùng lúc là mọi thay đổi trên các đối tượng sẽ được thực hiện đồng thời. Nếu giao dịch ngoài cùng bị hủy bỏ, mọi thao tác trên tất cả các giao dịch bên trong và chính nó cũng bị hủy bỏ theo.

Tránh giao dịch đệ quy (Recursive Transactions)


Cơ chế quản lý giao dịch trong AutoCAD dựa trên thực tế là các giao dịch không đệ quy. Khi một giao dịch kết thúc (hoặc hủy bỏ), bạn không cần cố gắng để khởi động và kết thúc (hoặc hủy bỏ) một giao dịch khác. Nếu làm như thế, có thể dẫn đến kết quả không lường trước và thiết ổn định. 

Để tránh gây ra vấn đề này, trước khi thực hiện một giao dịch, kiểm tra xem có an toàn để thực hiện hay không. Để làm điều này, thực thi một reactor giao dịch và đặt cờ flag để quyết định khi nào AutoCAD đang ở lưng chừng một quá trình kết thúc (hoặc hủy bỏ) giao dịch. Các phần khác của mã nguồn làm việc với giao dịch nên kiểm tra flag này trước khi tiến hành một giao dịch mới.

Bởi rất nhiều quá trình trong AutoCAD sử dụng quản lý giao dịch, và các quá trình này kích hoạt rất nhiều thông báo khác nhau, cho nên việc sử dụng mã thông báo để kiểm tra xem có an toàn khi thực thiện một giao dịch hay không là thật sự quan trọng.


Biên Giao dịch


Bởi vì chính bạn (không phải hệ thống) chịu trách nhiệm bắt đầu, kết thúc hoặc hủy bỏ giao dịch, nên việc ý thức về biên giao dịch là rất quan trọng. Một biên giao dịch là khoảng thời gian giữa lúc bắt đầu và kết thúc (hoặc hủy bỏ) một giao dịch. Nếu được, bạn hãy giới hạn biên giao dịch trong khoảng nhỏ nhất có thể. Ví dụ như, nếu bạn bắt đầu một giao dịch trong một hàm, hãy thử kết thúc giao dịch trước khi trả về từ hàm đó, bởi bạn có thể không có kiến thức về giao dịch bên ngoài hàm. Bạn không cần thực hiện quy tắc này nếu muốn duy trì một vài kiểu của trình quản lý toàn cầu cho các hoạt động giao dịch, nhưng bạn vẫn cần phải chịu trách nhiệm đối với việc hủy bỏ hay kết thúc giao dịch mà bạn đã khởi động trước đó.

Nhiều ứng dụng có thể sử dụng quản lý giao dịch cho công việc của chúng, và các thao tác trên các đối tượng  sẽ được thực hiện khi kết thúc giao dịch ngoài cùng. Vì thế, hãy kéo dài biên các giao dịch của bạn tối đa có thể trong biên của lệnh AutoCAD®. Khi một lệnh kết thúc, không nên có bất cứ một giao dịch đang hoạt động. Nếu còn giao dịch hoạt động (khối giao dịch không rỗng) khi một  lệnh kết thúc, AutoCAD sẽ hủy bỏ chúng. Có một ngoại lệ, giao dịch vẫn có thể hoạt động khi kết thúc lệnh và AutoCAD trả về dòng lệnh nhắc Command. Nói chung, ý tưởng tốt hơn cả là bắt đầu một giao dịch khi một trong số các hàm của bạn được khởi động như là một phần của lệnh đã đăng ký và kết thúc giao dịch khi bạn trả về từ hàm này. Bạn có thể khái quát nó vào tất cả các lệnh trong AutoCAD bằng cách sử dụng thông báo AcEditorReactor::commandWillStart()AcEditorReactor::commandEnded(), nhưng vẫn có những lệnh nhất định không nên sử dụng trong giao dịch. Các lệnh sau đây không nên thực hiện trong giao dịch:
  • ARX
  • DXFIN
  • INSERT
  • NEW
  • OPEN
  • PURGE
  • QUIT
  • RECOVER
  • REDO
  • SAVE
  • SCRIPT
  • U
  • UNDO
  • XREF

Lấy con trỏ đến đối tượng trong một giao dịch


Cả AcTransactionManager::getObject() lẫn AcTransaction::getObject() đều có thể được dùng để lấy con trỏ đối tượng từ ID của nó. Con trỏ lấy được sẽ làm việc với giao dịch gần nhất. Việc thử lấy con trỏ sử dụng các kết quả giao dịch khác sẽ gây ra lỗi. Ngoài ra, con trỏ lấy được cũng chỉ hợp lệ cho đến khi giao dịch mà chúng làm việc, hoặc một giao dịch bên ngoài, bị hủy bỏ. Khi giao dịch ngoài cùng kết thúc, các thay đổi trên các con trỏ hợp lệ sẽ được thực hiện.

Cả hai hàm getObject() đều lấy một tham số là AcDb::OpenMode và bạn có thể lấy con trỏ đối tượng để đọc, ghi, hoặc notify (thông báo). Mọi yêu cầu đều thành công, ngoài trừ trường hợp: nếu đối tượng đang thông báo và yêu cầu lấy con trỏ là để ghi (nhằm mục đích chỉnh sửa), lỗi (eWasNotifying) sẽ trả về. Không nên chỉnh sửa một đối tượng khi nó đang ở trong trạng thái thông báo đến các đối tượng khác.

Nếu bạn sử dụng getObject() để lấy con trỏ đối tượng, bạn không bao giờ được gọi phương thức close() trên con trỏ đó. Việc gọi close() chỉ hợp lệ nếu bạn lấy con trỏ bằng phương thức acdbOpenObject() hoặc đối tượng mới được tạo ra. Để có thêm thông tin về việc sử dụng phương thức close(), xem thêm các chuyên mục nhỏ dưới đây, Đối tượng mới tạo và giao dịch và Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng.

Đối tượng mới tạo và Giao dịch


Có hai cách để làm thực hiện việc tạo mới đối tượng trong một công việc quản lý giao dịch.

Phương pháp được khuyến nghị là sử dụng close(), đóng đối tượng sau khi thêm nó vào trong Cơ sở dữ liệu hoặc khoang chứa container thích hợp và lưu lại ID được trả về. Ngay sau khi đóng đối tượng, bạn đã có thể sử dụng hàm getObject() để lấy một con trỏ mới để thao tác tiếp theo. Thậm chí nếu bạn gọi phương thức close() trên đối tượng sau khi thêm nó vào trong CSDL, việc tạo ra nó sẽ bị hoàn tác nếu giao dịch bên ngoài bị hủy bỏ. Xem thêm Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng.

Phương pháp thứ hai là thêm đối tượng mới được tạo ra trong bộ nhớ vào CSDL hoặc khoang chứa thích hợp. Sau đó đưa nó vào trong giao dịch gần nhất bằng phương thức AcTransactionManager::addNewlyCreatedDBRObject() hoặc AcTransaction::addNewlyCreatedDBRObject. Bây giờ thì nó đã có thể làm việc với giao dịch. Mọi thay đổi trên đối tượng, thậm chí là việc tạo ra nó cũng sẽ được quyết định bởi giao dịch thành công hay hủy bỏ.

Nguyên tắc thời gian hẹn trước (Commit-Time) 


Khi giao dịch ngoài cùng kết thúc, trình quản lý giao dịch sẽ gọi hàm thông báo endCalledOnOutermostTransaction() (Xem trong Reactor giao dịch) và bắt đầu quá trình thực hiện các thay đổi hẹn trước. Mỗi đối tượng được thực hiện một cách riêng lẻ, cái này sau cái khác, cho đến khi tất cả các thay đổi hẹn trước được thực hiện. Trong suốt quá trình này, bạn không nên thay đổi bất cứ đối tượng nào đang tham gia quá trình, và cũng không nên bắt đầu một giao dịch mới. Nếu không, AutoCAD sẽ hủy bỏ với thông báo lỗi eInProcessOfCommitting

Bạn có thể chỉnh sửa từng đối tượng riêng lẻ sau khi quá trình hẹn trước được thực hiện xong, nhưng không nên lưu lại ID của đối tượng bạn muốn chỉnh sửa và đợi cho đến khi nhận được thông báo transactionEnded() báo hiệu kết thúc giao dịch, rồi mới thực hiện các chỉnh sửa. 

Undo và giao dịch


Mô hình giao dịch sử dụng cơ chế undo của AutoCAD và phương thức AcDbObject::cancel() khi thực thi AcTransactionManager::abortTransaction(). Nó yêu cầu bạn không được gộp bất kì quá trình có sử dụng cơ chế undo dưới lệnh của AutoCAD trong giao dịch. Nó sẽ gây nhầm lẫn cho  AcDbTransactionManager::abortTransaction() và tạo ra kết quả không mong muốn. Ví dụ của quá trình sử dụng cơ chế undo dưới lệnh là các lệnh PEDIT và SPLINEDIT.

Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng 

 
Mô hình giao dịch tồn tại song song với cơ chế đóng và mở đối tượng như đã nói ở chương 5, Đối tượng Cơ sở dữ liệu. Tuy nhiên nếu sử dụng mô hình giao dịch, khuyến cáo bạn không nên sử dụng lẫn lộn nó với cơ chế đóng mở. Ví dụ, nếu bạn lấy con trỏ tới đối tượng sử dụng AcTransaction::getObject(), bạn không nên gọi phương thức close() trên con trỏ của đối tượng nếu không muốn gặp phải những lỗi không mong đợi làm AutoCAD "chết bất đắc kỳ tử". Tuy nhiên, bạn vẫn có thể mở và đóng các đối tượng riêng rẽ khi các giao dịch vẫn đang hoạt động. Bạn cũng có thể khởi tạo đối tượng mới, thêm chúng vào CSDL và đóng chúng lại khi giao dịch chưa kết thúc. Mục đích cơ bản của việc pha trộn là cho phép thực hiện đa tác của nhiều ứng dụng mà một số sử dụng giao dịch, một số thì không.

 Giao dịch và phát sinh đồ hoạ 



Bạn có thể dùng AcTransactionManager::queueForGraphicsFlush()AcTransactionManager::flushGraphics() để vẽ các thực thể theo yêu cầu ngay cả khi chúng liên kết với giao dịch và một số giao dịch vẫn đang hoạt động, có nghĩa là các thay đổi trên thực thể không được hẹn trước. Phương thức AcTransactionManager::queueForGraphicsFlush() sắp xếp tất cả các thực thể eligible liên kết với các giao dịch để cập nhật đồ họa và phương thức AcTransactionManager::flushGraphics() vẽ lại chúng. Bạn có thể dùng AcDbEntity::draw() để vẽ từng thực thể riêng lẻ. Điều này giúp bạn nhìn thấy thực thể trên màn hình mà không phải đợi đến lúc kết thúc quá trình giao dịch ngoài cùng, khi mà mọi thay đổi đều được vẽ lại. Sử dụng AcTransactionManager::enableGraphicsFlush() để bật hoặc tắt chức năng vẽ thực thể. Khi một lệnh kết thúc, bạn sẽ mất quyền kiểm soát đồ họa và nó sẽ tự động kích hoạt lại.

Reactor giao dịch


Trình quản lý giao dịch có một danh sách các phản ứng reactor mà trình thông báo của nó liên quan đến kiểu giao dịch. Dưới đây là các sự kiện gửi thông báo:
  • AcTransactionReactor::transactionStarted()
  • AcTransactionReactor::transactionEnded()
  • AcTransactionReactor::transactionAborted()
  • AcTransactionReactor::endCalledOnOutermostTransaction()
  • AcTransactionReactor::transactionAboutToStart()
  • AcTransactionReactor::transactionAboutToEnd()
  • AcTransactionReactor::transactionAboutToAbort()
  • AcTransactionReactor::objectIdSwapped()
3 thông báo đầu tiên được gửi đi khi một giao dịch, bao giồm cả các giao dịch lồng được khởi động, kết thúc hoặc hủy bỏ. Bạn có thể dùng những thông báo này kết hợp với AcTransactionManager::numActiveTransactions() để xác định các giao dịch có liên quan đến thông báo. Ví dụ, nếu gọi hàm AcTransactionManager::numActiveTransactions() trả về 0 trong hàm chồng AcTransactionReactor::transactionEnded() hoặc AcTransactionReactor::transactionAborted(), bạn có thể biết được giao dịch ngoài cùng đang kết thúc hay bị hủy.
Thông báo endCalledOnOutermostTransaction() báo hiệu kết thúc quá trình thay đổi hẹn trước được thực hiện của tất cả các giao dịch. Bạn có thể sử dụng để sử dụng nó để thực hiện các dọn dẹp cần thiết trước khi quá trình hẹn trước bắt đầu.

Tham số đầu tiên trong tất cả các thông báo thể hiện số lượng các giao dịch đang hoạt động cộng với những giao dịch đã kết thúc thành công. Nó không bao gồm giao dịch vừa bắt đầu hoặc đã bị hủy bỏ.

July 13, 2013

Quản lý giao dịch Transaction

Chuyên mục này miêu tả mô hình Giao dịch (transaction), được sử dụng để thao tác trên các đối tượng AcDb. Trong mô hình này, nhiều thao tác trên nhiều đối tượng khác nhau được nhóm lại cùng với nhau trong một quá trình nguyên tử (atomic) gọi là một giao dịch (transaction).

Các giao dịch có thể được lồng và có thể kết thúc hay huỷ bỏ tuỳ theo quyết định của khách hàng (client). Mô hình này có thể được sử dụng kết hợp với cơ chế mở đóng trong từng đối tượng riêng lẻ như đã nói đến trong chuyên mục Đối tượng Cơ sở dữ liệu.



  • Tổng quan về quản lý giao dịch
  • Trình quản lý giao dịch
  • Các giao dịch lồng nhau
  • Giao dịch biên ngoài
  • Lấy con trỏ đến đối tượng trong một giao dịch
  • Đối tượng mới tạo và Giao dịch
  • Nguyên tắc thời gian hẹn trước (Commit-Time)
  • Undo và Giao dịch
  • Hoà trộn giữa mô hình giao dịch với cơ chế Mở và Đóng
  • Giao dịch và phát sinh đồ hoạ
  • Reactor giao dịch
  • Ví dụ về giao dịch lồng nhau
 

Tổng quan về quản lý giao dịch

 


Mô hình giao dịch đóng gói nhiều quá trình trên nhiều đối tượng bởi một số client (máy khách) thành một quá trình nguyên tử gọi là giao dịch. Bên trong ranh giới của một giao dịch, các client có thể mang các con trỏ từ ID của các đối tượng. Các con trỏ này sẽ hợp lệ cho đến khi giao dịch kết thúc, hoặc bị huỷ bỏ bởi client. Nếu giao dịch kết thúc thành công, các quá trình trên đối tượng sẽ được thực hiện theo đúng yêu cầu. Nếu huỷ bỏ giao dịch, các thao tác trên đối tượng cũng bị huỷ bỏ theo.

Quá trình trên các đối tượng sử dụng dạng thức (paradigm) này có một số lợi thế so với cơ chế mở và đóng đơn thuần đã nói đến trong chuyên mục Đối tượng Cơ sở dữ liệu. Cơ chế mở và đóng thích hợp hơn với các quá trình đơn giản trên một đối tượng riêng lẻ hoặc một nhóm đối tượng nhỏ. Tuy nhiên, sẽ có những hạn chế nhất định trong việc mở đối tượng theo cách này. Ví dụ, nếu đối tượng được mở để đọc, bạn không thể mở nó để Ghi tại cùng thời điểm. Nếu đối tượng được mở để ghi, bạn cũng không thể mở nó để ghi một lần nữa. Danh sách các lỗi xung đột của cơ chế mở và đóng, xem bài Đối tượng Cơ sở dữ liệu. Mô hình giao dịch khoan dung (lenient) hơn, có thể lấy được con trỏ đối tượng từ mã ID cho một phương thức đặc biệt thường thành công nếu như đối tượng có liên hệ với giao dịch.

Tuỳ thuộc vào ứng dụng của bạn, có thể sẽ gặp phải những khó khăn khác khi sử dụng cơ chế đóng mở. Nếu ứng dụng mở và đóng cùng một đối tượng một số lần nhất định chỉ trong một quá trình - ví dụ trong 1 lệnh - bạn sẽ gánh chịu (incur) sự thiếu hiệu quả nghiêm trọng do sử dụng đóng mở nhiều lần. Một số lượng các quá trình thời gian tiêu dùng (time-consuming) có liên kết với quá trình đóng đối tượng. Nếu bạn mở một đối tượng để Ghi, chỉnh sửa, và đóng đối tượng lại, thì bản ghi Undo của sự thay đổi được giao phó vào quá trình undo tệp tin, đồ hoạ đối tượng được phát sinh, và các thông báo được kích hoạt. Tất cả các quá trình được thực hiện mỗi lần đối tượng được đóng lại. Nếu bạn giao dịch hoá các quá trình và sử dụng con trỏ đối tượng sử dụng giao dịch, mọi hoạt động đề cập trước đó sẽ xảy ra một lần duy nhất khi kết thúc giao dịch. Kết quả được cải thiện hiệu quả đáng kể và một quá trình undo tệp tin nhỏ hơn, bởi vì số lượng các bản ghi undo giảm xuống.

Ngoài ra, nếu bạn có một mạng lưới phức tạp nơi các đối tượng tham chiếu đến các đối tượng khác bằng mã ID, bạn mỗ lấy con trỏ tới một đối tượng trong bất kỳ module nào của chương trình mà không phải lo lắng xem đã có module hay chương trình nào khác đã mở đối tượng hay chưa. Các hoạt động này chỉ có thể sử dụng mô hình giao dịch bởi vì giao dịch nhóm các quá trình và cho phép lấy con trỏ từ mã ID vượt qua cả ranh giới của một module.

Trình quản lý giao dịch


Trình quản lý giao dịch là một đối tượng quản lý toàn cục, giống như trình soạn thảo editor,  nó có trách nhiệm duy trì các giao dịch. Nó là một đại diện của AcTransactionManager và được duy trì trong hệ thống registry. Bạn có thể lấy được nó từ hệ thống registry bằng macro actrTransactionManager, có mở rộng là:

#define actrTransactionManager  \
AcTransactionManager::cast(
acrxSysRegistry()->at(AC_TRANSACTION_MANAGER_OBJ))

Trình quản lý giao dịch cần được dụng để mở đầu, kết thúc hoặc huỷ bỏ một giao dịch. Nó có thể cung cấp thông tin như số lượng các giao dịch hiện hành tại một thời điểm bất kỳ (xem thêm chuyên mục, Giao dịch lồng nhau) và một danh sách tất cả các đối tượng có con trỏ đang được sử dụng trong mọi giao dịch. Trình quản lý giao dịch duy trì một danh sách các reactor để nhắc nhở các máy trạm về sự kiện bắt đầu, kết thúc hay huỷ bỏ một giao dịch. 

Ngoài khả năng quản lý, trình quản lý giao dịch còn có thể được dùng để lấy con trỏ từ mã ID. Khi điều đó được thực hiện, đối tượng có liên hệ với giao dịch trên đỉnh (gần nhất). Trình quản lý giao dịch còn có thể được sử dụng để sắp xếp mọi đối tượng theo thứ tự trong tất cả giao dịch cho quá trình câp nhật đồ hoạ và dồn dịch sắp xếp (flush the queue).

Đối tượng trình quản lý giao dịch được tạo ra và quản lý bởi hệ thống. Bạn không nên xoá nó.

    July 12, 2013

    Tạo mới UCS và đặt làm UCS hiện hành

    By Gopinath Taget

    Đoạn mã nguồn sau đây trình bày cách tạo một UCS đặt tên và thiết lập nó thành UCS hiện hành. Có hai hàm cần phải nhớ là:

    1. acedVports2VportTableRecords() - Hàm này cần được gọi trước khi truy cập đến bảng các viewport.
    2. acedVportTableRecords2Vports() - Hàm này cần được gọi khi kết thúc chương trình để áp dụng các thay đổi lên bảng viewport.
    Hai hàm trên rất cần thiết để AutoCAD có thể cập nhật viewport với các thiết lập hiện hành.

    // - asdkucsarx._test command (do not rename)
    static void asdkucsarx_test(void)
    {
    Acad::ErrorStatus es;
    AcDbUCSTableRecord *myUCS = new AcDbUCSTableRecord;

    //define your own ucs

    AcGePoint3d origin_point(0,0,0);
    AcGeVector3d UCSXaxis(0,1,0);
    AcGeVector3d UCSYaxis(1,0,0);

    myUCS->setOrigin(origin_point);
    myUCS->setXAxis(UCSXaxis);
    myUCS->setYAxis(UCSYaxis);

    es=myUCS->setName( _T("MyUCS"));

    if (es != Acad::eOk)
    {
    acutPrintf(_T("\nFailed to set name"));
    return;
    }

    AcDbObjectId UCSId;
    AcDbSymbolTable *pUCSTable;

    if (acdbHostApplicationServices()->workingDatabase()->
    getUCSTable(pUCSTable,AcDb::kForWrite)==Acad::eOk)
    {
    es=pUCSTable->add(UCSId,myUCS);
    es=pUCSTable->close();
    es= myUCS->close();
    }
    else
    {
    acutPrintf(_T("\nFailed to get UCS table"));
    return;
    }

    //To set the current UCS, I accessed
    // the active AcDbViewportTableRecord
    // and used setUCS to set the UCS I created as current.

    AcDbViewportTable *pVT;
    es = acedVports2VportTableRecords();
    if (es != Acad::eOk)
    {
    acutPrintf(
    _T("\nFailed to load vport info into vport table records"));
    return;
    }

    es=acdbHostApplicationServices()->
    workingDatabase()->getViewportTable(pVT,AcDb::kForRead);
    if (es != Acad::eOk)
    {
    acutPrintf(_T("\nFailed to get vport table"));
    pVT->close();
    return;
    }

    AcDbViewportTableIterator* pIter = NULL;

    es=pVT->newIterator(pIter);

    if (es != Acad::eOk)
    {
    acutPrintf(_T("\nFailed to get vport table"));
    pVT->close();
    delete pIter;
    return;
    }

    for (pIter->start();!pIter->done();pIter->step())
    {

    AcDbViewportTableRecord* pRec;
    //it should be open for write mode
    es=pIter->getRecord(pRec,AcDb::kForWrite);

    if (es != Acad::eOk)
    {
    acutPrintf(
    _T("\nFailed to get vport table record"));
    pVT->close();
    pRec->close();
    delete pIter;
    return;
    }

    TCHAR* name=NULL;
    es=pRec->getName(name);
    if (es != Acad::eOk)
    {
    acutPrintf(
    _T("\nFailed to get name from vport table"));
    pVT->close();
    pRec->close();
    delete pIter;
    return;
    }

    if (_tcsicmp(name,_T("*ACTIVE"))==0)
    {
    es=pRec->setUcs(UCSId);
    }
    es=pRec->close();
    }
    es=acedVportTableRecords2Vports(); //force update
    es=pVT->close();
    delete pIter;
    return ;
    }

    Tạo Viewports mới trong không gian PaperSpace

    By Augusto Goncalves

    Để tạo mới một viewport trong không gian PaperSpace, giống như lệnh MVIEW, thực hiện theo các bước sau:
    1. Tạo một đối tượng mới có kiểu AcDbViewport.
    2. Thiết lập tọa độ view (với các hàm setWidth(), setHeight()setCenterPoint()).
    3. Thêm viewport vào không gian paper.
    4. Lấy các thiết lập view từ người dùng (AcDbViewTableRecord).
    5. Thiết lập view có được trong viewport mới tạo ra bằng hàm acdbSetCurrentView().
    6. Kích hoạt viewport với hàm AcDbViewport::setOn().

    CHÚ Ý: Hàm AcDbViewport::setOn() chỉ làm việc nếu lệnh của bạn được đăng ký mà không có flag ACRX_CMD_TRANSPARENT. Nếu không, AcDbViewport::setOn() sẽ trả về eCommandWasInProgress và bạn không thể kích hoạt viewport cho đến khi đặt biến TILEMODE bằng 1 rồi trở về 0.  avnd you cannot activate the viewport until you set tilemode to 1 and back to 0.

    Dưới đây là mã nguồn thực hiện:

    // Only works in paperspace
    AcDbObjectId mCurViewportId = acedGetCurViewportObjectId();
    if (mCurViewportId == AcDbObjectId::kNull)
    {
    acutPrintf(_T("\nCommand only works in paperspace."));
    return;
    }

    AcDbViewport *pCurViewport;
    if (Acad::eOk != acdbOpenObject(pCurViewport,mCurViewportId,
    AcDb::kForRead))
    {
    acutPrintf(_T("\nCannot get active viewport."));
    return;
    }

    if (pCurViewport->number() != 1)
    {
    acutPrintf(_T("\nCommand only works in paperspace."));
    pCurViewport->close();
    return;
    }
    pCurViewport->close();

    // Ask for the position
    ads_point pt1,pt2;

    if (RTNORM != acedGetPoint(NULL,
    _T("\nSelect first corner: "), pt1))
    return;

    if (RTNORM != acedGetCorner(pt1,
    _T("\nSelect second corner: "), pt2))
    return;

    // Ask for the view to use
    ACHAR mViewName[133];

    if (RTNORM != acedGetString(0,
    _T("\nEnter name of view to use: "), mViewName))
    return;

    // Create new viewport
    AcDbViewport *pViewport = new AcDbViewport;

    pViewport->setWidth(fabs(pt2[X] - pt1[X]));
    pViewport->setHeight(fabs(pt2[Y] - pt1[Y]));
    pViewport->setCenterPoint(AcGePoint3d(
    pt1[X] + (pt2[X] - pt1[X]) / 2,
    pt1[Y] + (pt2[Y] - pt1[Y]) / 2,
    pt1[Z]));

    // Append new viewport to paper space
    AcDbBlockTable *pTable;
    AcDbBlockTableRecord *pPsBTR;

    if (Acad::eOk != acdbHostApplicationServices()->
    workingDatabase()->getBlockTable(pTable, AcDb::kForRead))
    {
    acutPrintf(_T("\nCannot get block table."));
    delete pViewport;
    return;
    }

    if (Acad::eOk != pTable->getAt(ACDB_PAPER_SPACE, pPsBTR,
    AcDb::kForWrite))
    {
    acutPrintf(_T("\nCannot access paper space."));
    pTable->close();
    delete pViewport;
    return;
    }
    pTable->close();

    AcDbObjectId mViewPortId;
    if (Acad::eOk != pPsBTR->appendAcDbEntity(mViewPortId, pViewport))
    {
    acutPrintf(_T("\nCannot append viewport to paper space."));
    pPsBTR->close();
    delete pViewport;
    return;
    }
    pPsBTR->close();
    pViewport->setOn();

    // Set the view
    AcDbViewTable *pViewTable;
    AcDbViewTableRecord *pViewTR;

    if (Acad::eOk != acdbHostApplicationServices()->
    workingDatabase()->getViewTable(pViewTable, AcDb::kForRead))
    {
    acutPrintf(_T("\nCannot get view table."));
    pViewport->close();
    return;
    }

    if (Acad::eOk != pViewTable->getAt(mViewName,
    pViewTR, AcDb::kForRead))
    {
    acutPrintf(_T("\nCannot access view '%s'."), mViewName);
    pViewport->close();
    pViewTable->close();
    return;
    }

    pViewTable->close();

    if (acedSetCurrentView(pViewTR, pViewport)!=Acad::eOk)
    acutPrintf(_T("\nFailed to set view"));

    // Close the objects
    pViewTR->close();
    pViewport->close();

    July 5, 2013

    Lớp AcDbHatch class - ObjectARX SDK

    AcDbHatch là một thực thể đồng phẳng được tạo ra và đặt trong một mặt phẳng tùy ý trong không gian 3D. 

    Mặt phẳng HATCH có thể là đơn nhất tùy thuộc vào vector chỉ phương trong WCS (World Coordinate System) và cao độ cho biết khoảng cách từ trục WCS tới mặt phẳng HATCH. Mặt phẳng HATCH chấp nhận hệ tọa độ đối tượng của AutoCAD (OCS - object coordinate system). Gốc của nó trùng với gốc của WCS, trục X và Y được tính toán dựa trên thuật toán trục tọa độ riêng.



    Đường bao hatch thể hiện diện tích và được lấp đầy với mẫu hatch cho trước. Thể hiện bên trong của đường bao là tập hợp của các vòng loop đồng phẳng. Mỗi vòng lại được tạo bởi các đường 2D từ line, cung tròn arc, ellip và spline. Nếu đường bao chứa hơn 2 vòng loop, diện tích xác định bởi từng vòng nhỏ phải được phân tách hoàn toàn hoặc vòng này sẽ nằm trọn vẹn trong vòng khác.

    Một vòng phải là vòng đơn, khép kín và liên tục, tự giao cắt chỉ xảy ra tại điểm kết thúc endpoint. Hơn nữa, điểm bắt đầu và kết thúc phải trùng nhau. Khi xác định một đường bao hatch, ứng dụng phải đảm bảo rằng các vòng và đường bao đã được xác định và có cấu trúc. Nếu đường bao chứa nhiều hơn hai vòng, chúng phải được tổ chức trong một cấu trúc lồng, nghĩa là vòng bên ngoài sẽ được xây dựng trước, sau đó là tất cả các vòng trong lần lượt theo thứ tự. Nếu có nhiều hơn một vòng ngoài, hãy lặp lại quá trình đó. AutoCAD chỉ cho phép giới hạn số đường bao hợp lệ nhằm duy trì tính hiệu quả thực thi của API

    Thể hiện nội hàm của đường bao hatch là GELIB 2D geometry, bao gồm AcGeLineSeg2d, AcGeCircArc2d, AcGeEllipArc2dAcGeNurbCurve2d. Nếu đường bao chứa một polyline, một phương thức đặc biệt sẽ được tiến hành để xây dựng các vòng loop

    Chú ý: AutoCAD luôn giả thiết trục X cho một đường bao AcGeCircArc2d là vector 2D AcGeVector2d of (1.0, 0.0).

    Hatch liên kết (associative) cho phép ứng dụng tạo ra các thực thể hatch có thể liên kết với đường bao của các thực thể AutoCAD trong CSDL như LINE, ARC, CIRCLE, CIRCLE, ELLIPSE, SPLINE, POLYLINE, TEXT, MTEXT, ATTRIBUTE DEFINITION, ATTRIBUTE, SHAPE, SOLID, TRACE, TOLERANCE, REGION, VIEWPORT, 3D FACE, BLOCK INSERT, XREF, LWPOLYLINE, RASTER v.v.  Khi chỉnh sửa các thực thể hình học nguồn, hatch sẽ thay đổi một cách tự động.

    Khi sử dụng thực thể riêng (custom entity), bạn phải định nghĩa một phương thức explode để hatch có thể làm việc. Phương thức này cần phải bẻ gãy thực thể của bạn  thành các thực thể giản đơn. Nếu không xây dựng từ các thực thể nguyên bản, phương thức của bạn nên trả về giá trị eExplodeAgain. Nó khiến cho AcDbHatch tự gọi phương thức explode trên thực thể bạn trả về, cho đến khi mọi thực thể được bẻ gãy thành các thực thể nguyên bản (native).

    Khi định nghĩa một đường bao hatch sử dụng các đối tượng CSDL, ứng dụng phải chắc chắn rằng các đối tượng được chọn có đường bao hợp lệ và  đồng phẳng với mặt phẳng hatch. Đối tượng được chọn còn phải cấu thành được các vòng loop. Bạn cũng cần xác định flag liên kết (associativity flag) trước khi thiết lập đường bao. Phương thức insertLoopAt()appendLoop() sẽ xuất ra các yếu tố hình học từ đối tượng CSDL và bảo quản mã ID của đối tượng đó với cấu trúc vòng cho hatch liên kết.

    Nếu đường bao chứa nhiều hơn 2 vòng loop cho một hatch đặc, diện tích  xác định bởi từng vòng loop phải được chia cắt hoặc cái này sẽ nằm trọn vẹn trong cái khác. Ngoài ra, mỗi vòng cũng phải là đơn giản, đóng và liên tục, nghĩa là các giao cắt của chính nó chỉ xảy ra tại các điểm endpoint. Nếu đường bao hatch không thỏa mãn yêu cầu này, kết quả trả về có thể không lường trước và mâu thuẫn giữa phần hatch và phần không được hatch.

    Các Mline  là các thực thể phức hợp có thể tạo ra nhiều hơn một vòng loop, nghĩa là chúng sẽ bị trả về như là các đường bao. Nếu sử dụng API AcDbHatch và lựa chọn đối tượng AcDbMline, hatch sẽ không được hiển thị và phương thức appendloop() sẽ trả về eInvalidInput. Để khắc phục vấn đề này, bạn có thể tạo ra 1 vùng region bằng cách sử dụng đường biên để xây dựng một đối tượng AcDbRegion. Bạn có thể phá region để lấy vòng loop giản đơn, và đưa thành loop của đối tượng AcDbHatch. Ứng dụng của bạn cần phải kiểm tra trạng thái trả về của mỗi hàm API và xóa thực thể hatch nếu trạng thái là eInvalidInput.

    Hiện tại, AutoCAD hỗ trợ 3 kiểu mẫu hatch là User-defined,  Predefined, và Custom. Xem enum HatchPatternType để biết thêm thông tin.

    Phương thức sau đây kích hoạt ứng dụng để thiết lập và lấy dữ liệu mẫu hatch. Nếu gọi AcDbHatch::setPatternScale() (hoặc bất cứ phương thức nào khác), giá trị scale thay đổi, tuy nhiên mẫu vẫn không thay đổi trên màn hình. Đây là do thiết kế. Bạn cần phải gọi AcDbHatch::setPattern() sau khi thay đổi tỉ lệ scale patterne, góc quay, tên mẫu, ... Chỉ cần gọi một lần là đủ cho tất cả các kết hợp của mẫu thay đổi. 

    AutoCAD đang hỗ trợ 3 kiểu hatch, là Normal, Outer, và Ignore. Xem thê enum HatchStyle.

    Phương thức sau đâu cho phép ứng dụng thiết lập và lấy về kiểu hatch. Các phương thức này được cung cấp nhằm mục đích tương thích , tốt nhất là luôn sử dụng kiểu Normal.

    AcDbHatch::HatchStyle style() const;
    Acad::ErrorStatus setStyle(AcDbHatch::HatchStyle hstyle);
     
    Sau khi định nghĩa đường bao và xác định mẫu, kiểu hatch, ứng dụng phải dựng lên các đoạn thẳng hatch hoặc diện tích hatch để hiển thị. Lớp AcDbHatch duy trì việc tính toán các đoạn thẳng hatch và diện tích hatch để hỗ trợ phương thức worldDraw()viewportDraw() trong việc thể hiện thực thể. Tuy nhiên, việc tính toán này không được ghi lại trong bản vẽ hoặc tệp tin DXF nhằm đảm bảo dung lượng tệp tin. Thay vào đó, AutoCAD sẽ tính toán lại các đoạn thẳng và khối hatch khi các bản vẽ này được mở ra trong lần kế tiếp.

    Nếu đường bao hoặc các vòng loop thay đổi, ứng dụng phải xây dựng lại đoạn thẳng hatch và diện tích hatch phù hợp.

    Đường bao không hiển thị.  Đây không phải là vấn đề hiện tại bởi vì thực thể hatch luôn luôn liên kết với các đối tượng hình học trong CSDL trong mọi trường hợp. 

    Chú ý: Gọi hàm explode với một mẫu hatch solid sẽ trả về giá trị eNotApplicable.

    Ví dụ


    Chú ý: ví dụ dưới đây chưa được dịch hoặc gỡ lỗi (debugged) và có thể chứa các lỗi cú pháp. Nó chỉ trình bày thủ tục tạo ra các thực thể AcDbHatch trong môi trường API.

    Ví dụ dưới đây trình bày cách để tạo ra thực thể HATCH trên mặt phẳng WCS XY. Biên ngoài là một hình chữ nhật còn biên trong là một đường tròn. Mẫu hatch là SOLID với màu sắc mặc định của AutoCAD. HATCH tạo ra không có liên kết (associative - có thể kéo dãn theo các đường biên).

    Acad::ErrorStatus acqHatch1() 
    {
    AcDbHatch* pHatch = new AcDbHatch();

    // Set hatch plane
    //
    AcGeVector3d normal(0.0, 0.0, 1.0);
    pHatch->setNormal(normal);
    pHatch->setElevation(0.0);

    // Set non associative hatch
    //
    pHatch->setAssociative(Adesk::kFalse);

    // Set hatch pattern to SolidFill type
    //
    pHatch->setPattern(AcDbHatch::kPreDefined, "SOLID");

    // Set hatch style to kNormal
    //
    pHatch->setHatchStyle(AcDbHatch::kNormal);

    // Construct hatch external boundary
    //
    AcGePoint2dArray vertexPts;
    AcGeDoubleArray vertexBulges;
    vertexPts.setPhysicalLength(0).setLogicalLength(5);
    vertexPts[0].set(2.0, 2.0);
    vertexPts[1].set(8.0, 2.0);
    vertexPts[2].set(8.0, 8.0);
    vertexPts[3].set(2.0, 8.0);
    vertexPts[4].set(2.0, 2.0);
    vertexBulges.setPhysicalLength(0).setLogicalLength(5);
    for (int i = 0; i < 5; i++)
    vertexBulges[i] = 0.0;

    // Append an external loop (rectangle) to hatch boundary

    pHatch->appendLoop(AcDbHatch::kExternal, vertexPts, vertexBulges);

    // Construct a circle
    //
    AcGePoint2d cenPt(5.0, 5.0);
    double TWOPI = 2.0 * 3.1415926535897932;
    AcGeCircArc2d *cirArc = new AcGeCircArc2d();
    cirArc->setCenter(cenPt);
    cirArc->setRadius(1.0);
    cirArc->setAngles(0.0, TWOPI);

    // Append an internal circular loop to hatch boundary
    //
    AcGeIntArray edgeTypes;
    AcGeVoidPointerArray edgePtrs;
    edgeTypes.append(AcDbHatch::kCirArc);
    edgePtrs.append((void*)cirArc);
    pHatch->appendLoop(AcDbHatch::kDefault, edgePtrs, edgeTypes);

    // Elaborate solid fill
    //
    pHatch->evaluateHatch();

    // Post hatch entity to database
    //
    AcDbObjectId newId;
    postToModelSpace(pHatch, newId);

    return eOk;
    }

    Ví dụ dưới đây trình bày cách tạo một thực thể HATCH có liên kết (associative) trong mặt phẳng WCS XY. Đường biên ngoài là hình chữ nhật còn biên trong là một đường tròn. Mẫu HATCH là ANSI31, màu sắc mặc định. HATCH mới tạo ra có liên kết, có thể kéo dãn hoặc co lại theo các đường biên.

    Acad::ErrorStatus acqHatch2() 
    {
    AcDbHatch* pHatch = new AcDbHatch();

    // Set hatch plane
    //
    AcGeVector3d normal(0.0, 0.0, 1.0);
    pHatch->setNormal(normal);
    pHatch->setElevation(0.0);

    // Set hatch pattern to ANSI31 predefined type
    //
    pHatch->setHatchPattern(AcDbHatch::kPreDefined, "ANSI31");

    // Set Associativity
    //
    pHatch->setAssociative(Adesk::kTrue);

    // Construct database AcDbLines
    //
    AcGePoint2d vertexPts[4];
    AcDbObjectId lineId, cirId, hatchId;
    AcDbObjectIdArray dbObjIds;
    AcDbLine *line;

    vertexPts[0].set(2.0, 2.0, 0.0);
    vertexPts[1].set(8.0, 2.0, 0.0);
    vertexPts[2].set(8.0, 8.0, 0.0);
    vertexPts[3].set(2.0, 8.0, 0.0);

    for (int i = 0; i < 4; i++) {
    line = new AcDbLine();
    line->setStartPoint(vertexPts[i])
    line->setEndPoint(vertexPts[(i == 3) ? 0 : i+1])
    line->postToDb(line, lineId);
    dbObjIds.append(lineId);
    }

    // Append an external rectangular loop to hatch boundary
    //
    pHatch->appendLoop(AcDbHatch::kExternal, dbObjIds);

    // Create a AcDbCircle and post it to database
    //
    AcGePoint2d cenPt(5.0, 5.0, 0.0);
    AcGeVector3d normal(0.0, 0.0, 1.0);
    AcDbCircle *circle = new AcDbCircle();
    circle->setNormal(normal);
    circle->setCenter(cenPt);
    circle->setRadius(1.0);
    circle->postToDb(circle, cirId);
    dbObjIds.setLogicalLength(0);
    dbObjIds.append(cirId);

    // Append an internal loop (circle) to hatch boundary
    //
    pHatch->appendLoop(AcDbHatch::kDefault, dbObjIds);

    // Elaborate hatch lines
    //
    pHatch->evaluateHatch();

    // Get all associative source boundary object Ids for later use.
    //
    dbObjIds.setLogicalLength(0);
    pHatch->getAssocObjIds(dbObjIds);

    // Post hatch entity to database
    //
    pHatch->postToDb(pHatch, hatchId);

    // Attach hatchId to all source boundary objects for notification.
    //
    AcDbEntity *pEnt;
    int numObjs = dbObjIds.length();
    Acad::ErrorStatus es;
    for (i = 0; i < numObjs; i++) {
    es = acdbOpenAcDbEntity(pEnt, dbObjIds[i], AcDb::kForWrite);
    if (es == Acad::eOk) {
    pEnt->addPersistentReactor(hatchId);
    pEnt->close();
    }
    }
    return eOk;
    }

    Dẫn xuất từ

      AcRxObject
        |-AcGiDrawable
          |-AcDbObject
            |-AcDbEntity
              |-AcDbHatch

    Tính chiều dài đường cong Curve trong ObjectARX


    Dưới đây là đoạn mã dùng để tính toán chiều dài một đường cong Curve.

    Curve ở đây được hiểu theo nghĩa rộng hơn, bao gồm cả đoạn thẳng LINE, các đa tuyến POLYLINE, các cung tròn ARC, đường tròn CIRCLE.


    Acad::ErrorStatus GetLength(AcDbObjectId id, double& length)
    {
    Acad::ErrorStatus es;

    AcDbCurve* pEnt;
    es = acdbOpenObject(pEnt, id, AcDb::kForRead);
    if( es == Acad::eOk && pEnt)
    {
    double startParam, endParam, startDist, endDist;

    es = pEnt->getStartParam(startParam);
    if( es!=Acad::eOk ) { pEnt->close(); return es; }

    es = pEnt->getEndParam(endParam);
    if( es!=Acad::eOk ) { pEnt->close(); return es; }

    es = pEnt->getDistAtParam(startParam, startDist);
    if( es!=Acad::eOk ) { pEnt->close(); return es; }

    es = pEnt->getDistAtParam(endParam, endDist);
    if( es!=Acad::eOk ) { pEnt->close(); return es; }

    es = pEnt->close();
    length = endDist - startDist;
    }

    return es;
    }

    July 4, 2013

    37.6 Lưới tam giác đơn giản Regular Triangulations

    37.6   Regular Triangulations


    37.6.1   Description

    Let PW = {(pi, wi) | i = 1, , n } be a set of weighted points where each pi is a point and each wiis a scalar called the weight of point pi. Alternatively, each weighted point (pi, wi) can be regarded as a sphere (or a circle, depending on the dimensionality of pi) with center pi and radius ri=√wi. The power diagram of the set PW is a space partition in which each cell corresponds to a sphere (pi, wi) of PWand is the locus of points p whose power with respect to (pi, wi)is less than its power with respect to any other sphere in PW. In the two-dimensional space, the dual of this diagram is a triangulation whose domain covers the convex hull of the set P= { pi | i = 1, , n } of center points and whose vertices form a subset of P. Such a triangulation is called a regular triangulation. Three points pi, pj and pk of Pform a triangle in the regular triangulation of PWiff there is a point p of the plane with equal powers with respect to (pi, wi), (pj, wj)and (pk, wk) and such that this power is less than the power of pwith respect to any other sphere in PW.
    Let us defined the power product of two weighted points (pi, wi) and (pj, wj) as:
    Π(pi, wi, pj, wj) = pipj 2 - wi - wj .
    Π(pi, wi, pj, 0) is simply the power of point pjwith respect to the sphere (pi, wi), and two weighted points are said to be orthogonal if their power product is null. The power circle of three weighted points (pi, wi), (pj, wj)and (pk, wk) is defined as the unique circle (π, ω) orthogonal to (pi, wi), (pj, wj)and (pk, wk). The regular triangulation of the sets PWsatisfies the following regular property (which just reduces to the Delaunay property when all the weights are null): a triangle pipjpk is a face of the regular triangulation of PW iff the power product of any weighted point (pl, wl) of PW with the power circle of (pi, wi), (pj, wj) and (pk, wk) is positive or null. We call power test of (pi, wi), (pj, wj), (pk, wk), and (pl, wl), the predicates which amount to compute the sign of the power product of (pl, wl) with respect to the power circle of (pi, wi), (pj, wj) and (pk, wk). This predicate amounts to computing the sign of the following determinant
    |
    1 xi yi xi 2 + yi 2 - wi
    1 xj yj xj 2 + yj 2 - wj
    1 xk yk xk 2 + yk 2 - wk
    1 xl yl xl 2 + yl 2 - wl
    |
    A pair of neighboring faces pipjpkand pipjpl is said to be locally regular (with respect to the weights in PW) if the power test of (pi, wi), (pj, wj), (pk, wk), and (pl, wl) is positive. A classical result of computational geometry establishes that a triangulation of the convex hull of Psuch that any pair of neighboring faces is regular with respect to PW, is a regular triangulation of PW.
    Alternatively, the regular triangulation of the weighted points set PWcan be obtained as the projection on the two dimensional plane of the convex hull of the set of three dimensional points P'= { (pi,pi 2 - wi ) | i = 1, , n }.
    The class Regular_triangulation_2<Traits, Tds> is designed to maintain the regular triangulation of a set of 2d weighted points. It derives from the class Triangulation_2<Traits, Tds>. The functions insert and remove are overwritten to handle weighted points and maintain the regular property. The function move is not overwritten and thus does not preserve the regular property. The vertices of the regular triangulation of a set of weighted points PW correspond only to a subset of PW. Some of the input weighted points have no cell in the dual power diagrams and therefore do not correspond to a vertex of the regular triangulation. Such a point is called a hidden point. Because hidden points can reappear later on as vertices when some other point is removed, they have to be stored somewhere. The regular triangulation store those points in special vertices, called hidden vertices. A hidden point can reappear as vertex of the triangulation only when the two dimensional face that hides it is removed from the triangulation. To deal with this feature, each face of a regular triangulation stores a list of hidden vertices. The points in those vertices are reinserted in the triangulation when the face is removed.
    Regular triangulation have member functions to construct the vertices and edges of the dual power diagrams.

    The Geometric Traits

    The geometric traits of a regular triangulation must provide a weighted point type and a power test on these weighted points. The concept RegularTriangulationTraits_2, is a refinement of the concept TriangulationTraits_2. Cgal provides the class Regular_triangulation_euclidean_traits_2<Rep,Weight>which is a model for the traits concept RegularTriangulationTraits_2. The class Regular_triangulation_euclidean_traits_2<Rep,Weight>derives from the class Triangulation_euclidean_traits_2<Rep>and uses a Weighted_point type derived from the type Point_2 of Triangulation_euclidean_traits_2<Rep>. Note that, since the type Weighted_point is not defined in Cgal kernels, plugging a filtered kernel such as Exact_predicates_exact_constructions_kernel in Regular_triangulation_euclidean_traits_2<K,Weight> will in fact not provide exact predicates on weighted points.To solve this, there is also another model of the traits concept, Regular_triangulation_filtered_traits_2<FK>, which is providing filtered predicates (exact and efficient). The argument FK must be a model of the Kernel concept, and it is also restricted to be a instance of the Filtered_kernel template.

    The Vertex Type and Face Type of a Regular Triangulation

    The base vertex type of a regular triangulation includes a Boolean data member to mark the hidden state of the vertex. Therefore Cgal defines the concept RegularTriangulationVertexBase_2 which refine the concept TriangulationVertexBase_2and provides a default model for this concept.
    The base face type of a regular triangulation is required to provide a list of hidden vertices, designed to store the points hidden by the face. It has to be a model of the concept RegularTriangulationFaceBase_2. Cgal provides the templated class Regular_triangulation_face_base_2<Traits>as a default base class for faces of regular triangulations.

    37.6.2   Example: a Regular Triangulation

    The following code creates a regular triangulation of a set of weighted points and output the number of vertices and the number of hidden vertices.

    File: examples/Triangulation_2/regular.cpp

    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Regular_triangulation_euclidean_traits_2.h>
    #include <CGAL/Regular_triangulation_filtered_traits_2.h>
    #include <CGAL/Regular_triangulation_2.h>

    #include <fstream>

    typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
    typedef CGAL::Regular_triangulation_filtered_traits_2<K> Traits;
    typedef CGAL::Regular_triangulation_2<Traits> Regular_triangulation;

    int main()
    {
    std::ifstream in("data/regular.cin");

    Regular_triangulation::Weighted_point wp;
    int count = 0;
    std::vector<Regular_triangulation::Weighted_point> wpoints;
    while(in >> wp){
    count++;
    wpoints.push_back(wp);
    }
    Regular_triangulation rt(wpoints.begin(), wpoints.end());
    rt.is_valid();
    std::cout << "number of inserted points : " << count << std::endl;
    std::cout << "number of vertices : " ;
    std::cout << rt.number_of_vertices() << std::endl;
    std::cout << "number of hidden vertices : " ;
    std::cout << rt.number_of_hidden_vertices() << std::endl;
    return 0;
    }

    37.5 Lưới tam giác Delaunay


    37.5.1   Giới thiệu

    Lớp Delaunay_triangulation_2<Traits,Tds>  được thiết kế để đại diện cho lưới tam giác Delaunay tạo từ một tập hợp điểm trong mặt phẳng. Một lưới tam giác Delaunay thỏa mãn điều kiện vòng rỗng - empty circle property (còn gọi là điều kiện Delaunay property): bên trong đường tròn ngoại tiếp circumscribing của tam giác không chứa bất kỳ điểm nào của lưới của lưới tam giác đó. Với một tập hợp điểm được thiếp lập mà không có tập con của bốn điểm đồng tròn, lưới tam giác Delaunay là duy nhất, nó tương ứng với đường Vorôni về tập hợp các điểm. Lớp Delaunay_triangulation_2<Traits,Tds> dẫn xuất ra từ lớp Triangulation_2<Traits,Tds>.
    Lớp Delaunay_triangulation_2<Traits,Tds>kế thừa các kiểu được định nghĩa bởi lớp cơ bản Triangulation_2<Traits,Tds>. Kiểu bổ sung, cung cấp bởi lớp traits, được định nghĩa để thể hiện sơ đồ Voronoi.

    Lớp Delaunay_triangulation_2<Traits,Tds> ghi đè lên các hàm hành viên chịu trách nhiệm chèn, di chuyển hay loại bỏ một điểm từ lưới để duy trì điều kiện Delaunay. Nó cũng có hàm thành viên  (Vertex_handle nearest_vertex(const Point& p)) để trả lời yêu cầu tìm kiếm hàng xóm gần nhất và hàm thành viên để xây dựng các phần tử (đỉnh và cạnh) của sơ đồ Voronoi kép.

    Trait hình học - Geometric traits

    Trait hình học là một mô hình của khái niệm DelaunayTriangulationTraits_2 đã được nâng cao từ TriangulationTraits_2. Đặc biệt, khái niệm này cung cấp predicate side_of_oriented_circle cái mà cho trước 4 điểm p,q,r,s quyết định vị trí của các điểm với đường tròn đi qua 3 điểm p, q và r. Predicate side_of_oriented_circle đã xác định lưới tam giác Delaunay thật sự. Việc thay đổi predicate cho phép xây dựng các biến thể của lưới tam giácDelaunay cho các hệ đo lường khác nhau như L1 hoặc L hoặc bất kỳ metric nào được định nghĩa bởi đối tượng lồi nào. Tuy nhiên, người dùng một hệ đo khác phải cẩn thận rằng lưới tam giác được xây dựng phải là một lưới của đường bao lồi nghĩa là đường bao lòi phải là một cạnh Delaunay. Nó được cấp cho bất cứ hệ đo lồi mịn (như L2) và có thể được đảm bảo cho hệ đo khác (như L) bởi sự bổ sung đến tập hợp điểm của các điểm bao bên ngoài (sentinel point). Các lớp kernel CGAL và lớp Triangulation_euclidean_traits_2<R>là các mô hình của khái niệm DelaunayTriangulationTraits_2 cho hệ hình học Ơ-clit (euclidean). Lớp trait cho địa hình, Projection_traits_xy_3<R>,
    Projection_traits_yz_3<R>, và
    Projection_traits_xz_3<R>
    đều là kiểu của DelaunayTriangulationTraits_2ngoại trừ việc chúng không thỏa mãn cả hai điều kiện hàm và truy vấn điểm gần nhất.

    Thực thi

    Chèn một điểm mới vào trong lưới Delaunay được thực hiện bằng cách sử dụng hàm thành viên chèn điểm của lưới tam giác cơ bản và tiếp đến tiến hành một chuỗi các phép đối xứng flip để khôi phục điều kiện Delaunay. Số lượng phép đối xứng phải thực hiện là O(d) nếu đỉnh mới có góc d trong lưới Delaunay cập nhật. Với các điểm phân bố một cách ngẫu nhiên, mỗi phép chèn mất một khoảng thời gian trung bình O(1), một khi điểm đã được định vị trong lưới. Việc gọi hàm loại bỏ trong lưới và trắc lượng lại lỗ hổng tạo ra sẽ làm cho tiêu chuẩn Delaunay được thỏa mãn. Việc loại bỏ đỉnh của góc d mất O(d2). Góc d là O(1) với một một điểm ngẫu nhiên trong lưới. Khi góc của điểm nhỏ hơn hoặc bằng ( 7) một thủ tục đặc biệt được sử dụng để cho phép gia tăng thời gian loại bỏ toàn cục bởi hệ số của 2 điểm ngẫu nhiên [Dev09].

    Dịch chuyển của đỉnh v từ điểm p tới vị trị mới p',đầu tiên kiểm tra lưới được nhúng có còn đồng phẳng hay không sau khi dịch chuyển. Nếu có, việc di chuyển được tiến hành đơn giản bởi một chuỗi các phép đối xứng để xây dựng lại điều kiện Delaunay, cái mà là O(d) ở nơi d là góc đo của đỉnh sau khi dịch chuyển. Ngược lại, sự dịch chuyển được thực hiển bởi việc thêm một đỉnh tại vị trí mới và loại bỏ đỉnh cũ. Sự phức tạp là O(n) trong trường hợp xấu nhất, nhưng chỉ còn O(1 + δ√n) khi phân bố các đỉnh một cách đồng đều trong một đơn vị vuông, nơi δ là khoảng cách (Ơ-clit Euclidean) giữa vị trí mới và cũ.

    Sau khi tiến hành định vị một điểm,  hàng xóm gần nhất của điểm được tìm thấy sau khoảng thời gian O(n) trong trường hợp xấu nhất, nhưng chỉ là O(1) nếu các đỉnh phân bố ngẫu nhiên.

    37.5.2   Ví dụ: Địa hình Delaunay Terrain

    Dưới đây là đoạn mã tạo một lưới tam giác Delaunay dựa trên hệ hình học Ơ-clit cho hình chiếu của mô hình địa hình. Các điểu có cao độ, là những điểm 3D, nhưng predicate được sử dụng để xây dựng lưới Delaunay được tính toán dựa trên tọa độ x và y của các điểm mà thôi.

    Các lớp Projection_traits_xy_3<K> là một phần của Kernel hình học đồng phẳng 2D và 3D, và thay thế lớp Triangulation_euclidean_traits_xy_3<K>.

    File: examples/Triangulation_2/terrain.cpp

    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Projection_traits_xy_3.h>
    #include <CGAL/Delaunay_triangulation_2.h>

    #include <fstream>

    typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
    typedef CGAL::Projection_traits_xy_3<K> Gt;
    typedef CGAL::Delaunay_triangulation_2<Gt> Delaunay;

    typedef K::Point_3 Point;

    int main()
    {
    std::ifstream in("data/terrain.cin");
    std::istream_iterator<Point> begin(in);
    std::istream_iterator<Point> end;

    Delaunay dt(begin, end);
    std::cout << dt.number_of_vertices() << std::endl;
    return 0;
    }


    37.5.3   Ví dụ: Sơ đồ Voronoi


    Đoạn mã dưới đây có tác dụng tính toán các cạnh của sơ đồ Voronoi dựa trên tập điểm và in số lượng của các cạnh hữu hạn và số lượng tia của sơ đồ.
     
    File: examples/Triangulation_2/voronoi.cpp

    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Delaunay_triangulation_2.h>

    #include <fstream>

    typedef CGAL::Exact_predicates_inexact_constructions_kernel K;

    typedef CGAL::Delaunay_triangulation_2<K> Triangulation;
    typedef Triangulation::Edge_iterator Edge_iterator;
    typedef Triangulation::Point Point;

    int main( )
    {
    std::ifstream in("data/voronoi.cin");
    std::istream_iterator<Point> begin(in);
    std::istream_iterator<Point> end;
    Triangulation T;
    T.insert(begin, end);

    int ns = 0;
    int nr = 0;
    Edge_iterator eit =T.edges_begin();
    for ( ; eit !=T.edges_end(); ++eit) {
    CGAL::Object o = T.dual(eit);
    if (CGAL::object_cast<K::Segment_2>(&o)) {++ns;}
    else if (CGAL::object_cast<K::Ray_2>(&o)) {++nr;}
    }
    std::cout << "The Voronoi diagram has " << ns << " finite edges "
    << " and " << nr << " rays" << std::endl;
    return 0;
    }

    37.5.4   Ví dụ: In đường Voronoi giới hạn bởi hình chữ nhật

    Đoạn mã dưới đây có tác dụng tính toán lưới tam giác Delaunay dựa trên tập hợp điểm và in đường Voronoi được giới hạn bởi hình chữ nhật cho trước.


    Figure 37.6:  Sơ đồ Voronoi (màu đỏ) của các điểm màu đen được giới hạn bởi hình chữ nhật xanh.
     
    File: examples/Triangulation_2/print_cropped_voronoi.cpp
     
    #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
    #include <CGAL/Delaunay_triangulation_2.h>
    #include <iterator>

    typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
    typedef K::Point_2 Point_2;
    typedef K::Iso_rectangle_2 Iso_rectangle_2;
    typedef K::Segment_2 Segment_2;
    typedef K::Ray_2 Ray_2;
    typedef K::Line_2 Line_2;
    typedef CGAL::Delaunay_triangulation_2<K> Delaunay_triangulation_2;

    //A class to recover Voronoi diagram from stream.
    //Rays, lines and segments are cropped to a rectangle
    //so that only segments are stored
    struct Cropped_voronoi_from_delaunay{
    std::list<Segment_2> m_cropped_vd;
    Iso_rectangle_2 m_bbox;

    Cropped_voronoi_from_delaunay(const Iso_rectangle_2& bbox):m_bbox(bbox){}

    template <class RSL>
    void crop_and_extract_segment(const RSL& rsl){
    CGAL::Object obj = CGAL::intersection(rsl,m_bbox);
    const Segment_2* s=CGAL::object_cast<Segment_2>(&obj);
    if (s) m_cropped_vd.push_back(*s);
    }

    void operator<<(const Ray_2& ray) { crop_and_extract_segment(ray); }
    void operator<<(const Line_2& line) { crop_and_extract_segment(line); }
    void operator<<(const Segment_2& seg){ crop_and_extract_segment(seg); }
    };

    int main(){
    //consider some points
    std::vector<Point_2> points;
    points.push_back(Point_2(0,0));
    points.push_back(Point_2(1,1));
    points.push_back(Point_2(0,1));

    Delaunay_triangulation_2 dt2;
    //insert points into the triangulation
    dt2.insert(points.begin(),points.end());
    //construct a rectangle
    Iso_rectangle_2 bbox(-1,-1,2,2);
    Cropped_voronoi_from_delaunay vor(bbox);
    //extract the cropped Voronoi diagram
    dt2.draw_dual(vor);
    //print the cropped Voronoi diagram as segments
    std::copy(vor.m_cropped_vd.begin(),vor.m_cropped_vd.end(),
    std::ostream_iterator<Segment_2>(std::cout,"\n"));
    }

    Featured Post

    Excel data link - Copy và liên kết dữ liệu từ Excel vào AutoCAD | Copy and link from Excel to AutoCAD

    Ứng dụng được phát triển bởi đội ngũ AutoLISP Thật là đơn giản       Thông tin thêm: 👉👉👉

    Popular Posts