|
[code]void cmdMinSurf()
{
ads_name ename;
ads_point pickpoint;
int stat;
LPCWSTR name[2][2] = { { L"left", L"right"}, { L"front", L"back"} };
TCHAR msg[256];
AcDbObjectId idCurve[2][2];
Acad::ErrorStatus es;
int i, j, count;
static int N = 40, M = 50; // Grid size. Made static for quick experiments.
// I'm using AcGeVector3d instead of AcGePoint3d because so I can simply add and multiply.
// p[0][M-1] - p[1][M-1] - ... p[N-2][M-1] - p[N-1][M-1]
// +-----------------------------+
// p[0][M-2] | p[1][M-2] - ... p[N-2][M-2] | p[N-1][M-2]
// ... . ... ... .
// ... . ... ... .
// p[0][1] | p[1][1] - ... p[N-2][1] | p[N-1][1]
// +-----------------------------+
// p[0][0] - p[1][0] - ... p[N-2][0] - p[N-1][0]
//
// p[0][j] and p[N-1][j] are the fixed points on the given curves in X-directions
// p[i][0] and p[i][M-1] are the fixed points on the given curves in Y-directions
// p[1..N-2][1..M-2] are the points inside of the grid
std::vector< std::vector<AcGeVector3d > > p; // works like AcGeVector3d p[N][M] - but dynamic size
p.resize(N);
for (auto& arr : p)
arr.resize(M);
AcGePoint3d pt;
AcGePoint3d startpoints[2][2], endpoints[2][2];
for (i=0; i<2; ++i) // 0: left/right 1:front/back
{
for (j = 0; j < 2; ++j) // 0: left/front 1:right/back
{
swprintf_s(msg, L"\nSelect %s border curve: ", name[i][j]);
stat = acedEntSel(msg, ename, pickpoint);
if (stat != RTNORM)
return;
es = acdbGetObjectId(idCurve[i][j], ename);
if (!es)
{
AcDbCurve *curve;
if ((es = acdbOpenObject(curve, idCurve[i][j], AcDb::kForRead)) == Acad::eOk)
{
curve->getStartPoint(startpoints[i][j]);
curve->getEndPoint(endpoints[i][j]);
curve->close();
}
}
}
}
std::vector<double> distances;
distances.resize(4);
bool reverse[2][2];
//startpoints[2][2], endpoints[2][2];
// The "normal" case is: curves run from left to right and from front to back
// we must make sure that the l/r curves and the f/b curves don't run in opposite directions
distances[0] = startpoints[0][0].distanceTo(startpoints[1][0]); // should be 0 (or minimal)
distances[1] = startpoints[0][0].distanceTo(endpoints[1][0]);
distances[2] = endpoints[0][0].distanceTo(startpoints[1][0]);
distances[3] = endpoints[0][0].distanceTo(endpoints[1][0]);
auto itMin = std::min_element(distances.begin(), distances.end());
size_t ixLF = itMin - distances.begin();
reverse[1][0] = !!(ixLF & 1);
reverse[0][0] = !!(ixLF & 2);
distances[0] = endpoints[1][1].distanceTo(endpoints[0][1]);
distances[1] = endpoints[1][1].distanceTo(startpoints[0][1]);
distances[2] = startpoints[1][1].distanceTo(endpoints[0][1]);
distances[3] = startpoints[1][1].distanceTo(startpoints[0][1]); // should be 0 (or minimal)
itMin = std::min_element(distances.begin(), distances.end());
size_t ixRB = itMin - distances.begin();
reverse[0][1] = !!(ixRB & 1);
reverse[1][1] = !!(ixRB & 2);
for (i = 0; i < 2; ++i) // 0: left/right 1:front/back
{
for (j = 0; j < 2; ++j) // 0: left/front 1:right/back
{
AcDbCurve* curve;
if ((es = acdbOpenObject(curve, idCurve[i][j], AcDb::kForRead)) == Acad::eOk)
{
bool r = reverse[i][j];
double par, parStart, parEnd, dPar;
es = curve->getStartParam(parStart);
es = curve->getEndParam(parEnd);
count = i ? M : N;
dPar = (parEnd - parStart) / (count - 1);
for (int k = 0; k < count; ++k)
{
par = parStart + k * dPar;
es = curve->getPointAtParam(par, pt);
int kr = r ? count-k-1 : k; // reverse or not?
if (i)
p[j ? N - 1 : 0][kr] = pt.asVector();
else
p[kr][j ? M - 1 : 0] = pt.asVector();
}
curve->close();
}
}
}
// Roughly initialize grid
double _N = 1.0 / N, _M = 1.0 / M;
for (i = 1; i < N - 1; ++i)
{
for (j = 1; j < M - 1; ++j)
{
double fn = i * _N;
double fm = j * _M;
p[i][j] = (
(fm * p[i][0] + (1.0 - fm) * p[i][M-1])
+ (fn * p[0][j] + (1.0 - fn) * p[N-1][j])
) / 2.0;
}
}
// Vary grid points until there is no more relevant change
double deltaMax = 0.1;
double delta;
int innerGridCount = (N - 2) * (M - 2);
AcGeVector3d pNew;
do {
delta = 0.0;
for (i = 1; i < N - 1; ++i)
for (j = 1; j < M - 1; ++j)
{
// Calculate a new point p[i][j] by the "average" from p[i-1][j], p[i+1][j], p[i][j-1] and p[i][j+1].
// This simulates p[i][j] being "dragged" to the point with the smallest distance to these neighbour points.
pNew = (p[i - 1][j] + p[i + 1][j] + p[i][j - 1] + p[i][j + 1]) / 4;
delta += (p[i][j] - pNew).lengthSqrd();
p[i][j] = pNew;
}
delta = delta / innerGridCount;
} while (delta > deltaMax);
//AcDbPolyFaceMesh, AcDbSubDMesh
AcGePoint3dArray totalArray(N*M);
for (i = 0; i < N; ++i)
for (j = 0; j < M; ++j)
totalArray.append(asPnt3d(asDblArray(p[i][j])));
AcDbDatabase* pDB = acdbHostApplicationServices()->workingDatabase();
AcDbPolygonMesh* mesh = new AcDbPolygonMesh(AcDb::kSimpleMesh, N, M, totalArray, false, false);
mesh->setDatabaseDefaults(pDB);
AcDbObjectId meshId;
postToDb(pDB, mesh, meshId);
}
Acad::ErrorStatus postToDb(AcDbDatabase *db, AcDbEntity* ent, AcDbObjectId& objId)
{
Acad::ErrorStatus es;
AcDbBlockTable* pBlockTable;
AcDbBlockTableRecord* pSpaceRecord;
if ((es = db->getSymbolTable(pBlockTable, AcDb::kForRead)) != Acad::eOk)
return es;
if ((es = pBlockTable->getAt(ACDB_MODEL_SPACE, pSpaceRecord, AcDb::kForWrite)) != Acad::eOk)
return es;
pBlockTable->close();
es = pSpaceRecord->appendAcDbEntity(objId, ent);
pSpaceRecord->close();
ent->close();
return es;
}[/code] |
|