mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 06:05:58 -06:00
1210 lines
31 KiB
C++
1210 lines
31 KiB
C++
#include "vfkstrm.h"
|
|
|
|
static struct VFKLongName
|
|
{
|
|
const char *longname;
|
|
const char *shortname;
|
|
}
|
|
vfk_long_names[] =
|
|
{
|
|
{ "PARCELY", "PAR" },
|
|
{ "BUDOVY", "BUD" },
|
|
{ "CASTI_BUDOV", "CABU" },
|
|
{ "ZP_OCHRANY_NEM", "ZPOCHN" },
|
|
{ "D_POZEMKU", "DRUPOZ" },
|
|
{ "ZP_VYUZITI_POZ", "ZPVYPO" },
|
|
{ "ZDROJE_PARCEL_ZE", "ZDPAZE" },
|
|
{ "ZP_URCENI_VYMERY", "ZPURVY" },
|
|
{ "T_BUDOV", "TYPBUD" },
|
|
{ "MAPOVE_LISTY", "MAPLIS" },
|
|
{ "KATASTR_UZEMI", "KATUZE" },
|
|
{ "CASTI_OBCI", "CASOBC" },
|
|
{ "R_ZPOCHR", "RZO" },
|
|
{ "ZP_VYUZITI_BUD", "ZPVYBU" },
|
|
{ "JEDNOTKY", "JED" },
|
|
{ "T_JEDNOTEK", "TYPJED" },
|
|
{ "ZP_VYUZITI_JED", "ZPVYJE" },
|
|
{ "BONIT_DILY_PARC", "BDP" },
|
|
{ "OPRAV_SUBJEKTY", "OPSUB" },
|
|
{ "VLASTNICTVI", "VLA" },
|
|
{ "CHAR_OS", "CHAROS" },
|
|
{ "TELESA", "TEL" },
|
|
{ "JINE_PRAVNI_VZTAHY", "JPV" },
|
|
{ "T_PRAVNICH_VZTAHU", "TYPRAV" },
|
|
{ "RIZENI_KU", "RIZKU" },
|
|
{ "OBJEKTY_RIZENI", "OBJRIZ" },
|
|
{ "PREDMETY_RIZENI", "PRERIZ" },
|
|
{ "UCASTNICI", "UCAST" },
|
|
{ "ADRESY", "ADRUC" },
|
|
{ "LISTINY", "LISTIN" },
|
|
{ "DALSI_UDAJE_LISTINY", "DUL" },
|
|
{ "LISTINY_DALSI_UDAJE", "LDU" },
|
|
{ "T_LISTIN", "TYPLIS" },
|
|
{ "T_PREDMETU_R", "TYPPRE" },
|
|
{ "TYPY_RIZENI", "TYPRIZ" },
|
|
{ "TYPY_UCASTNIKU", "TYPUCA" },
|
|
{ "UCASTNICI_TYP", "UCTYP" },
|
|
{ "R_LIST", "RL" },
|
|
{ "SOURADNICE_OBRAZU", "SOBR" },
|
|
{ "SPOJENI_B_POLOH", "SBP" },
|
|
{ "SPOJENI_B_MAPY", "SBM" },
|
|
{ "KODY_CHAR_Q_BODU", "KODCHB" },
|
|
{ "T_SOURAD_SYS", "TYPSOS" },
|
|
{ "HRANICE_PARCEL", "HP" },
|
|
{ "OBRAZY_PARCEL", "OP" },
|
|
{ "OBRAZY_BUDOV", "OB" },
|
|
{ "DALSI_PRVKY_MAPY", "DPM" },
|
|
{ "OBRAZY_BODU_BP", "OBBP" },
|
|
{ "T_PRVKU_P_DAT", "TYPPPD" },
|
|
{ "HRANICE_BPEJ", "HBPEJ" },
|
|
{ "OZNACENI_BPEJ", "OBPEJ" },
|
|
{ "NAVRHY_ZMEN_KM", "NZ" },
|
|
{ "NZ_ZPMZ", "NZZP" },
|
|
{ "SOURADNICE_POLOHY", "SPOL" },
|
|
{ "REZ_PARCELNI_CISLA", "RECI" },
|
|
{ "DOTCENA_PAR_CISLA", "DOCI" },
|
|
{ "DOT_HIST_PAR_CISLA", "DOHICI" },
|
|
{ "SROVNAVACI_SESTAVENI", "SRST" },
|
|
{ 0 }
|
|
};
|
|
|
|
static bool IsPointMap(String name)
|
|
{
|
|
return name == "SPOJENI_B_MAPY"
|
|
|| name == "SOURADNICE_OBRAZU"
|
|
|| name == "DALSI_PRVKY_MAPY"
|
|
|| name == "OBRAZY_PARCEL"
|
|
|| name == "OBRAZY_BUDOV"
|
|
|| name == "OBRAZY_BODU_BP"
|
|
|| name == "SOURADNICE_POLOHY"
|
|
|| name == "PARCELY"
|
|
|| name == "BUDOVY";
|
|
}
|
|
|
|
inline bool IsIdent(char c)
|
|
{
|
|
return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '_';
|
|
}
|
|
|
|
static String GetVTypeName(int vtype)
|
|
{
|
|
switch(vtype) {
|
|
default:
|
|
case VOID_V: return "void";
|
|
case ERROR_V: return "error";
|
|
case UNKNOWN_V: return "unknown";
|
|
case VALUE_V: return "Value";
|
|
case INT_V: return "int";
|
|
case DOUBLE_V: return "double";
|
|
case INT64_V: return "int64";
|
|
case STRING_V: return "String";
|
|
case WSTRING_V: return "WString";
|
|
case DATE_V: return "Date";
|
|
case TIME_V: return "Time";
|
|
case VALUEARRAY_V: return "ValueArray";
|
|
return NFormat("unknown(%d)", vtype);
|
|
}
|
|
}
|
|
|
|
inline Time VfkReadTime(const char *p, void *endptr)
|
|
{
|
|
int fields[6];
|
|
ZeroArray(fields);
|
|
int nfld = 0;
|
|
for(;;)
|
|
if(IsDigit(*p)) {
|
|
int v = ScanInt(p, &p);
|
|
if(IsNull(v) || v < 0)
|
|
return Null;
|
|
fields[nfld++] = v;
|
|
if(nfld >= 6)
|
|
break;
|
|
}
|
|
else if(*p == ' ' || *p == ':' || *p == '.')
|
|
p++;
|
|
else
|
|
break;
|
|
if(endptr)
|
|
*(const char **)endptr = p;
|
|
if(nfld < 3)
|
|
return Null;
|
|
if(fields[0] >= 1 && fields[0] <= 31
|
|
&& fields[1] >= 1 && fields[1] <= 12
|
|
&& fields[2] >= 1900 && fields[2] <= 2900
|
|
&& fields[3] >= 0 && fields[3] <= 23
|
|
&& fields[4] >= 0 && fields[4] <= 59
|
|
&& fields[5] >= 0 && fields[5] <= 59) {
|
|
Time dt(fields[2], fields[1], fields[0], fields[3], fields[4], fields[5]);
|
|
if(dt.IsValid())
|
|
return dt;
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
static String VFKParsePolvz(const char *text)
|
|
{
|
|
for(; *text; text++)
|
|
if(text[0] == 'P' && text[1] == 'O' && text[2] == 'L' && text[3] == 'V' && text[4] == 'Z' && text[5] == ':') {
|
|
text += 6;
|
|
while(*text && (byte)*text <= ' ')
|
|
text++;
|
|
const char *b = text;
|
|
bool slash = false;
|
|
while(*text && (byte)*text > ' ')
|
|
if(*text++ == '/')
|
|
slash = true;
|
|
String out;
|
|
int len = text - b;
|
|
if(slash || len <= 4)
|
|
out = String(b, text);
|
|
else {
|
|
out.Cat(b, len - 4);
|
|
out.Cat('/');
|
|
out.Cat(text - 4, 4);
|
|
}
|
|
return out;
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
static String VFKParseDOPLKOD(String s)
|
|
{
|
|
const char *p = s;
|
|
while(*p && !(p[0] == '/' && p[1] == ' ' && p[2] == 'D' && p[3] == 'O'
|
|
&& p[4] == 'P' && p[5] == 'L' && p[6] == 'K' && p[7] == 'O' && p[8] == 'D'))
|
|
p++;
|
|
if(!*p)
|
|
return s;
|
|
while(p > s.Begin() && (byte)p[-1] <= ' ')
|
|
p--;
|
|
return s.Left(p - s.Begin());
|
|
}
|
|
|
|
/*
|
|
void VfkStream::Table::FlushCache()
|
|
{
|
|
scanned = scanned_sbp = scanned_sobr = false;
|
|
row_offsets.Clear();
|
|
segment_index.Clear();
|
|
point_index.Clear();
|
|
extents.Clear();
|
|
points.Clear();
|
|
sobr_map.Clear();
|
|
par_ku_map.Clear();
|
|
cobce_obec_map.Clear();
|
|
tel_ku_map.Clear();
|
|
bud_id_index.Clear();
|
|
bud_id_zdb.Clear();
|
|
bud_id_ob.Clear();
|
|
ku_index.Clear();
|
|
obec_kod_index.Clear();
|
|
}
|
|
*/
|
|
|
|
int VfkStream::Table::SizeOfInstance() const
|
|
{
|
|
return columns.GetCount() * 100
|
|
/*
|
|
+ row_offsets.GetCount() * sizeof(int)
|
|
+ segment_index.GetCount() * sizeof(int) + point_index.GetCount() * sizeof(int)
|
|
+ extents.GetCount() * sizeof(Rectf) + points.GetCount() * sizeof(const Pointf *)
|
|
+ sobr_map.GetCount() * (sizeof(double) + sizeof(Pointf) + 4 * sizeof(int))
|
|
+ par_ku_map.GetCount() * (6 * sizeof(int))
|
|
+ cobce_obec_map.GetCount() * (6 * sizeof(int))
|
|
+ tel_ku_map.GetCount() * (6 * sizeof(int))
|
|
+ bud_id_zdb.GetCount() * (5 * sizeof(int) + sizeof(Pointf))
|
|
+ bud_id_ob.GetCount() * 200
|
|
+ ku_index.GetCount() * (4 * sizeof(int))
|
|
+ bud_id_index.GetCount() * (4 * sizeof(int))
|
|
+ obec_kod_index.GetCount() * (4 * sizeof(int));
|
|
*/
|
|
;
|
|
}
|
|
|
|
VfkStream::Data::Data(VfkStream *vfk_, Table *table_)
|
|
: vfk(vfk_), table(table_)
|
|
{
|
|
header = table->header;
|
|
row_buffer.SetCount(table->columns.GetCount());
|
|
row_index = -1;
|
|
}
|
|
|
|
int64 VfkStream::Data::GetCount() const
|
|
{
|
|
if(!table->IsScanned())
|
|
vfk->ScanTable(*table);
|
|
return table->header ? vfk->header.GetCount() : table->row_count;
|
|
}
|
|
|
|
const Vector<Value>& VfkStream::Data::operator [] (int64 row) const
|
|
{
|
|
if(!table->IsScanned())
|
|
vfk->ScanTable(*table);
|
|
if(header)
|
|
return vfk->header[(int)row];
|
|
ASSERT(row >= 0 && row < table->row_count);
|
|
if(row != row_index) {
|
|
row_index = row;
|
|
vfk->ReadRow(*table, row_index = row, row_buffer);
|
|
}
|
|
return row_buffer;
|
|
}
|
|
|
|
GisObject VfkStream::Data::GetPath(int64 row, double resolution) const
|
|
{
|
|
return Null;
|
|
/*
|
|
int bs = table->segment_index[row + 0], es = table->segment_index[row + 1];
|
|
if(es <= bs)
|
|
return Null;
|
|
RefPtr<GisObjectPath> path = new GisObjectPath;
|
|
path->extent = table->extents[row];
|
|
if(path->extent.Width() <= resolution && path->extent.Height() <= resolution) {
|
|
path->index.SetCount(1, 1);
|
|
path->points.SetCount(1, path->extent.CenterPoint());
|
|
}
|
|
else {
|
|
path->index.Reserve(es - bs);
|
|
int bx = table->point_index[bs], ex = table->point_index[es];
|
|
for(int t = bs; t < es; t++)
|
|
path->index.Add(table->point_index[t + 1] - bx);
|
|
path->points.Reserve(ex - bx);
|
|
for(int t = bx; t < ex; t++)
|
|
path->points.Add(*table->points[t]);
|
|
}
|
|
return GisObject(~path);
|
|
*/
|
|
}
|
|
|
|
GisObject VfkStream::Data::GetArea(int64 row, double resolution) const
|
|
{
|
|
return Null;
|
|
/*
|
|
int bs = table->segment_index[row + 0], es = table->segment_index[row + 1];
|
|
if(es <= bs)
|
|
return Null;
|
|
RefPtr<GisObjectArea> area = new GisObjectArea;
|
|
area->extent = table->extents[row];
|
|
if(area->extent.Width() <= resolution && area->extent.Height() <= resolution) {
|
|
area->index.SetCount(1, 1);
|
|
area->points.SetCount(1, area->extent.CenterPoint());
|
|
}
|
|
else {
|
|
area->index.Reserve(es - bs);
|
|
int bx = table->point_index[bs], ex = table->point_index[es];
|
|
for(int t = bs; t < es; t++)
|
|
area->index.Add(table->point_index[t + 1] - bx);
|
|
area->points.Reserve(ex - bx);
|
|
for(int t = bx; t < ex; t++)
|
|
area->points.Add(*table->points[t]);
|
|
}
|
|
return GisObject(~area);
|
|
*/
|
|
}
|
|
|
|
VfkStream::VfkStream()
|
|
{
|
|
charset = CHARSET_DEFAULT;
|
|
}
|
|
|
|
void VfkStream::Open(const VectorMap<int, String>& fns)
|
|
{
|
|
Close();
|
|
charset = CHARSET_ISO8859_2;
|
|
file_groups <<= fns;
|
|
streams.SetCount(file_groups.GetCount());
|
|
for(int i = 0; i < streams.GetCount(); i++)
|
|
if(!streams[i].Open(file_groups[i]))
|
|
throw Exc(NFormat("cannot open file '%s'", file_groups[i]));
|
|
indexfilename = GetTempFileName("vfk");
|
|
if(!indexfile.Open(indexfilename, FileStream::CREATE))
|
|
throw Exc(NFormat("cannot create indexfile '%s'", indexfilename));
|
|
String hdrname = "X_HEADER";
|
|
Table& hdr = tables.Add(hdrname);
|
|
hdr.name = hdr.rawname = hdrname;
|
|
hdr.header = true;
|
|
hdr.row_count = 0;
|
|
ASSERT(hdr.columns.GetCount() == HDR_ID);
|
|
hdr.AddColumn(Column("ID", STRING_V, 30));
|
|
ASSERT(hdr.columns.GetCount() == HDR_ORD);
|
|
hdr.AddColumn(Column("ORD", INT_V));
|
|
ASSERT(hdr.columns.GetCount() == HDR_STR);
|
|
hdr.AddColumn(Column("STR", STRING_V, 1000));
|
|
ASSERT(hdr.columns.GetCount() == HDR_NUM);
|
|
hdr.AddColumn(Column("NUM", DOUBLE_V));
|
|
ASSERT(hdr.columns.GetCount() == HDR_DTM);
|
|
hdr.AddColumn(Column("DTM", TIME_V));
|
|
ASSERT(hdr.columns.GetCount() == HDR_COUNT);
|
|
Scan();
|
|
}
|
|
|
|
void VfkStream::Close()
|
|
{
|
|
streams.Clear();
|
|
if(indexfile.IsOpen()) {
|
|
indexfile.Close();
|
|
FileDelete(indexfilename);
|
|
}
|
|
tables.Clear();
|
|
header.Clear();
|
|
}
|
|
|
|
int VfkStream::SizeOfInstance() const
|
|
{
|
|
int si = sizeof(*this);
|
|
for(int i = 0; i < tables.GetCount(); i++)
|
|
si += SizeOf(tables[i]);
|
|
return si;
|
|
}
|
|
|
|
/*
|
|
int VfkStream::MappingSizeOfInstance() const
|
|
{
|
|
return mapping.IsOpen() ? mapping.GetLength() : 0;
|
|
}
|
|
*/
|
|
|
|
Vector<String> VfkStream::GetTables() const
|
|
{
|
|
Vector<String> out;
|
|
out <<= tables.GetKeys();
|
|
return out;
|
|
}
|
|
|
|
VfkStream::Data VfkStream::GetData(String table)
|
|
{
|
|
int f = tables.Find(table);
|
|
ASSERT(f >= 0);
|
|
/*
|
|
if(table == "HRANICE_PARCEL")
|
|
ScanSBP(table, "HP_ID");
|
|
else if(table == "OBRAZY_BUDOV")
|
|
ScanSBP(table, "OB_ID");
|
|
else if(table == "DALSI_PRVKY_MAPY")
|
|
ScanSBP(table, "DPM_ID");
|
|
*/
|
|
return GetDataRaw(f);
|
|
}
|
|
|
|
void VfkStream::Dump()
|
|
{
|
|
puts(NFormat("Index file: %s", indexfilename));
|
|
puts(NFormat("%d file group(s):", file_groups.GetCount()));
|
|
for(int i = 0; i < file_groups.GetCount(); i++)
|
|
puts(NFormat("\t[%d] = %d / %s (%n B)", i, file_groups.GetKey(i), file_groups[i],
|
|
streams[i].IsOpen() ? streams[i].GetSize() : 0));
|
|
puts("Tables:");
|
|
for(int t = 0; t < tables.GetCount(); t++) {
|
|
const Table& tbl = tables[t];
|
|
puts(NFormat("\tTable(%d, %s (%s)) sections:\n", t, tbl.name, tbl.rawname));
|
|
for(int s = 0; s < tbl.file_index.GetCount(); s++)
|
|
puts(NFormat("\t\t[%d]: file index [%d], range %n .. %n, %n bytes, %n rows",
|
|
s, tbl.file_index[s], tbl.begin_offset[s], tbl.end_offset[s],
|
|
tbl.end_offset[s] - tbl.begin_offset[s], tbl.row_count));
|
|
/*
|
|
for(int c = 0; c < tbl.columns.GetCount(); c++) {
|
|
const Column& col = tbl.columns[c];
|
|
puts(NFormat("\t\t(%d, %s): %s %~d", c, col.name, GetVTypeName(col.vtype), col.width));
|
|
}
|
|
*/
|
|
Data data = GetData(tbl.name);
|
|
puts(NFormat("%d row(s):", data.GetCount()));
|
|
for(int i = 0; i < 10 && i < data.GetCount(); i++) {
|
|
const Vector<Value>& row = data[i];
|
|
String out;
|
|
for(int v = 0; v < row.GetCount(); v++)
|
|
out << (v ? ";" : "") << StdFormat(row[v]);
|
|
puts(out);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VfkStream::SpeedTest()
|
|
{
|
|
for(int i = 0; i < tables.GetCount(); i++) {
|
|
int begin = msecs();
|
|
Data data = GetData(tables[i].name);
|
|
int get = msecs();
|
|
if(data.GetCount() > 0)
|
|
data[0];
|
|
int first = msecs();
|
|
for(int r = 1; r < data.GetCount(); r++)
|
|
data[r];
|
|
int nth = msecs();
|
|
puts(NFormat("%s: get(%d), first(%d), nth(%2v)",
|
|
tables[i].name, get - begin, first - get, (nth - first) / max<double>((double)data.GetCount() - 1, 1)));
|
|
}
|
|
}
|
|
|
|
bool VfkStream::IsDateTime(String field) const
|
|
{
|
|
return field == "PLATNOST" || field == "VYTVORENO";
|
|
}
|
|
|
|
WString VfkStream::ReadString(const char *b, const char **endptr) const
|
|
{
|
|
if(*b++ != '\"')
|
|
throw Exc("'\"' expected");
|
|
StringBuffer out;
|
|
for(;;) {
|
|
const char *b0 = b;
|
|
while(*b && *b != '\"')
|
|
b++;
|
|
out.Cat(b0, b - b0);
|
|
if(*b != '\"' || *++b != '\"')
|
|
break;
|
|
out.Cat(*b++);
|
|
}
|
|
if(endptr)
|
|
*endptr = b;
|
|
return ToUnicode((String)out, charset);
|
|
}
|
|
|
|
void VfkStream::ScanHeader(const char *b, Table& table)
|
|
{
|
|
int colid = 0;
|
|
while(*b && *b != '\n') {
|
|
if(IsAlpha(*b)) {
|
|
const char *id = b;
|
|
while(IsIdent(*++b))
|
|
;
|
|
Column col;
|
|
col.name = String(id, b);
|
|
try {
|
|
while(*b == ' ')
|
|
b++;
|
|
col.vtype = ERROR_V;
|
|
col.width = Null;
|
|
switch(*b++) {
|
|
case 'N': {
|
|
col.vtype = DOUBLE_V;
|
|
break;
|
|
}
|
|
case 'T': {
|
|
col.vtype = STRING_V;
|
|
if(!IsDigit(*b))
|
|
throw Exc("missing string width");
|
|
col.width = ScanInt(b, &b);
|
|
break;
|
|
}
|
|
case 'D': {
|
|
col.vtype = TIME_V;
|
|
break;
|
|
}
|
|
default: {
|
|
throw Exc(NFormat("invalid data type '%c'", *--b));
|
|
}
|
|
}
|
|
if(colid < table.columns.GetCount()) {
|
|
const Column& old = table.columns[colid];
|
|
if(old.name != col.name)
|
|
throw Exc(NFormat("column name mismatch (%s / %s)", old.name, col.name));
|
|
if(old.vtype != col.vtype)
|
|
throw Exc(NFormat("column type mismatch (%d / %d)", old.vtype, col.vtype));
|
|
if(old.width != col.width)
|
|
throw Exc(NFormat("column width mismatch (%~d / %~d)", old.width, col.width));
|
|
}
|
|
else
|
|
table.AddColumn(col);
|
|
colid++;
|
|
}
|
|
catch(Exc e) {
|
|
throw Exc(NFormat("column '%s': %s", colid, e));
|
|
}
|
|
}
|
|
else
|
|
b++;
|
|
}
|
|
}
|
|
|
|
void VfkStream::SkipRow(Stream& strm)
|
|
{
|
|
RTIMING("VfkStream::SkipRow");
|
|
enum { BATCH = 100 };
|
|
char buffer[BATCH + 1];
|
|
int count = strm.Get(buffer, BATCH);
|
|
buffer[count] = '\n';
|
|
const char *p = buffer;
|
|
for(;;)
|
|
if(*p == '\n') {
|
|
if(p - buffer < count) {
|
|
strm.SeekCur((p - buffer) - count + 1);
|
|
return;
|
|
}
|
|
buffer[count = strm.Get(buffer, BATCH)] = '\n';
|
|
p = buffer;
|
|
if(!count)
|
|
return;
|
|
}
|
|
else if(*p++ == '\"') {
|
|
for(;;) {
|
|
if(*p == '\n' && p - buffer >= count) {
|
|
buffer[count = strm.Get(buffer, BATCH)] = '\n';
|
|
p = buffer;
|
|
if(!count)
|
|
return;
|
|
}
|
|
else if(*p++ == '\"') {
|
|
if(*p == '\n' && p - buffer >= count) {
|
|
buffer[count = strm.Get(buffer, BATCH)] = '\n';
|
|
p = buffer;
|
|
if(!count)
|
|
return;
|
|
}
|
|
if(*p != '\"')
|
|
break;
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
void VfkStream::ScanRow(Table& table) const
|
|
{
|
|
table.row_offsets.Add(b - (const char *)mapping.Begin() - 1);
|
|
return SkipRow(b);
|
|
}
|
|
*/
|
|
|
|
void VfkStream::ReadRow(const Table& table, int64 index, Vector<Value>& row)
|
|
{
|
|
RTIMING("VfkStream::ReadRow");
|
|
indexfile.Seek(table.row_offsets_xpos + 12 * index);
|
|
int64 off = indexfile.Get64le();
|
|
int len = indexfile.Get16le();
|
|
int ixf = indexfile.Get16le();
|
|
if(off < 0 || len < 0 || ixf < 0 || ixf > streams.GetCount())
|
|
return;
|
|
Buffer<char> rowbuf(len + 1);
|
|
Stream& strm = streams[ixf];
|
|
strm.Seek(off);
|
|
strm.Get(rowbuf, len);
|
|
rowbuf[len] = 0;
|
|
const char *b = rowbuf;
|
|
for(int i = 0; *b++ == ';' && i < row.GetCount(); i++) {
|
|
switch(table.columns[i].vtype) {
|
|
case DOUBLE_V: {
|
|
row[i] = ScanDouble(b, &b);
|
|
break;
|
|
}
|
|
|
|
case TIME_V: {
|
|
if(*b == '\"') {
|
|
row[i] = VfkReadTime(b + 1, &b);
|
|
if(*b == '\"')
|
|
b++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case STRING_V: {
|
|
row[i] = ReadString(b, &b);
|
|
break;
|
|
}
|
|
|
|
default: NEVER(); return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void VfkStream::Scan()
|
|
{
|
|
RTIMING("VfkStream::Scan");
|
|
for(int i = 0; i < file_groups.GetCount(); i++)
|
|
ScanFile(i);
|
|
}
|
|
|
|
void VfkStream::ScanFile(int fx)
|
|
{
|
|
RTIMING("VfkStream::ScanFile");
|
|
Stream& strm = streams[fx];
|
|
int64 last_line = strm.GetSize();
|
|
while(last_line > 0) {
|
|
strm.Seek(last_line - 1);
|
|
if(strm.Get() == '\n')
|
|
break;
|
|
last_line--;
|
|
}
|
|
strm.Seek(0);
|
|
try {
|
|
int c;
|
|
int64 rowpos = strm.GetPos();
|
|
while((c = strm.Get()) == '&' && ((c = strm.Get()) == 'H' || c == 'D') && IsAlpha(strm.Term())) {
|
|
char type = c;
|
|
int64 begin = strm.GetPos();
|
|
SkipRow(strm);
|
|
rowpos = strm.GetPos();
|
|
int len = (int)(strm.GetPos() - begin);
|
|
StringBuffer linebuf(len + 1);
|
|
strm.Seek(begin);
|
|
strm.Get(linebuf, len);
|
|
linebuf[len] = 0;
|
|
const char *b = linebuf;
|
|
const char *id = b;
|
|
while(IsIdent(*++b))
|
|
;
|
|
String ident(id, b);
|
|
if(*b++ != ';')
|
|
throw Exc(NFormat("';' expected after '%s' (found: '%c', %2:02x)", ident, *b));
|
|
if(type == 'D') {
|
|
String fident = "X_" + ident;
|
|
int f = tables.Find(fident);
|
|
if(f < 0)
|
|
throw Exc(NFormat("unexpected data for filter table '%s'", ident));
|
|
// b = ScanRow(b, tables[f]);
|
|
}
|
|
else if(IsAlpha(*b)) {
|
|
String fident = "X_" + ident;
|
|
Table& tbl = tables.GetAdd(fident);
|
|
tbl.name = tbl.rawname = fident;
|
|
tbl.row_count = 0;
|
|
ScanHeader(b, tbl);
|
|
}
|
|
else {
|
|
do {
|
|
Vector<Value> row;
|
|
row.SetCount(HDR_COUNT);
|
|
if(*b == '\"') {
|
|
WString text = ReadString(b, &b);
|
|
if(IsDateTime(ident) && !IsNull(text)) {
|
|
Time dt = VfkReadTime(text.ToString(), NULL);
|
|
if(IsNull(dt))
|
|
throw Exc(NFormat("invalid date/time value %s", AsCString(text.ToString())));
|
|
row[HDR_DTM] = dt;
|
|
}
|
|
else {
|
|
row[HDR_STR] = text;
|
|
if(ident == "CODEPAGE")
|
|
if(text == WString("EE8MSWIN1250")) charset = CHARSET_WIN1250;
|
|
}
|
|
}
|
|
else {
|
|
double num = ScanDouble(b, &b);
|
|
if(IsNull(num))
|
|
throw Exc("invalid numeric value");
|
|
row[HDR_NUM] = num;
|
|
}
|
|
int l = header.FindLast(ident);
|
|
row[HDR_ID] = ident;
|
|
row[HDR_ORD] = (l >= 0 ? (int)header[l][HDR_ORD] + 1 : 0);
|
|
header.Add(ident) = row;
|
|
}
|
|
while(*b++ == ';');
|
|
b--;
|
|
}
|
|
}
|
|
strm.Seek(rowpos);
|
|
while(strm.Get() == '&' && strm.Get() == 'B' && IsAlpha(strm.Term())) {
|
|
int64 header_offset = strm.GetPos();
|
|
SkipRow(strm);
|
|
int64 begin_offset = strm.GetPos();
|
|
int len = (int)(begin_offset - header_offset);
|
|
Buffer<char> linebuf(len + 1);
|
|
strm.Seek(header_offset);
|
|
strm.Get(linebuf, len);
|
|
linebuf[len] = 0;
|
|
const char *b = linebuf;
|
|
const char *id = b;
|
|
while(IsIdent(*++b))
|
|
;
|
|
int idlen = b - id;
|
|
String ident(id, b);
|
|
if(*b++ != ';')
|
|
throw Exc(NFormat("';' expected after '%s' (found: '%c', %2:02x)", ident, *b));
|
|
String name = ident;
|
|
for(const VFKLongName *ln = vfk_long_names; ln->shortname; ln++)
|
|
if(name == ln->shortname) {
|
|
name = ln->longname;
|
|
break;
|
|
}
|
|
Table& tbl = tables.GetAdd(name);
|
|
tbl.name = name;
|
|
tbl.rawname = ident;
|
|
ScanHeader(b, tbl);
|
|
int64 p = begin_offset, e = last_line;
|
|
Buffer<char> idbuf(idlen + 3);
|
|
while(p < e) {
|
|
int64 m = (p + e) >> 1;
|
|
while(m > p) {
|
|
char part[100];
|
|
int partsize = (int)min<int64>(m - p, sizeof(part));
|
|
strm.Seek(m - partsize);
|
|
strm.Get(part, partsize);
|
|
const char *x = &part[partsize];
|
|
while(x > part && x[-1] != '\n')
|
|
x--;
|
|
int lfpos = x - part;
|
|
if(x > part && --x > part && x[-1] == '\r')
|
|
x--;
|
|
m -= partsize - lfpos;
|
|
if(x <= part)
|
|
continue;
|
|
if(*--x != '\xA4')
|
|
break;
|
|
m -= lfpos - (x - part);
|
|
}
|
|
strm.Seek(m);
|
|
if(strm.Get(idbuf, idlen + 3) != idlen + 3 || idbuf[0] != '&' || idbuf[1] != 'D'
|
|
|| memcmp(~idbuf + 2, id, idlen) || idbuf[idlen + 2] != ';')
|
|
e = m;
|
|
else {
|
|
SkipRow(strm);
|
|
p = strm.GetPos();
|
|
}
|
|
}
|
|
int xgrp = file_groups.GetKey(fx);
|
|
int f;
|
|
for(f = 0; f < tbl.file_index.GetCount(); f++)
|
|
if(file_groups.GetKey(tbl.file_index[f]) == xgrp)
|
|
break;
|
|
if(f >= tbl.file_index.GetCount()) {
|
|
tbl.file_index.Add(fx);
|
|
tbl.begin_offset.Add(begin_offset);
|
|
tbl.end_offset.Add(p);
|
|
}
|
|
strm.Seek(p);
|
|
}
|
|
}
|
|
catch(Exc e) {
|
|
throw Exc(NFormat("%s (offset %n): %s", file_groups[fx], strm.GetPos(), e));
|
|
}
|
|
}
|
|
|
|
void VfkStream::ScanTable(Table& tbl)
|
|
{
|
|
RTIMING("VfkStream::ScanTable");
|
|
if(tbl.row_count >= 0)
|
|
return;
|
|
tbl.row_count = 0;
|
|
tbl.row_offsets_xpos = indexfile.GetPos();
|
|
int idlen = tbl.rawname.GetLength();
|
|
enum { READBUF = 10000000 };
|
|
Buffer<char> readbuf(READBUF + 1);
|
|
for(int i = 0; i < tbl.file_index.GetCount(); i++) {
|
|
RTIMING("VfkStream::ScanTable / file");
|
|
int fx = tbl.file_index[i];
|
|
Stream& strm = streams[fx];
|
|
int64 pos = tbl.begin_offset[i], end = tbl.end_offset[i];
|
|
while(pos < end) {
|
|
RTIMING("VfkStream::ScanTable / buffer");
|
|
strm.Seek(pos);
|
|
int part = strm.Get(readbuf, (int)min<int64>(end - pos, READBUF));
|
|
readbuf[part] = '\n';
|
|
const char *b = readbuf, *e = b + part;
|
|
for(;;) {
|
|
RTIMING("VfkStream::ScanTable / row");
|
|
int64 boff = pos + (b - ~readbuf);
|
|
if(e - b < idlen + 3) {
|
|
pos = boff;
|
|
break;
|
|
}
|
|
if(b[0] != '&' || b[1] != 'D' || memcmp(b + 2, tbl.rawname, idlen) || b[2 + idlen] != ';') {
|
|
pos = end;
|
|
break;
|
|
}
|
|
b += 3 + idlen;
|
|
int64 eoff = -1;
|
|
while(b < e) {
|
|
if(*b == '\n') {
|
|
if(b < e)
|
|
eoff = pos + (++b - ~readbuf);
|
|
break;
|
|
}
|
|
else if(*b++ == '\"') {
|
|
for(;;)
|
|
if(*b == '\n' && b >= e)
|
|
break;
|
|
else if(*b++ == '\"') {
|
|
if(*b == '\n' && b >= e)
|
|
break;
|
|
if(*b != '\"')
|
|
break;
|
|
b++;
|
|
}
|
|
}
|
|
}
|
|
if(eoff < 0) {
|
|
pos = boff;
|
|
break;
|
|
}
|
|
RTIMING("VfkStream::ScanTable / index stream write");
|
|
indexfile.Put64le(boff);
|
|
indexfile.Put16le((int)(eoff - boff));
|
|
indexfile.Put16le(fx);
|
|
tbl.row_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VfkStream::Data VfkStream::GetDataRaw(int t)
|
|
{
|
|
RTIMING("VfkStream::GetData");
|
|
return Data(this, &tables[t]);
|
|
}
|
|
|
|
/*
|
|
void VfkStream::ScanSOBR()
|
|
{
|
|
int f = tables.Find("SOURADNICE_OBRAZU");
|
|
if(f < 0 || !tables[f].sobr_map.IsEmpty())
|
|
return;
|
|
Table& sobr = tables[f];
|
|
int id = sobr.columns.Find("ID");
|
|
int px = sobr.columns.Find("SOURADNICE_Y");
|
|
int py = sobr.columns.Find("SOURADNICE_X");
|
|
if(id < 0 || px < 0 || py < 0)
|
|
return;
|
|
Data data = GetDataRaw(f);
|
|
for(int i = 0, dc = data.GetCount(); i < dc; i++) {
|
|
const Vector<Value>& row = data[i];
|
|
double x = row[px], y = row[py];
|
|
if(!IsNull(x) && !IsNull(y))
|
|
sobr.sobr_map.Add(row[id], Pointf(-x, -y));
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanSBP(String table, String sbp_id)
|
|
{
|
|
int tf = tables.Find(table), sf = tables.Find("SPOJENI_B_POLOH"), of = tables.Find("SOURADNICE_OBRAZU");
|
|
if(tf < 0 || sf < 0 || of < 0)
|
|
return;
|
|
Table& tbl = tables[tf];
|
|
int tid = tbl.columns.Find("ID");
|
|
if(tid < 0 || tbl.scanned_sbp)
|
|
return;
|
|
Data tdata = GetDataRaw(tf);
|
|
tbl.segment_index.SetCount(tdata.GetCount() + 1, 0);
|
|
tbl.point_index.Add(0);
|
|
tbl.extents.SetCount(tdata.GetCount(), Null);
|
|
tbl.scanned_sbp = true;
|
|
|
|
Table& sbp = tables[sf];
|
|
int rid = sbp.columns.Find(sbp_id);
|
|
int por = sbp.columns.Find("PORADOVE_CISLO_BODU");
|
|
int pid = sbp.columns.Find("BP_ID");
|
|
if(rid < 0 || por < 0 || pid < 0)
|
|
return;
|
|
|
|
ScanSOBR();
|
|
Table& sobr = tables[of];
|
|
Index<double> rid_index;
|
|
Vector< Vector<int> > por_lists;
|
|
Vector< Vector<double> > pid_lists;
|
|
|
|
Data sdata = GetDataRaw(sf);
|
|
for(int i = 0, sc = sdata.GetCount(); i < sc; i++) {
|
|
const Vector<Value>& row = sdata[i];
|
|
if(!IsNull(row[rid])) {
|
|
int n = rid_index.FindAdd(row[rid]);
|
|
por_lists.At(n).Add(row[por]);
|
|
pid_lists.At(n).Add(row[pid]);
|
|
}
|
|
}
|
|
|
|
for(int i = 0, tc = tdata.GetCount(); i < tc; i++) {
|
|
Rectf ext = Null;
|
|
int f = rid_index.Find(tdata[i][tid]);
|
|
if(f >= 0) {
|
|
Vector<int>& por = por_lists[f];
|
|
Vector<double>& pid = pid_lists[f];
|
|
IndexSort(por, pid);
|
|
for(int j = 0; j < pid.GetCount(); j++) {
|
|
int p = sobr.sobr_map.Find(pid[j]);
|
|
if(p >= 0)
|
|
ext |= *(tbl.points.Add() = &sobr.sobr_map[p]);
|
|
}
|
|
if(!pid.IsEmpty())
|
|
tbl.point_index.Add(tbl.points.GetCount());
|
|
}
|
|
tbl.extents[i] = ext;
|
|
tbl.segment_index[i + 1] = tbl.point_index.GetCount() - 1;
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanParKUMap()
|
|
{
|
|
int pf = tables.Find("PARCELY");
|
|
if(pf < 0)
|
|
return;
|
|
Table& par = tables[pf];
|
|
if(!par.par_ku_map.IsEmpty())
|
|
return;
|
|
Data pdata = GetDataRaw(pf);
|
|
int pid = par.columns.Find("BUD_ID");
|
|
if(pid < 0)
|
|
return;
|
|
int kid = par.columns.Find("KATUZE_KOD");
|
|
if(kid < 0)
|
|
return;
|
|
for(int i = 0, pc = pdata.GetCount(); i < pc; i++) {
|
|
const Vector<Value>& row = pdata[i];
|
|
par.par_ku_map.Add(row[pid], row[kid]);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanTelKUMap()
|
|
{
|
|
int tf = tables.Find("TELESA");
|
|
if(tf < 0)
|
|
return;
|
|
Table& tel = tables[tf];
|
|
if(!tel.tel_ku_map.IsEmpty())
|
|
return;
|
|
Data tdata = GetDataRaw(tf);
|
|
int tid = tel.columns.Find("ID");
|
|
if(tid < 0)
|
|
return;
|
|
int kid = tel.columns.Find("KATUZE_KOD");
|
|
if(kid < 0)
|
|
return;
|
|
for(int i = 0, tc = tdata.GetCount(); i < tc; i++) {
|
|
const Vector<Value>& row = tdata[i];
|
|
tel.tel_ku_map.Add(row[tid], row[kid]);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanKuIndex()
|
|
{
|
|
int kf = tables.Find("KATASTR_UZEMI");
|
|
if(kf < 0)
|
|
return;
|
|
Table& kat = tables[kf];
|
|
if(!kat.ku_index.IsEmpty())
|
|
return;
|
|
Data tdata = GetDataRaw(kf);
|
|
int kod = kat.columns.Find("KOD");
|
|
if(kod < 0)
|
|
return;
|
|
int tc = tdata.GetCount();
|
|
kat.ku_index.Reserve(tc);
|
|
for(int i = 0; i < tc; i++)
|
|
kat.ku_index.Add(tdata[i][kod]);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanBudIndex()
|
|
{
|
|
int bf = tables.Find("BUDOVY");
|
|
if(bf < 0)
|
|
return;
|
|
Table& bud = tables[bf];
|
|
if(!bud.bud_id_index.IsEmpty())
|
|
return;
|
|
Data tdata = GetDataRaw(bf);
|
|
int bid = bud.columns.Find("ID");
|
|
if(bid < 0)
|
|
return;
|
|
int tc = tdata.GetCount();
|
|
bud.bud_id_index.Reserve(tc);
|
|
for(int i = 0; i < tc; i++)
|
|
bud.bud_id_index.Add(tdata[i][bid]);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanObrBud()
|
|
{
|
|
int of = tables.Find("OBRAZY_BUDOV");
|
|
if(of < 0)
|
|
return;
|
|
Table& obr = tables[of];
|
|
if(!obr.bud_id_ob.IsEmpty() || !obr.bud_id_zdb.IsEmpty())
|
|
return;
|
|
Data tdata = GetDataRaw(of);
|
|
int bid = obr.columns.Find("BUD_ID");
|
|
int sx = obr.columns.Find("SOURADNICE_X");
|
|
int sy = obr.columns.Find("SOURADNICE_Y");
|
|
int obt = obr.columns.Find("OBRBUD_TYPE");
|
|
if(bid < 0 || sx < 0 || sy < 0 || obt < 0)
|
|
return;
|
|
int tc = tdata.GetCount();
|
|
for(int i = 0; i < tc; i++) {
|
|
const Vector<Value>& row = tdata[i];
|
|
int budid = row[bid];
|
|
Pointf pt(row[sy], row[sx]);
|
|
String tp = row[obt];
|
|
if(!IsNull(pt.x) && !IsNull(pt.y))
|
|
if(tp == "ZDB")
|
|
obr.bud_id_zdb.Add(budid, -pt);
|
|
else if(tp == "OB")
|
|
obr.bud_id_ob.GetAdd(budid).Add(-pt);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanObceIndex()
|
|
{
|
|
int of = tables.Find("OBCE");
|
|
if(of < 0)
|
|
return;
|
|
Table& obce = tables[of];
|
|
if(!obce.obec_kod_index.IsEmpty())
|
|
return;
|
|
Data tdata = GetDataRaw(of);
|
|
int kid = obce.columns.Find("KOD");
|
|
if(kid < 0)
|
|
return;
|
|
int tc = tdata.GetCount();
|
|
obce.obec_kod_index.Reserve(tc);
|
|
for(int i = 0; i < tc; i++)
|
|
obce.obec_kod_index.Add(tdata[i][kid]);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanCastiObciMap()
|
|
{
|
|
int cf = tables.Find("CASTI_OBCI");
|
|
if(cf < 0)
|
|
return;
|
|
Table& cobce = tables[cf];
|
|
if(!cobce.cobce_obec_map.IsEmpty())
|
|
return;
|
|
Data cdata = GetDataRaw(cf);
|
|
int ck = cobce.columns.Find("KOD");
|
|
if(ck < 0)
|
|
return;
|
|
int ok = cobce.columns.Find("OBCE_KOD");
|
|
if(ok < 0)
|
|
return;
|
|
for(int i = 0, cc = cdata.GetCount(); i < cc; i++) {
|
|
const Vector<Value>& row = cdata[i];
|
|
cobce.cobce_obec_map.Add(row[ck], row[ok]);
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
void VfkStream::ScanPAR()
|
|
{
|
|
int pf = tables.Find("PARCELY");
|
|
if(pf < 0)
|
|
return;
|
|
Table& par = tables[pf];
|
|
if(par.scanned_sbp)
|
|
return;
|
|
par.scanned_sbp = true;
|
|
Data pdata = GetDataRaw(pf);
|
|
par.segment_index.SetCount(pdata.GetCount() + 1, 0);
|
|
par.point_index.Add(0);
|
|
par.extents.SetCount(pdata.GetCount(), Null);
|
|
int pid = par.columns.Find("ID");
|
|
if(pid < 0)
|
|
return;
|
|
int hf = tables.Find("HRANICE_PARCEL");
|
|
if(hf < 0)
|
|
return;
|
|
Table& hp = tables[hf];
|
|
int hp1 = hp.columns.Find("PAR_ID_1");
|
|
int hp2 = hp.columns.Find("PAR_ID_2");
|
|
if(hp1 < 0 || hp2 < 0)
|
|
return;
|
|
ScanSBP("HRANICE_PARCEL", "HP_ID");
|
|
Index<double> hp1ix, hp2ix;
|
|
Data hdata = GetDataRaw(hf);
|
|
hp1ix.Reserve(hdata.GetCount());
|
|
hp2ix.Reserve(hdata.GetCount());
|
|
for(int i = 0, dc = hdata.GetCount(); i < dc; i++) {
|
|
const Vector<Value>& row = hdata[i];
|
|
hp1ix.Add(row[hp1]);
|
|
hp2ix.Add(row[hp2]);
|
|
}
|
|
for(int i = 0, pc = pdata.GetCount(); i < pc; i++) {
|
|
Rectf ext = Null;
|
|
double id = pdata[i][pid];
|
|
Index<const Pointf *, PtrHash> sb, se;
|
|
Vector<int> sh;
|
|
for(int i1 = hp1ix.Find(id); i1 >= 0; i1 = hp1ix.FindNext(i1)) {
|
|
int bi = hp.point_index[hp.segment_index[i1]], li = hp.point_index[hp.segment_index[i1 + 1]] - 1;
|
|
if(bi < li) {
|
|
sh.Add(i1);
|
|
sb.Add(hp.points[bi]);
|
|
se.Add(hp.points[li]);
|
|
}
|
|
}
|
|
for(int i2 = hp2ix.Find(id); i2 >= 0; i2 = hp2ix.FindNext(i2)) {
|
|
int bi = hp.point_index[hp.segment_index[i2]], li = hp.point_index[hp.segment_index[i2 + 1]] - 1;
|
|
if(bi < li) {
|
|
sh.Add(i2);
|
|
sb.Add(hp.points[bi]);
|
|
se.Add(hp.points[li]);
|
|
}
|
|
}
|
|
for(;;) {
|
|
int f;
|
|
for(f = 0; f < sh.GetCount() && sb.IsUnlinked(f); f++)
|
|
;
|
|
if(f >= sh.GetCount())
|
|
break;
|
|
int begin = par.points.GetCount();
|
|
for(int bi = hp.point_index[hp.segment_index[sh[f]]],
|
|
ei = hp.point_index[hp.segment_index[sh[f] + 1]]; bi < ei; bi++)
|
|
ext |= *(par.points.Add() = hp.points[bi]);
|
|
sb.Unlink(f);
|
|
se.Unlink(f);
|
|
const Pointf *close = sb[f];
|
|
const Pointf *next = se[f];
|
|
while(next != close) {
|
|
bool direct = true;
|
|
if((f = sb.Find(next)) >= 0)
|
|
direct = true;
|
|
else if((f = se.Find(next)) >= 0)
|
|
direct = false;
|
|
else {
|
|
double d, b = 1e100;
|
|
for(int x = 0; x < sh.GetCount(); x++)
|
|
if(!sb.IsUnlinked(x)) {
|
|
if((d = Distance(*sb[x], *next)) < b) {
|
|
f = x;
|
|
direct = true;
|
|
b = d;
|
|
}
|
|
if((d = Distance(*se[x], *next)) < b) {
|
|
f = x;
|
|
direct = false;
|
|
b = d;
|
|
}
|
|
}
|
|
if(f < 0)
|
|
break;
|
|
}
|
|
int bi = hp.point_index[hp.segment_index[sh[f]]];
|
|
int ei = hp.point_index[hp.segment_index[sh[f] + 1]];
|
|
if(direct) {
|
|
if(hp.points[bi] != next)
|
|
ext |= *(par.points.Add() = hp.points[bi]);
|
|
while(++bi < ei)
|
|
ext |= *(par.points.Add() = hp.points[bi]);
|
|
next = hp.points[bi - 1];
|
|
}
|
|
else {
|
|
if(hp.points[--ei] != next)
|
|
ext |= *(par.points.Add() = hp.points[ei]);
|
|
while(--ei >= bi)
|
|
ext |= *(par.points.Add() = hp.points[ei]);
|
|
next = hp.points[ei + 1];
|
|
}
|
|
sb.Unlink(f);
|
|
se.Unlink(f);
|
|
}
|
|
if(par.points.GetCount() > begin + 1 && par.points.Top() == par.points[begin])
|
|
par.points.Drop();
|
|
par.point_index.Add(par.points.GetCount());
|
|
}
|
|
par.segment_index[i + 1] = par.point_index.GetCount() - 1;
|
|
par.extents[i] = ext;
|
|
}
|
|
}
|
|
*/
|