Using baricenters... lotsa changes.

This commit is contained in:
Federico Ponchio 2004-11-18 18:30:15 +00:00
parent b23413ecb2
commit 75c9f2e194
24 changed files with 1015 additions and 134 deletions

107
apps/nexus/exploder.cpp Normal file
View File

@ -0,0 +1,107 @@
#include <stdlib.h>
#include <iostream>
#include "nexus.h"
using namespace nxs;
using namespace vcg;
using namespace std;
Point3f explode(Point3f &v, Point3f &center, Point3f &dir, float mag) {
v = ((v - center) * mag + center) + dir;
return v;
}
int main(int argc, char *argv[]) {
if(argc < 3) {
cerr << "Usage: " << argv[0] << " <input> <output> [factor]\n";
return -1;
}
string input = argv[1];
string output = argv[2];
float factor = 2;
if(argc == 4) {
factor = atoi(argv[3]);
if(factor == 0) {
cerr << "Invalid factor: " << argv[3] << endl;
return -1;
}
}
Nexus in;
if(!in.Load(input, true)) {
cerr << "Could not load nexus: " << input << endl;
return -1;
}
Nexus out;
if(!out.Create(output, in.signature, in.chunk_size)) {
cerr << "Could not create nexus: " << output << endl;
return -1;
}
out.sphere = in.sphere;
out.history = in.history;
for(unsigned int i = 0; i < in.index.size(); i++) {
unsigned int patch = i;
Nexus::PatchInfo &src_entry = in.index[patch];
Patch &src_patch = in.GetPatch(patch);
Border src_border = in.GetBorder(patch);
out.AddPatch(src_entry.nvert, src_entry.nface, src_border.Available());
Nexus::PatchInfo &dst_entry = out.index[patch];
Patch dst_patch = out.GetPatch(patch);
Point3f dir = src_entry.sphere.Center() - in.sphere.Center();
dir.Normalize();
dir *= 10 * src_entry.error;
memcpy(dst_patch.VertBegin(), src_patch.VertBegin(),
src_patch.nv * sizeof(Point3f));
Point3f *ptr = dst_patch.VertBegin();
for(int i = 0; i < dst_patch.nv; i++) {
ptr[i] = explode(ptr[i], src_entry.sphere.Center(), dir,
0.5 *src_entry.error);
}
if(in.signature & NXS_STRIP) {
memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(),
src_patch.nf * sizeof(unsigned short));
} else {
memcpy(dst_patch.FaceBegin(), src_patch.FaceBegin(),
src_patch.nf * sizeof(unsigned short) * 3);
}
if((in.signature & NXS_COLORS) && (out.signature & NXS_COLORS))
memcpy(dst_patch.ColorBegin(), src_patch.ColorBegin(),
src_patch.nv * sizeof(unsigned int));
if((in.signature & NXS_NORMALS_SHORT) &&
(out.signature & NXS_NORMALS_SHORT))
memcpy(dst_patch.Norm16Begin(), src_patch.Norm16Begin(),
src_patch.nv * sizeof(short)*4);
//reordering
//WATCH OUT BORDERS!
// Reorder(out.signature, dst_patch);
//copying entry information;
dst_entry.sphere = src_entry.sphere;
dst_entry.error = src_entry.error;
//adding borders.
for(unsigned int i = 0; i < src_border.Size(); i++) {
Link &link = src_border[i];
if(link.IsNull()) continue;
assert(link.end_patch < in.index.size());
}
Border dst_border = out.GetBorder(patch);
out.borders.ResizeBorder(patch, src_border.Size());
memcpy(dst_border.Start(), src_border.Start(),
src_border.Size() * sizeof(Link));
}
in.Close();
out.Close();
return 0;
}

View File

@ -120,3 +120,11 @@ void File::WriteBuffer(void *data, unsigned int sz) {
assert(0 && "Could not write");
#endif
}
void File::Delete(const string &filename) {
#ifdef WIN32
DeleteFile(filename.c_str());
#else
unlink(filename.c_str());
#endif
}

View File

@ -39,6 +39,7 @@ class File {
bool IsReadOnly() { return readonly; }
static void Delete(const std::string &filename);
protected:
#ifdef WIN32

View File

@ -40,41 +40,54 @@ void NxsPatch::Read(instm *in) {
in->read(&*bord.begin(), bord.size() * sizeof(Link));
}
void Fragment::Write(outstm *out) {
out->write(&id, sizeof(unsigned int));
out->write(&error, sizeof(float));
unsigned int ssize = seeds.size();
out->write(&ssize, sizeof(unsigned int));
out->write(&*seeds.begin(), ssize * sizeof(Point3f));
out->write(&*seeds_id.begin(), ssize * sizeof(unsigned int));
bool Fragment::Write(outstm *out) {
try {
out->write(&id, sizeof(unsigned int));
out->write(&error, sizeof(float));
unsigned int ssize = seeds.size();
out->write(&ssize, sizeof(unsigned int));
out->write(&*seeds.begin(), ssize * sizeof(Point3f));
out->write(&*seeds_id.begin(), ssize * sizeof(unsigned int));
unsigned int psize = pieces.size();
out->write(&psize, sizeof(unsigned int));
unsigned int psize = pieces.size();
out->write(&psize, sizeof(unsigned int));
for(unsigned int i = 0; i < pieces.size(); i++)
pieces[i].Write(out);
for(unsigned int i = 0; i < pieces.size(); i++)
pieces[i].Write(out);
return true;
} catch (estream *e) {
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
return false;
}
}
void Fragment::Read(instm *in) {
in->read(&id, sizeof(unsigned int));
in->read(&error, sizeof(float));
unsigned int ssize;
in->read(&ssize, sizeof(unsigned int));
seeds.resize(ssize);
seeds_id.resize(ssize);
in->read(&*seeds.begin(), ssize * sizeof(Point3f));
in->read(&*seeds_id.begin(), ssize * sizeof(unsigned int));
unsigned int psize;
in->read(&psize, sizeof(unsigned int));
pieces.resize(psize);
for(unsigned int i = 0; i < psize; i++)
pieces[i].Read(in);
bool Fragment::Read(instm *in) {
try {
in->read(&id, sizeof(unsigned int));
in->read(&error, sizeof(float));
//TODO move this control to all read!
unsigned int ssize;
if(sizeof(int) != in->read(&ssize, sizeof(unsigned int)))
return false;
seeds.resize(ssize);
seeds_id.resize(ssize);
in->read(&*seeds.begin(), ssize * sizeof(Point3f));
in->read(&*seeds_id.begin(), ssize * sizeof(unsigned int));
unsigned int psize;
in->read(&psize, sizeof(unsigned int));
pieces.resize(psize);
for(unsigned int i = 0; i < psize; i++) {
pieces[i].Read(in);
}
return true;
} catch (estream *e) {
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
return false;
}
}
void nxs::Join(Fragment &in,
@ -132,6 +145,54 @@ void nxs::Join(Fragment &in,
}
assert(vcount < (1<<16));
set<BigLink> newborders;
for(unsigned int i = 0; i < in.pieces.size(); i++) {
unsigned int offset = offsets[i];
vector<Link> &bord = in.pieces[i].bord;
for(unsigned int k = 0; k < bord.size(); k++) {
Link llink = bord[k];
if(llink.IsNull()) continue;
if(!patch_remap.count(llink.end_patch)) {//external
BigLink link;
link.start_vert = remap[offset + llink.start_vert];
link.end_patch = in.pieces[i].patch;
link.end_vert = llink.start_vert;
newborders.insert(link);
}
}
}
newvert.resize(vcount);
newface.resize(fcount*3);
newbord.resize(newborders.size());
fcount = 0;
for(unsigned int i = 0; i < in.pieces.size(); i++) {
unsigned int offset = offsets[i];
vector<Point3f> &vert = in.pieces[i].vert;
vector<unsigned short> &face = in.pieces[i].face;
vector<Link> &bord = in.pieces[i].bord;
for(unsigned int i = 0; i < vert.size(); i++) {
assert(offset + i < remap.size());
assert(remap[offset + i] < vcount);
newvert[remap[offset + i]] = vert[i];
}
for(unsigned int i = 0; i < face.size(); i++) {
assert(offset + face[i] < remap.size());
assert(remap[offset + face[i]] < newvert.size());
assert(fcount < newface.size());
newface[fcount++] = remap[offset + face[i]];
}
}
set<BigLink>::iterator b;
for(b = newborders.begin(); b != newborders.end(); b++)
newbord.push_back(*b);
/* old code (more general.. but not parallelizable)
//L(a, b): Exist link between a, b
//An external link L(e, v) where v belongs to the patches (and e not)
//is valid only if: for every x in patches L(v, x) => L(e, x)
@ -148,8 +209,10 @@ void nxs::Join(Fragment &in,
for(unsigned int k = 0; k < bord.size(); k++) {
Link llink = bord[k];
if(llink.IsNull()) continue;
if(!patch_remap.count(llink.end_patch)) {//external...may be erased though
if(!patch_remap.count(llink.end_patch)) {//external...may be erased
BigLink link;
link.orig_vert = llink.start_vert;
link.orig_patch = in.pieces[i].patch;
link.start_vert = remap[offset + llink.start_vert];
link.end_patch = llink.end_patch;
link.end_vert = llink.end_vert;
@ -196,14 +259,13 @@ void nxs::Join(Fragment &in,
unsigned int n = (*b).second;
if(n * (n-1) == internal_links[link.start_vert])
newbord.push_back(link);
}
}*/
}
void nxs::Split(Fragment &out,
vector<Point3f> &newvert,
vector<unsigned int> &newface,
vector<BigLink> &newbord,
VoronoiPartition &part) {
vector<BigLink> &newbord) {
unsigned int nseeds = out.seeds.size();
vector<Point3f> &seeds = out.seeds;
@ -219,12 +281,12 @@ void nxs::Split(Fragment &out,
assert(seed < nseeds);
count[seed]++;
}
//pruning small patches
float min_size = (newface.size()/3) / 20.0f;
vector<Point3f> newseeds;
vector<unsigned int> newseeds_id;
for(unsigned int seed = 0; seed < nseeds; seed++) {
if(count[seed] > min_size) {
newseeds.push_back(seeds[seed]);
@ -313,17 +375,15 @@ void nxs::Split(Fragment &out,
//for every esternal link we must update external patches!
for(unsigned int i = 0; i < newbord.size(); i++) {
BigLink link = newbord[i];
if(v_remap[link.start_vert] == -1) continue;
link.start_vert = v_remap[link.start_vert];
assert(link.start_vert < (1<<16));
if(v_remap[link.start_vert] == -1) continue;
Link llink;
llink.start_vert = link.start_vert;
llink.start_vert = v_remap[link.start_vert];
llink.end_patch = link.end_patch;
llink.end_vert = link.end_vert;
bords.push_back(llink);
}
//process internal borders;
//process internal borders;
//TODO higly inefficient!!!
for(unsigned int rseed = 0; rseed < nseeds; rseed++) {
if(seed == rseed) continue;

View File

@ -12,6 +12,8 @@ namespace nxs {
class VoronoiPartition;
struct BigLink {
// unsigned int orig_vert;
// unsigned int orig_patch;
unsigned int start_vert;
unsigned int end_patch;
unsigned int end_vert;
@ -33,8 +35,7 @@ class NxsPatch {
unsigned int patch;
std::vector<vcg::Point3f> vert;
std::vector<unsigned short> face;
//when this is an outfragment link.end_patch is (1<<31) + end_patch
//when it is an internal border!
std::vector<Link> bord;
void Write(pt::outstm *out);
@ -52,8 +53,8 @@ class Fragment {
std::vector<NxsPatch> pieces;
void Write(pt::outstm *out);
void Read(pt::instm *in);
bool Write(pt::outstm *out);
bool Read(pt::instm *in);
//returns the index of the seed
unsigned int Locate(const vcg::Point3f &p);
@ -67,8 +68,7 @@ class Fragment {
void Split(Fragment &out,
std::vector<vcg::Point3f> &newvert,
std::vector<unsigned int> &newface,
std::vector<BigLink> &newbord,
VoronoiPartition &part);
std::vector<BigLink> &newbord);
}
#endif

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.5 2004/07/05 15:49:39 ponchio
Windows (DevCpp, mingw) port.
Revision 1.4 2004/07/04 14:23:14 ponchio
*** empty log message ***
@ -73,6 +76,11 @@ bool MFHash::Load(const string &file, unsigned int used) {
return true;
}
void MFHash::Delete() {
buffer.Flush();
buffer.Delete();
}
void MFHash::Resize(unsigned int n) {
assert(buffer.Size() - space <= n);
//lets dump actual content

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.4 2004/07/02 17:40:30 ponchio
Debug.
Revision 1.3 2004/07/02 13:08:43 ponchio
*** empty log message ***
@ -62,6 +65,7 @@ class MFHash {
MFHash() {}
bool Create(const std::string &file, unsigned int reserved = 32);
bool Load(const std::string &file, unsigned int used = 0xffffffff);
void Delete();
void Resize(unsigned int n);
void Insert(unsigned int key, unsigned int value, bool rehash = true);

View File

@ -52,6 +52,11 @@ void MFile::Close() {
files.clear();
}
void MFile::Delete() {
while(files.size())
RemoveFile();
}
void MFile::Redim(int64 sz) {
assert(!readonly);
if(sz > size) {
@ -120,11 +125,12 @@ void MFile::WriteBuffer(void *data, unsigned int sz) {
void MFile::RemoveFile() {
assert(files.size());
string name = Name(files.size());
string name = Name(files.size()-1);
File &file = files.back();
unsigned int last_size = file.Length();
files.pop_back();
size -= last_size;
cerr << "Removing file: " << name << endl;
#ifdef WIN32
DeleteFile(name.c_str());
#else

View File

@ -27,6 +27,7 @@ class MFile {
unsigned int max_file_size = MFILE_MAX_SIZE);
bool Load(const std::string &filename, bool readonly = false);
void Close();
void Delete();
int64 Length() { return size; }
void Redim(int64 size);

View File

@ -147,6 +147,19 @@ Border Nexus::GetBorder(unsigned int patch, bool flush) {
return borders.GetBorder(patch);
}
void Nexus::AddBorder(unsigned int patch, Link &link) {
Border border = GetBorder(patch);
unsigned int pos = border.Size();
if(borders.ResizeBorder(patch, pos+1)) {
border = GetBorder(patch);
}
assert(border.Size() < border.Available());
assert(border.Available() > pos);
border[pos] = link;
}
unsigned int Nexus::AddPatch(unsigned int nvert, unsigned int nface,
unsigned int nbord) {

View File

@ -59,6 +59,8 @@ class Nexus {
Patch &GetPatch(unsigned int patch, bool flush = true);
Border GetBorder(unsigned int patch, bool flush = true);
void AddBorder(unsigned int patch, Link &link);
bool IsCompressed() { return (signature & NXS_COMPRESSED) != 0; }
bool HasStrips() { return (signature & NXS_STRIP) != 0; }
bool HasColors() { return (signature & NXS_COLORS) != 0; }

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.19 2004/10/30 20:17:03 ponchio
Fixed big patches problem.
Revision 1.18 2004/10/21 13:40:16 ponchio
Debugging.
@ -386,6 +389,23 @@ int main(int argc, char *argv[]) {
} else
nexus.Draw(cells);
if(show_borders) {
for(unsigned int i = 0; i < cells.size(); i++) {
Border border = nexus.GetBorder(cells[i]);
Patch &patch = nexus.GetPatch(cells[i]);
glPointSize(4);
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_POINTS);
for(int b = 0; b < border.Size(); b++) {
Link &link = border[b];
Point3f &p = patch.Vert(link.start_vert);
glVertex3f(p[0], p[1], p[2]);
}
glEnd();
glPointSize(1);
}
}
//cerr Do some reporting:
if(show_statistics) {
glMatrixMode(GL_MODELVIEW);

42
apps/nexus/nxsclient.cpp Normal file
View File

@ -0,0 +1,42 @@
#include <ptypes/pinet.h>
class NxsClientPool {
vector<NxsClient> clients;
void Push(NxsRequest *request);
};
class NxsClient: public pt::ipstream {
public:
queue<NxsRequest *> requests;
void Push(NxsRequest *request);
};
ipaddress addr = phostbyname("nb-ponchio.isti.cnr.it");
ipstream client;
client.set_ip(addr);
client.set_port(testport);
try
{
client.open();
pout.put("Sending a request to the server...\n");
client.write("Hello", 6);
client.flush();
// receive the response
string rsp = client.line(maxtoken);
pout.putf("Received: %s\n", pconst(rsp));
// need to close the socket explicitly to gracefully shutdown
// the peer host too. otherwise, ~ipstream() will call cancel()
// and leave the peer in a waiting state (not forever though).
client.close();
}
catch(estream* e)
{
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
}

View File

@ -0,0 +1,178 @@
#include "nxsdispatcher.h"
#include "fragment.h"
#include "decimate.h"
#include <iostream>
#include <ptypes/ptypes.h>
using namespace std;
using namespace vcg;
using namespace nxs;
using namespace pt;
void SaveFragment(Nexus &nexus, VoronoiChain &chain,
Fragment &fragin,
Fragment &fragout);
void Opener::execute() {
server->reading.lock();
server->writing.lock();
while(1) {
if(get_signaled())
return;
try {
server->open();
server->connected = true;
break;
} catch(...) {
}
sleep(4);
}
server->reading.unlock();
server->writing.unlock();
}
void FragIO::execute() {
server->writing.lock();
// cerr << "Writing frag...: " << fragin->id << "\n";
outmemory outm;
outm.open();
fragin->Write(&outm);
pt::string a = outm.get_strdata();
try {
server->write((const char *)a, length(a));
server->flush();
} catch (estream *e) {
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
message *msg = new message(MSG_FAIL, (int)fragin);
dispatcher->post(msg);
//TODO restart Server!
return;
}
server->reading.lock();
server->writing.unlock();
Fragment *out = new Fragment;
if(!out->Read(server)) {
message *msg = new message(MSG_FAIL, (int)fragin);
dispatcher->post(msg);
return;
}
server->reading.unlock();
// cerr << "Received frag: " << out->id << endl;
message *msg = new message(MSG_RECEIVE, (int)fragin);
msg->result = (int)out;
dispatcher->post(msg);
// dispatcher->ReceiveFragment(fragin, out);
}
bool Dispatcher::Init(const std::string &file) {
FILE *fp = fopen(file.c_str(), "rb");
if(!fp) return false;
char host[256];
int port;
while(fscanf(fp, "%s %d\n", host, &port) == 2) {
cerr << "Host: " << host << " port: " << port << endl;
Server *server = new Server(host, port);
server->opener.start();
servers.push_back(server);
}
fclose(fp);
if(servers.size() == 0) {
cerr << "Empty server file!\n";
return false;
}
return true;
}
Dispatcher::~Dispatcher() {
for(unsigned int i = 0; i < servers.size(); i++) {
Server *server = servers[i];
server->opener.signal();
server->close();
delete server;
}
}
void Dispatcher::SendFragment(Fragment *frag) {
//WARNING this handles no more than 1<<31 fragments!
frag->id = count++;
message *msg = new message(MSG_SEND, (int)frag);
post(msg);
}
Server *Dispatcher::BestServer() {
Server *best = NULL;
for(unsigned int i = 0; i < servers.size(); i++){
if(servers[i]->connected) {
if(!best || servers[i]->queue < best->queue) {
best = servers[i];
}
}
}
return best;
}
void Dispatcher::ReceiveFragment(Fragment *in, Fragment *out) {
//lock nexus if run in thread.
// cerr << "Saving: " << in->id << endl;
SaveFragment(*nexus, *chain, *in, *out);
if(frags.count(in->id)) {
FragIO *frag = frags[in->id];
delete frag;
frags.erase(frags.find(in->id));
}
delete in;
delete out;
}
void Dispatcher::msghandler(message &msg) {
switch(msg.id) {
case MSG_FAIL: break;
case MSG_SEND: {
//get server!
Server *best = BestServer();
Fragment *fragin = (Fragment *)(msg.param);
if(!best) { //no server process locally....
// cerr << "No best!" << endl;
vector<Point3f> newvert;
vector<unsigned int> newface;
vector<BigLink> newbord;
Join(*fragin, newvert, newface, newbord);
float error = Decimate(QUADRIC,
(unsigned int)((newface.size()/3) * 0.5),
newvert, newface, newbord);
Fragment *fragout = new Fragment;
fragout->error = error;
fragout->id = fragin->id;
fragout->seeds = fragin->seeds;
fragout->seeds_id = fragin->seeds_id;
Split(*fragout, newvert, newface, newbord);
ReceiveFragment(fragin, fragout);
} else {
FragIO *frag = new FragIO(best, this, fragin);
assert(!frags.count(fragin->id));
frags[fragin->id] = frag;
frag->start();
}
} break;
case MSG_RECEIVE:
ReceiveFragment((Fragment *)(msg.param), (Fragment *)(msg.result));
break;
default:
defhandler(msg);
}
}

View File

@ -0,0 +1,81 @@
#include <ptypes/pinet.h>
#include <ptypes/pasync.h>
#include <vector>
#include <map>
#include <string>
namespace nxs {
#define MSG_SEND MSG_USER + 1
#define MSG_RECEIVE MSG_USER + 2
#define MSG_FAIL MSG_USER + 3
class Fragment;
class Nexus;
class VoronoiChain;
class Server;
class FragIO;
class Dispatcher;
class Opener: public pt::thread {
public:
Opener(Server *s): thread(false), server(s) {}
~Opener() { waitfor(); }
void execute();
void cleanup() {}
Server *server;
};
class Server: public pt::ipstream {
public:
Server(pt::string host, int port): ipstream(host, port),
connected(false), opener(this) {}
int queue;
pt::mutex reading;
pt::mutex writing;
bool connected;
Opener opener;
};
class Dispatcher: public pt::msgqueue {
public:
Dispatcher(Nexus *nx, VoronoiChain *ch):
count(0), nexus(nx), chain(ch) {}
~Dispatcher();
bool Init(const std::string &file);
void SendFragment(Fragment *frag);
void ReceiveFragment(Fragment *in, Fragment *out);
Server *BestServer();
void msghandler(pt::message &msg);
int count;
Nexus *nexus;
VoronoiChain *chain;
std::vector<Server *> servers;
std::map<int, FragIO *> frags;
};
class FragIO: public pt::thread {
public:
FragIO(Server *se, Dispatcher *di, Fragment *frag):
thread(false), server(se), dispatcher(di), fragin(frag) {}
~FragIO() { waitfor(); }
void execute();
void cleanup() {}
Server *server;
Dispatcher *dispatcher;
Fragment *fragin;
};
}

47
apps/nexus/nxspatcher.cpp Normal file
View File

@ -0,0 +1,47 @@
#include <iostream>
#include "crude.h"
using namespace nxs;
using namespace vcg;
using namespace std;
int main(int argc, char *argv[]) {
if(argc != 2) {
cerr << "Uso: " << argv[0] << " <side>\n";
return -1;
}
unsigned int side = atoi(argv[1]);
if(side > 1000 || side == 0) {
cerr << "Invalid side: " << argv[1] << endl;
return -1;
}
Crude crude;
if(!crude.Create("square")) {
cerr << "Could not create square" << endl;
return -1;
}
int half = side/2;
crude.Resize(side * side, (side-1) * (side-1) * 2);
for(unsigned int x = 0; x < side; x++)
for(unsigned int y = 0; y < side; y++) {
Point3f p(x*x*x/((float)side),
y*y*y/((float)side), x*y/((float)side));
crude.GetVertex(x + side * y) = p;
crude.GetBox().Add(p);
}
for(unsigned int x = 0; x < side-1; x++)
for(unsigned int y = 0; y < side-1; y++) {
unsigned int pos = x + side*y;
Crude::Face face(pos, pos + 1, pos + side);
crude.GetFace(0 + 2*x + (side-1)*y*2) = face;
face = Crude::Face(pos + 1, pos + 1 + side, pos +side);
crude.GetFace(1 + 2*x + (side-1)*y*2) = face;
}
crude.Close();
return 0;
}

179
apps/nexus/nxsserver.cpp Normal file
View File

@ -0,0 +1,179 @@
#include <ptypes/ptime.h>
#include <ptypes/pinet.h>
#include <ptypes/pasync.h>
using namespace pt;
#include "fragment.h"
#include "decimate.h"
using namespace nxs;
using namespace vcg;
using namespace std;
const int port = 10102;
class FragOutQueue: public msgqueue {
public:
FragOutQueue(ipstream &cli): client(cli) {}
void msghandler(message &msg) {
if(msg.id != MSG_USER) {
defhandler(msg);
return;
}
Fragment &fragment = *(Fragment *)(msg.param);
// pout.putf("Sending: %d\n", fragment.id);
outmemory outm;
outm.open();
fragment.Write(&outm);
pt::string a = outm.get_strdata();
try {
client.write((const char *)a, length(a));
client.flush();
pout.putf("Sent fragment id: %d\n", fragment.id);
} catch (estream *e) {
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
}
delete (Fragment *)(msg.param);
}
ipstream &client;
};
class FragInQueue: public msgqueue {
public:
FragInQueue(FragOutQueue &o): out(o) {}
void msghandler(message &msg) {
if(msg.id != MSG_USER) {
defhandler(msg);
return;
}
Fragment &fragin = *(Fragment *)(msg.param);
// pout.putf("Processing: %d\n", fragin.id);
vector<Point3f> newvert;
vector<unsigned int> newface;
vector<BigLink> newbord;
Join(fragin, newvert, newface, newbord);
float error = Decimate(QUADRIC,
(unsigned int)((newface.size()/3) * 0.5),
newvert, newface, newbord);
message *outmsg = new message(MSG_USER);
outmsg->param = (int)(new Fragment);
Fragment &fragout = *(Fragment *)(outmsg->param);
fragout.error = error;
fragout.id = fragin.id;
fragout.seeds = fragin.seeds;
fragout.seeds_id = fragin.seeds_id;
Split(fragout, newvert, newface, newbord);
out.post(outmsg);
delete (Fragment *)(msg.param);
}
FragOutQueue &out;
};
class Reader: public thread {
public:
Reader(ipstream &cli, FragInQueue &que):
thread(false), client(cli), queue(que) {}
~Reader() {waitfor(); }
void execute() {
while(1) {
if(get_signaled()) return;
message *msg = new message(MSG_USER);
msg->param = (int)(new Fragment);
Fragment &fragment = *(Fragment *)(msg->param);
if(!fragment.Read(&client)) {
pout.putf("Could not read!\n");
return;
}
queue.post(msg);
// pout.putf("Incoming: %d\n", fragment.id);
}
}
void cleanup() {}
ipstream &client;
FragInQueue &queue;
};
class Worker: public thread {
public:
Worker(FragInQueue &que): thread(false), queue(que) {}
~Worker() { waitfor(); }
void execute() {
queue.run();
}
void cleanup() {}
FragInQueue &queue;
};
class Writer: public thread {
public:
Writer(FragOutQueue &que): thread(false), queue(que) {}
~Writer() {waitfor(); }
void execute() {
queue.run();
}
void cleanup() {}
FragOutQueue &queue;
};
void servermain(ipstmserver& svr) {
ipstream client;
pout.putf("Ready to answer queries on port %d\n", port);
while(true) {
// serve() will wait for a connection request and will prepare
// the supplied ipstream object for talking to the peer.
svr.serve(client);
if (client.get_active()) {
try {
pout.putf("Incoming connection\n");
FragOutQueue out(client);
FragInQueue in(out);
Reader reader(client, in);
Worker worker(in);
Writer writer(out);
reader.start();
worker.start();
writer.start();
reader.waitfor();
worker.waitfor();
writer.waitfor();
client.flush();
client.close();
} catch(estream* e) {
perr.putf("Error: %s\n", pconst(e->get_message()));
delete e;
}
}
}
}
int main() {
ipstmserver svr;
try {
// bind to all local addresses on port 8085
svr.bindall(port);
// enter an infinite loop of serving requests
servermain(svr);
} catch(estream* e) {
perr.putf("FATAL: %s\n", pconst(e->get_message()));
delete e;
}
return 0;
}

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.5 2004/09/21 00:53:23 ponchio
Lotsa changes.
Revision 1.4 2004/08/27 00:39:28 ponchio
Rewrote.
@ -62,7 +65,8 @@ class PChain {
virtual unsigned int Locate(unsigned int level, const vcg::Point3f &p) = 0;
virtual void RemapFaces(Crude &crude, VFile<unsigned int> &face_remap,
virtual void RemapFaces(VFile<vcg::Point3f> &baricenters,
VFile<unsigned int> &face_remap,
std::vector<unsigned int> &patch_faces,
float scaling, int steps) = 0;

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.5 2004/07/05 15:49:39 ponchio
Windows (DevCpp, mingw) port.
Revision 1.4 2004/07/02 17:41:37 ponchio
Debug.
@ -66,6 +69,12 @@ void VertRemap::Close() {
borders.Close();
}
void VertRemap::Delete() {
all.Flush();
all.Delete();
borders.Delete();
}
void VertRemap::Resize(unsigned int n_vert) {
all.Resize(n_vert);
for(unsigned int i = 0; i < n_vert; i++)

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.4 2004/10/01 15:59:52 ponchio
Added include <assert.h>
Revision 1.3 2004/07/02 17:41:37 ponchio
Debug.
@ -55,6 +58,7 @@ class VertRemap {
bool Create(const std::string &file);
bool Load(const std::string &file);
void Close();
void Delete();
void Resize(unsigned int n_vert);
unsigned int Size();

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.14 2004/10/19 16:50:27 ponchio
Added row file access ....
Revision 1.13 2004/10/08 15:12:04 ponchio
Working version (maybe)
@ -78,7 +81,7 @@ Created
#ifndef VFILE_H
#define VFILE_H
#include "file.h"
#include "mfile.h"
#include <assert.h>
#include <map>
@ -94,7 +97,7 @@ Created
namespace nxs {
template <class T> class VFile: public File {
template <class T> class VFile: public MFile {
public:
struct Buffer {
@ -138,7 +141,7 @@ template <class T> class VFile: public File {
chunk_size = _chunk_size;
queue_size = _queue_size;
return File::Create(filename);
return MFile::Create(filename);
}
bool Load(const std:: string &filename,
@ -150,7 +153,7 @@ template <class T> class VFile: public File {
chunk_size = _chunk_size;
queue_size = _queue_size;
if(!File::Load(filename)) return false;
if(!MFile::Load(filename)) return false;
n_elements = size/sizeof(T);
return true;
}
@ -159,6 +162,11 @@ template <class T> class VFile: public File {
Flush();
}
void Delete() {
Flush();
MFile::Delete();
}
void Flush() {
list_iterator i;
for(i = buffers.begin(); i != buffers.end(); i++)
@ -176,7 +184,7 @@ template <class T> class VFile: public File {
void Resize(unsigned int elem) {
Flush();
File::Redim(elem * sizeof(T));
MFile::Redim(elem * sizeof(T));
n_elements = elem;
}

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.19 2004/11/03 16:31:38 ponchio
Trying to fix big patches.
Revision 1.18 2004/10/30 20:17:03 ponchio
Fixed big patches problem.
@ -159,6 +162,8 @@ bool VoronoiChain::Optimize(int mean, VoronoiPartition &part,
vector<unsigned int> &counts,
bool join) {
//remove small or really big patches.
unsigned int failed = 0;
vector<Point3f> seeds;
@ -233,8 +238,10 @@ bool VoronoiChain::Optimize(int mean, VoronoiPartition &part,
return failed == 0;
}
void VoronoiChain::Init(Crude &crude, float scaling, int steps) {
unsigned int f_cells = crude.Faces() / mean_size;
void VoronoiChain::Init(VFile<Point3f> &baricenters,
float scaling, int steps) {
unsigned int f_cells = baricenters.Size() / mean_size;
unsigned int c_cells = (unsigned int)(scaling * f_cells);
levels.push_back(VoronoiPartition());
@ -244,23 +251,22 @@ void VoronoiChain::Init(Crude &crude, float scaling, int steps) {
srand(0);
float fine_vmean = mean_size/2.0f;
float coarse_vmean = (mean_size/scaling)/2.0f;
float coarse_vmean = mean_size/scaling;
for(unsigned int i = 0; i < crude.Vertices(); i++) {
int f = (int)(fine_vmean * rand()/(RAND_MAX + 1.0));
int c = (int)(coarse_vmean * rand()/(RAND_MAX + 1.0));
for(unsigned int i = 0; i < baricenters.Size(); i++) {
int f = (int)(mean_size * (float)rand()/(RAND_MAX + 1.0));
int c = (int)(coarse_vmean * (float)rand()/(RAND_MAX + 1.0));
if(f == 2) {
Point3f &point = crude.GetVertex(i);
Point3f &point = baricenters[i];
fine.push_back(point);
}
if(c == 2) {
Point3f &point = crude.GetVertex(i);
Point3f &point = baricenters[i];
coarse.push_back(point);
}
}
//TODO! Check for duplicates (use the closest :P)
//cerr << "fine_seeds.size: " << fine.size() << endl;
// cerr << "fine_seeds.size: " << fine.size() << endl;
//cerr << "coarse_seeds.size: " << coarse.size() << endl;
fine.Init();
coarse.Init();
@ -277,18 +283,18 @@ void VoronoiChain::Init(Crude &crude, float scaling, int steps) {
centroids.resize(fine.size(), Point3f(0, 0, 0));
counts.resize(fine.size(), 0);
report.Init(crude.Vertices());
for(unsigned int v = 0; v < crude.Vertices(); v++) {
report.Init(baricenters.Size());
for(unsigned int v = 0; v < baricenters.Size(); v++) {
report.Step(v);
unsigned int target = fine.Locate(crude.vert[v]);
centroids[target] += crude.vert[v];
unsigned int target = fine.Locate(baricenters[v]);
centroids[target] += baricenters[v];
counts[target]++;
}
if(step == steps-1) {
if(!Optimize(fine_vmean, fine, centroids, counts, false))
if(!Optimize(mean_size, fine, centroids, counts, false))
step--;
} else
Optimize(fine_vmean, fine, centroids, counts, true);
Optimize(mean_size, fine, centroids, counts, true);
}
cerr << "Optimized (fine)!\n";
//here goes some optimization pass.
@ -302,23 +308,23 @@ void VoronoiChain::Init(Crude &crude, float scaling, int steps) {
counts.resize(coarse.size(), 0);
//radius.resize(coarse.size(), 0);
report.Init(crude.Vertices());
for(unsigned int v = 0; v < crude.Vertices(); v++) {
report.Init(baricenters.Size());
for(unsigned int v = 0; v < baricenters.Size(); v++) {
if(v & 0xffff) report.Step(v);
unsigned int ctarget = 0xffffffff;
ctarget = coarse.Locate(crude.vert[v]);
ctarget = coarse.Locate(baricenters[v]);
// float dist;
// coarse.Closest(crude.vert[v], ctarget, dist);
assert(ctarget != 0xffffffff);
centroids[ctarget] += crude.vert[v];
centroids[ctarget] += baricenters[v];
counts[ctarget]++;
//if(dist > radius[ctarget]) radius[ctarget] = dist;
}
if(step == steps-1) {
if(!Optimize(coarse_vmean, coarse, centroids, counts, false))
if(!Optimize((int)coarse_vmean, coarse, centroids, counts, false))
step --;
} else
Optimize(coarse_vmean, coarse, centroids, counts, true);
Optimize((int)coarse_vmean, coarse, centroids, counts, true);
}
cerr << "Optimized coarse!\n";
}
@ -329,12 +335,12 @@ unsigned int VoronoiChain::Locate(unsigned int level,
}
//TODO move this to nxsbuild
void VoronoiChain::RemapFaces(Crude &crude,
void VoronoiChain::RemapFaces(VFile<Point3f> &baricenters,
VFile<unsigned int> &face_remap,
vector<unsigned int> &patch_faces,
float scaling, int steps) {
Init(crude, scaling, steps);
Init(baricenters, scaling, steps);
//TODO: improve quality of patches and implement threshold.
typedef map<pair<unsigned int, unsigned int>, unsigned int> FragIndex;
@ -345,8 +351,8 @@ void VoronoiChain::RemapFaces(Crude &crude,
unsigned int totpatches = 0;
Point3f bari;
for(unsigned int i = 0; i < crude.Faces(); i++) {
bari = crude.GetBari(i);
for(unsigned int i = 0; i < baricenters.Size(); i++) {
bari = baricenters[i];
unsigned int fine = Locate(0, bari);
unsigned int coarse = Locate(1, bari);
@ -485,10 +491,10 @@ void VoronoiChain::BuildLevel(Nexus &nexus, unsigned int offset,
}
}
if(step == steps-1) {
if(!Optimize(coarse_vmean, coarse, centroids, counts, false))
if(!Optimize((int)coarse_vmean, coarse, centroids, counts, false))
step--;
} else
Optimize(coarse_vmean, coarse, centroids, counts, true);
Optimize((int)coarse_vmean, coarse, centroids, counts, true);
}
newfragments.clear();

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.5 2004/10/30 20:17:03 ponchio
Fixed big patches problem.
Revision 1.4 2004/09/30 00:27:42 ponchio
Lot of changes. Backup.
@ -49,7 +52,6 @@ First draft.
#include "pchain.h"
#include "pvoronoi.h"
#include "crude.h"
#include "nexus.h"
#include "vfile.h"
@ -67,9 +69,10 @@ class VoronoiChain: public PChain {
mean_size(mean_s), min_size(min_s), max_size(max_s) {}
virtual ~VoronoiChain() {}
void Init(Crude &crude, float scaling, int steps);
void Init(VFile<vcg::Point3f> &baricenters, float scaling, int steps);
virtual unsigned int Locate(unsigned int level, const vcg::Point3f &p);
void RemapFaces(Crude &crude, VFile<unsigned int> &face_remap,
void RemapFaces(VFile<vcg::Point3f> &baricenters,
VFile<unsigned int> &face_remap,
std::vector<unsigned int> &patch_faces, float scaling,
int steps);

View File

@ -24,6 +24,9 @@
History
$Log: not supported by cvs2svn $
Revision 1.21 2004/11/03 16:31:38 ponchio
Trying to fix big patches.
Revision 1.20 2004/10/30 20:17:03 ponchio
Fixed big patches problem.
@ -107,18 +110,17 @@ using namespace std;
#include "fragment.h"
#include "nxsbuild.h"
#include "watch.h"
#include "nxsdispatcher.h"
using namespace vcg;
using namespace nxs;
void BuildFragment(Nexus &nexus, VoronoiPartition &part,
set<unsigned int> &patches,
Nexus::Update &update,
Fragment &fragment);
void SaveFragment(Nexus &nexus, VoronoiChain &chain,
unsigned int level,
Nexus::Update &update,
Fragment &fragin,
Fragment &fragout);
void ReverseHistory(vector<Nexus::Update> &history);
@ -126,6 +128,9 @@ void ReverseHistory(vector<Nexus::Update> &history);
void TestBorders(Nexus &nexus);
void TestPatches(Nexus &nexus);
unsigned int current_level;
vector<unsigned int> patch_levels;
int main(int argc, char *argv[]) {
Decimation decimation = CLUSTER;
@ -258,13 +263,24 @@ int main(int argc, char *argv[]) {
return -1;
}
VFile<Point3f> baricenters;
if(!baricenters.Create(output + string(".bvr"))) {
cerr << "Could not create temporary baricenters file\n";
return -1;
}
baricenters.Resize(crude.Faces());
//TODO use some smarter rule :P
for(unsigned int i = 0; i < crude.Faces(); i++) {
baricenters[i] = crude.GetBari(i);
}
/* BUILDING FIRST LEVEL */
//Remapping faces and vertices using level 0 and 1 of the chain
cerr << "Remapping faces.\n";
vector<unsigned int> patch_faces;
vchain.RemapFaces(crude, face_remap, patch_faces,
vchain.RemapFaces(baricenters, face_remap, patch_faces,
scaling, optimization_steps);
cerr << "Remapping vertices.\n";
@ -274,6 +290,8 @@ int main(int argc, char *argv[]) {
if(stop_after_remap) return 0;
cerr << "Allocating space\n";
//allocate chunks for patches and copy faces (absoklute indexing) into them.
NexusAllocate(crude, nexus, face_remap, patch_faces, patch_verts);
@ -282,12 +300,20 @@ int main(int argc, char *argv[]) {
//insert vertices and remap faces, prepare borders
NexusFill(crude, nexus, vert_remap, border_remap);
// NexusFixBorder(nexus, border_remap);
//Now face_remap, vert_remap, border_remap can be deleted!
face_remap.Delete();
vert_remap.Delete();
border_remap.Delete();
baricenters.Delete();
//filling history
Nexus::Update update;
for(unsigned int i = 0; i < nexus.index.size(); i++)
for(unsigned int i = 0; i < nexus.index.size(); i++) {
update.created.push_back(i);
patch_levels.push_back(0);
}
nexus.history.push_back(update);
//unify vertices otherwise you may get cracks.
@ -297,9 +323,15 @@ int main(int argc, char *argv[]) {
/* BUILDING OTHER LEVELS */
Report report;
Dispatcher dispatcher(&nexus, &vchain);
if(!dispatcher.Init("servers.txt")) {
cerr << "Could not parse server file: " << "servers.txt"
<< " proceding locally\n";
}
unsigned int oldoffset = 0;
for(unsigned int level = 1; level < max_level; level++) {
current_level = level;
cerr << "Level: " << level << endl;
unsigned int newoffset = nexus.index.size();
@ -307,20 +339,20 @@ int main(int argc, char *argv[]) {
report.Init(vchain.oldfragments.size(), 1);
unsigned int fcount = 0;
vector<Nexus::Update> level_history;
map<unsigned int, set<unsigned int> >::iterator fragment;
for(fragment = vchain.oldfragments.begin();
fragment != vchain.oldfragments.end(); fragment++) {
report.Step(fcount++);
Fragment fragin;
BuildFragment(nexus, vchain.levels[level+1], (*fragment).second,
update, fragin);
Fragment *fragin = new Fragment;
BuildFragment(nexus, vchain.levels[level+1],
(*fragment).second, *fragin);
dispatcher.SendFragment(fragin);
/*
//this can be executed on a remote host
//TODO move this part to remote....
vector<Point3f> newvert;
@ -336,19 +368,19 @@ int main(int argc, char *argv[]) {
fragout.id = fragin.id;
fragout.seeds = fragin.seeds;
fragout.seeds_id = fragin.seeds_id;
Split(fragout, newvert, newface, newbord, vchain.levels[level+1]);
Split(fragout, newvert, newface, newbord);//, vchain.levels[level+1]);
SaveFragment(nexus, vchain, level, update, fragout);
level_history.push_back(update);
SaveFragment(nexus, vchain, fragin, fragout);
*/
dispatcher.processmsgs();
}
//TODO porcata!!!!
while(dispatcher.frags.size()) {
dispatcher.processmsgs();
}
report.Finish();
for(unsigned int i = 0; i < level_history.size(); i++)
nexus.history.push_back(level_history[i]);
report.Finish();
if(vchain.oldfragments.size() == 1) break;
@ -379,8 +411,8 @@ int main(int argc, char *argv[]) {
void BuildFragment(Nexus &nexus, VoronoiPartition &part,
set<unsigned int> &patches,
Nexus::Update &update,
Fragment &fragment) {
set<unsigned int>::iterator f;
for(f = patches.begin(); f != patches.end(); f++) {
fragment.pieces.push_back(NxsPatch());
@ -396,42 +428,54 @@ void BuildFragment(Nexus &nexus, VoronoiPartition &part,
memcpy(&*nxs.face.begin(), patch.FaceBegin(), patch.nf * 3*sizeof(short));
for(unsigned int i = 0; i < border.Size(); i++) {
Link &link = border[i];
if(!link.IsNull())
if(!link.IsNull() && patch_levels[link.end_patch] == current_level-1)
nxs.bord.push_back(link);
}
}
update.created.clear();
update.erased.clear();
set<unsigned int> &fcells = patches;
set<unsigned int>::iterator s;
for(s = fcells.begin(); s != fcells.end(); s++) {
update.erased.push_back(*s);
set<unsigned int> seeds;
vector<int> nears;
vector<float> dists;
int nnears = 10;
if(part.size() < 10) nnears = part.size();
for(f = patches.begin(); f != patches.end(); f++) {
Point3f &center = nexus.index[*f].sphere.Center();
part.Closest(center, nnears, nears, dists);
for(int i = 0; i < nnears; i++)
seeds.insert(nears[i]);
}
//copy all seeds! //TODO copy only closest ones
for(unsigned int i = 0; i < part.size(); i++) {
fragment.seeds.push_back(part[i]);
fragment.seeds_id.push_back(i);
for(f = seeds.begin(); f != seeds.end(); f++) {
Point3f &p = part[*f];
fragment.seeds.push_back(p);
fragment.seeds_id.push_back(*f);
}
}
void SaveFragment(Nexus &nexus, VoronoiChain &chain,
unsigned int level,
Nexus::Update &update,
Fragment &fragin,
Fragment &fragout) {
set<unsigned int> orig_patches;
Nexus::Update update;
for(unsigned int i = 0; i < fragin.pieces.size(); i++) {
NxsPatch &patch = fragin.pieces[i];
update.erased.push_back(patch.patch);
orig_patches.insert(patch.patch);
}
vector<unsigned int> patch_remap;
patch_remap.resize(fragout.pieces.size());
for(unsigned int i = 0; i < fragout.pieces.size(); i++) {
NxsPatch &patch = fragout.pieces[i];
//TODO detect best parameter below for bordersize...
unsigned int patch_idx = nexus.AddPatch(patch.vert.size(),
patch.face.size()/3,
6 * patch.bord.size());
patch_levels.push_back(current_level);
Nexus::PatchInfo &entry = nexus.index[patch_idx];
entry.error = fragout.error;
@ -455,19 +499,59 @@ void SaveFragment(Nexus &nexus, VoronoiChain &chain,
entry.sphere.Add(outpatch.vert[v]);
nexus.sphere.Add(outpatch.vert[v]);
}
//remap borders
vector<Link> actual;
//remap internal borders
for(unsigned int i = 0; i < outpatch.bord.size(); i++) {
Link &link = outpatch.bord[i];
if(link.end_patch >= (1<<31)) //internal
if(link.end_patch >= (1<<31)) { //internal
link.end_patch = patch_remap[link.end_patch - (1<<31)];
else { //if external add the reverse border
Border rborder = nexus.GetBorder(link.end_patch);
actual.push_back(link);
}
}
//TODO not efficient!
//processing external borders
for(unsigned int i = 0; i < outpatch.bord.size(); i++) {
Link &link = outpatch.bord[i];
if(link.end_patch >= (1<<31)) continue;
unsigned short &start_vert = link.start_vert;
unsigned int &start_patch = patch_idx;
Border cborder = nexus.GetBorder(link.end_patch);
for(unsigned int k = 0; k < cborder.Size(); k++) {
Link &clink = cborder[k];
if(clink.start_vert != link.end_vert) continue;
if(patch_levels[clink.end_patch] < current_level-1) continue;
unsigned short &end_vert = clink.end_vert;
unsigned int &end_patch = clink.end_patch;
Link newlink;
newlink.start_vert = start_vert;
newlink.end_vert = end_vert;
newlink.end_patch = end_patch;
actual.push_back(newlink);
// nexus.AddBorder(start_patch, newlink);
newlink.start_vert = end_vert;
newlink.end_vert = start_vert;
newlink.end_patch = start_patch;
nexus.AddBorder(end_patch, newlink);
/* Border rborder = nexus.GetBorder(clink.end_patch);
unsigned int pos = rborder.Size();
if(nexus.borders.ResizeBorder(link.end_patch, pos+1)) {
rborder = nexus.GetBorder(link.end_patch);
}
assert(rborder.Size() < rborder.Available());
assert(rborder.Available() > pos);
@ -475,17 +559,23 @@ void SaveFragment(Nexus &nexus, VoronoiChain &chain,
newlink.start_vert = link.end_vert;
newlink.end_vert = link.start_vert;
newlink.end_patch = patch_idx;
rborder[pos] = newlink;
rborder[pos] = newlink;*/
}
}
Border border = nexus.GetBorder(patch_idx);
assert(border.Available() >= outpatch.bord.size());
if(nexus.borders.ResizeBorder(patch_idx, actual.size())) {
border = nexus.GetBorder(patch_idx);
}
memcpy(&(border[0]), &(actual[0]),
actual.size() * sizeof(Link));
/* assert(border.Available() >= outpatch.bord.size());
if(nexus.borders.ResizeBorder(patch_idx, outpatch.bord.size())) {
border = nexus.GetBorder(patch_idx);
}
memcpy(&(border[0]), &(outpatch.bord[0]),
outpatch.bord.size() * sizeof(Link));
outpatch.bord.size() * sizeof(Link)); */
}
nexus.history.push_back(update);
}
void ReverseHistory(vector<Nexus::Update> &history) {