Trang

Hiển thị các bài đăng có nhãn Thu vien ObjectARX. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Thu vien ObjectARX. Hiển thị tất cả bài đăng

Thứ Bảy, 13 tháng 7, 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ỏ.

Thứ Năm, 4 tháng 7, 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

Thứ Tư, 3 tháng 7, 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"));
}

Thứ Tư, 26 tháng 6, 2013

37.4 Lưới tam giác cơ bản Basic Triangulations

37.4   L­ưới tam giác cơ bản


37.4.1   Giới thiệu

Flip
Figure 37.5:  Flip.


Lớp Triangulation_2<Traits,Tds>giống như một lớp cơ sở phục vụ cho các lớp lưới tam giác 2D và thực thi giao diện người dùng tới một lưới tam giác.
Các đỉnh và mặt của lưới tam giác được truy cập thông qua điều khiển (handles), biến lặp (iterators) bộ truyền (circulators). Handle là một kiểu của khái niệm Handle đề xuất đến hai toán tử khác nhau * và ->. Bộ truyền là một kiểu dành hết cho việc duyệt qua các chỗi vòng tròn.
(A circulator is a type devoted to visit circular sequences). Điều khiển được dùng đến bất cứ khi nào phần tử được truy cập không phải là thành phần của một chuỗi. Biến lặp và bộ truyền được sử dụng để duyệt qua tất cả hoặc từng phần của lưới tam giác.
Biến lặp và bộ truyền đều có hai chiều không thay đổi. Chúng có thể được chuyển đổi thành điều khiển với cùng một kiểu giá trị, để khi gọi một hàm thành viên, bất cứ đối số kiểu điều khiển đều có thể được thay thế bởi biến lặp hay bộ truyền với cùng kiểu giá trị. Lớp lưới tam giác cho phép duyệt tới các đỉnh và các mặt bên của mặt phẳng theo cùng hoặc ngược chiều kim đồng hồ.
Có các bộ truyền để truy suất đến các cạnh, hay các mặt có liên quan đến đỉnh cho trước, hoặc đỉnh liền kề với nó. Một kiểu bộ truyền khác lại cho phép truy suất đến mọi mặt phẳng có chứa một đoạn thẳng cho trước. Bộ truyền bước qua các đối tượng vô hạn cũng giống như các đối tượng hữu hạn.
Lớp lưới tam giác đề xuất một vài biến lặp để duyệt qua tất cả các mặt, các cạnh hay đỉnh và một số biến khác để duyệt đến mặt, đường, hoặc đỉnh được chọn.
Lớp lưới tam giác cung cấp một phương thức để định vị một điểm cho trước có liên quan đến lưới tam giác. Trong trường hợp đặc biệt, phương thức đó sẽ cho biết bất kể là điểm trùng đó với một mắt lưới, nằm trên một cạnh, trong mặt phẳng hay bên ngoài đường bao. Trong trường hợp lưới suy biến, điểm cũng có thể nằm bên ngoài đường bao afin của lưới.
 Lưới tam giác còn cung cấp các phương thức định vị một điểm với một mặt phẳng hữu hạn của lưới hay với vòng tròn ngoại tiếp. Các mặt phẳng của lưới và các vòng tròn ngoại tiếp của chúng có chiều ngược chiều kim đồng hồ.

Lưới có thể được chỉnh sửa bởi vài hàm khác nhau như thêm 1 điểm, loại bỏ 1 đỉnh, chuyển đổi một đỉnh, đối xứng một cạnh... Việc đối xứng một cạnh hoàn toàn có thể thực hiện khi hợp của hai mặp phẳng liên quan hình thành một tứ giác lồi (xem hình 37.5).

Flip
Hình 37.5:  Đối xứng.

Thực thi

Công việc định vị được thực hiện bởi một đoạn thẳng di động. Nó bắt đầu từ một đỉnh của mặt phẳng cho trước như là một tham số tuỳ chọn hoặc tại một điểm tự chọn của lưới nếu không được cho trước. Mất một khoảng thời gian O(n) trong trường hợp xấu nhất, còn trung bình là O(√n) nếu các đỉnh phân bố một cách ngẫu nhiên. Lớp Triangulation_hierarchy_2<Traits,Tds>, được nói đến trong mục 37.10, thực thie một cấu trúc dữ liệu được thiết kế để đưa ra một thuật toán định vị điểm hiệu quả hơn.

Chèn một điểm được thực hiện bằng cách định vị một mặt phẳng chứa điểm đó, sau đó chia mặt phẳng thành 3 mặt phẳng mới. Nếu điểm rơi bên ngoài đường bao, lưới tam giác sẽ được khôi phục lại bởi phép đối xứng. Nếu nằm ngoài vị, việc chèn điểm sẽ mất một khoảng thời gian O(1).Đây chỉ là đường biên amortized cho các điểm nằm bên ngoài đường bao lồi.


Việc loại một đỉnh được thực hiện bằng cách loại bỏ tất cả các tam giác kề bên, và xây dựng lại lưới tam giác từ lỗ thủng (hole). Công việc này mất một khoảng thời gian tỉ lệ đến d2, trong đó d là số đo độ của đỉnh loại bỏ, là O(1) cho một đỉnh ngẫu nhiên.

Di chuyển một đỉnh được thực hiện như sau: Đầu tiên, kiểm tra xem lưới tam giác mới có còn đồng phẳng hay không, nếu còn, đỉnh đã được đặt chính xách tại một vị trí mới; ngược lại, điểm được chèn tại một vị trí mới và đỉnh tại vị trí cũ sẽ được loại bỏ.
Displacement of a vertex is done by: first, verifying if the triangulation embedding remains planar after the displacement; if yes the vertex is directly placed at the new location; otherwise, a point is inserted at the new location and the vertex at the obsolete location is removed.

Mặt, cạnh và đỉnh lặp trên các đặc tính hữu hận được dẫn xuất từ bản sao của chúng kiểm tra tất cả tính năng (cả hữu hạn và vô hạn) được tự dẫn xuất từ biến lặp tương ứng của cấu trúc dữ liệu lưới tam giác.
The face, edge, and vertex iterators on finite features are derived from their counterparts visiting all (finite and infinite) features which are themselves derived from the corresponding iterators of the triangulation data structure.

Traits hình học - Geometric Traits



Traits hình học là một lưới tam giác được đòi hỏi để cung cấp đối tượng hình học (điểm, đoạn thẳng và tam giác) cùng xây dựng lên lưới tam giác với predicate hình học trên các đối tượng đó. Các predicate là:
The geometric traits of a triangulation is required to provide the geometric objects (points, segments and triangles) building up the triangulation together with the geometric predicates on those objects. The required predicates are:
- So sánh của tọa độ x hoặc y của hai điểm.
- Sự kiểm tra hướng, cái mà tính toán kiểu thứ tự của 3 điểm cho trước.

Khái niệm TriangulationTraits_2 miêu tả yêu cầu cho lớp traits hình học của lưới tam giác. Lớp kernel CGAL là các mô hình của thuật ngữ này. Thư viện CGAL còn cung cấp các mô hình chuyên dụng của TriangulationTraits_2 sử dụng đối tượng kernel hình học và predicate. Các lớp này tự templated với kernel CGAL và trích xuất các kiểu và predicate yêu cầu từ kernel. Lớp traits Triangulation_euclidean_traits_2<R> được thiết kế để làm việc với các điểm hai chiều thông thường. LớpProjection_traits_xy_3<R> là một lớp traits hình học để xây dựng lưới tam giác của một địa hình. Các điểm dữ liệu đều có 3 tọa độ x, y, z. Lưới tam giác được xây dựng phù hợp với hình chiếu của các điểm trên mặt phẳng xy và được kéo lên điểm dữ liệu 3 chiều gốc. Việc này vô cùng hữu ích khi làm việc với địa hình GIS. Thay vì chiếu các điểm 3 chiều và duy trì một ánh xạ giữa các điểm với hình chiếu của nó (mất rất nhiều không gian và dễ bị lỗi), lớp traits cung cấp predicate hình học được mà bỏ đi tọa độ z của điểm. Xem mục  37.5 làm ví dụ. CGAL còn cung cấp lớp traits hình học Projection_traits_yz_3<R>Projection_traits_xz_3<R> để làm việc với hình chiếu trên mặt phẳng yz và xz tương ứng.

37.4.2 Ví dụ về một lưới tam giác cơ bản

Chương trình sau đây tạo ra một lưới tam giác của các điểm 2D, sử dụng kernel mặc định Exact_predicate_inexact_constructions_kernel như là một traits hình học và cấu trúc lưới tam giác mặc định. Dữ liệu tọa độ điểm được đọc từ tệp tin và chèn vào lưới tam giác. Cuối cùng, điểm trên đường bao được viết ra bằng lệnh cout.
File: examples/Triangulation_2/triangulation_prog1.cpp
#include <fstream>

#include <cgal xact_predicates_inexact_constructions_kernel.h="">
#include <cgal riangulation_2.h="">

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;

typedef CGAL::Triangulation_2<k> Triangulation;
typedef Triangulation::Vertex_circulator Vertex_circulator;
typedef Triangulation::Point Point;

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

Triangulation t;
t.insert(begin, end);

Vertex_circulator vc = t.incident_vertices(t.infinite_vertex()),
done(vc);
if (vc != 0) {
do { std::cout << vc->point() << std::endl;
}while(++vc != done);
}
return 0;
}
Link nguồn: http://www.cgal.org/

37.3 Thiết kế phần mềm dựa trên lưới tam giác CGAL

37.3   Thiết kế phần mềm

The triangulations classes of Cgal provide high-level geometric functionalities such as location of a point in the triangulation, insertion, removal, or displacement of a point. They are build as a layer on top of a data structure called the triangulation data structure. The triangulation data structure can be thought of as a container for the faces and vertices of the triangulation. This data structure also takes care of all the combinatorial aspects of the triangulation.
This separation between the geometric aspect and the combinatorial part is reflected in the software design by the fact that the triangulation classes have two template parameters:

  • the first parameter stands for a geometric traits class providing the geometric primitives (points, segments and triangles) of the triangulation and the elementary operations (predicate or constructions) on those objects.
  • the second parameter stands for a triangulation data structure class. The concept of triangulation data structure is described in Section 38.2 of Chapter 38. The triangulation data structure defines the types used to represent the faces and vertices of the triangulation, as well as additional types (handles, iterators and circulators) to access and visit the faces and vertices. Cgal provides the class Triangulation_data_structure_2<Vb,Fb>as a default model of triangulation data structure. The class Triangulation_data_structure_2<Vb,Fb>has two template parameters standing for a vertex class and a face class. Cgal defines concepts for these template parameters and provide default models for these concepts. The vertex and base classes are templated by the geometric traits which allows them to have some knowledge of the geometric primitives of the triangulation. Those default vertex and face base classes can be replaced by user customized base classes in order, for example, to deal with additional properties attached to the vertices or faces of a triangulation. See section 37.11for more details on the way to make use of this flexibility.
The Figure 37.3 summarizes the design of the triangulation package, showing the three layers (base classes, triangulation data structure and triangulation) forming this design.


Three_levels
Figure 37.3:  The triangulations software design.
The top triangulation level, responsible for the geometric embedding of the triangulation comes in different flavors according to the different kind of triangulations: basic, Delaunay, regular, constrained or constrained Delaunay. Each kind of triangulations correspond to a different class. Figure 37.4 summarizes the derivation dependencies of Cgal 2D triangulations classes. Any 2D triangulation class is parametrized by a geometric traits and a triangulation data structure. While a unique concept TriangulationDataStructure_2describes the triangulation data structure requirements for any triangulation class, the concept of geometric traits actually depends on the triangulation class. In general, the requirements for the vertex and face base classes are described by the basic concepts TriangulationVertexBase_2and TriangulationFaceBase_2. However, some triangulation classes requires base classes implementing refinements of the basic concepts.


Figure 37.4:  The derivation tree of 2D triangulations.

Link nguồn: http://www.cgal.org/ 

Thư viện AutoLISP mở rộng Doslib (Phần 2)

Các hàm thiết lập -------------------------------------------------------------------------------- dos_getini Trả về giá trị từ tệp tin INI ...