Obsah
1. Načítání a analýza souborů DXF
2. Grafické entity použité v 2D výkresech
3. Důležité parametry 2D entit
4. Jednoduchý prohlížeč 2D výkresů uložených ve formátu DXF
5. Parsing údajů uložených v DXF a interní reprezentace vybraných 2D entit
6. Vykreslení 2D entit s pomocí funkcí OpenGL
7. Možné další rozšíření funkcionality prohlížeče
8. Odkazy na další informační zdroje
9. Obsah dalšího pokračování tohoto seriálu
1. Načítání a analýza souborů DXF
Dnes si na praktickém příkladu ukážeme jednu variantu načítání a následného zobrazení DXF souborů. Při práci s tímto grafickým vektorovým formátem je nejsložitější samotné načítání výkresů. Formát DXF je totiž navržen poněkud volně a některé informace o grafických entitách, ze kterých se výkresy skládají, vůbec nemusí být v souborech typu DXF uloženy. Také nemusí být dodržena posloupnost uložení jednotlivých parametrů (atributů) grafických entit, proto se nemůžeme například spoléhat na to, že u úseček nejdříve načteme souřadnice prvního vrcholu a až následně souřadnice vrcholu druhého. Z tohoto důvodu bude dále uvedený prohlížeč souborů typu DXF vytvořen tak, aby dokázal načíst a zobrazit i DXF soubory s nestandardním uspořádáním jednotlivých údajů o grafických entitách.
2. Grafické entity použité v 2D výkresech
Již v předchozí části tohoto seriálu jsme si řekli, že se soubory typu DXF používají jak pro ukládání plošných výkresů (2D výkresů), tak i pro přenos trojrozměrných modelů mezi různými grafickými programy, typicky mezi specializovaným programem pro pořizování dat, CADem a následně programem typu CAM. Původním posláním souborového formátu DXF byla podpora komunikace AutoCADu (pravděpodobně nejznámějšího produktu firmy AutoDesk) s okolními aplikacemi, takže možnosti DXF reflektují i rozšiřující se možnosti tohoto známého programu od jednoduchých 2D výkresů přes parametrické plochy až po objemové modelování (CSG). Mezi grafické entity, jež jsou v souborech DXF popsané svou geometrií a dalšími důležitými atributy, které je možné použít v plošných výkresech, patří především:
Jméno entity | Význam |
---|---|
POINT | bod, jehož značka může být natočena různým směrem (natočení je zadáno úhlem) |
LINE | úsečka zadaná dvojicí bodů, snad nejpoužívanější entita v CADech |
POLYLINE | polyčára složená z úseček, oblouků nebo pomocí spline (také velmi často používaná) |
TRACE | „stopa“, což je vlastně úsečka s nenulovou tloušťkou |
CIRCLE | kružnice zadaná středem a poloměrem |
ARC | kruhový oblouk zadaný středem, poloměrem a dvojicí omezujících úhlů |
TEXT | textový řetězec zadaný bodem vložení, rotací, výškou a dalšími atributy |
SHAPE | značka (obdoba neměnného bloku) zadaná středem, velikostí, úhlem natočení a jménem značky |
INSERT | blok složený z jedné i více entit sdružených pod jedním jménem |
3. Důležité parametry 2D entit
Ke každé grafické entitě uložené v souboru typu DXF je přiřazeno několik číselných a/nebo textových parametrů (atributů). Nejdůležitější jsou samozřejmě informace o geometrii entity, tj. o jejím umístění v ploše výkresu či prostoru 3D modelu, velikosti apod. Nesmíme však zapomenout ani na další atributy, které mohou určovat způsob zobrazení entity. Mezi tyto atributy patří označení hladiny, ve které entita leží (hladiny je možné vypnout či zmrazit), barva entity, styl čáry, kterou je entita vykreslena, apod. V následujících tabulkách jsou vypsány typické parametry přiřazované k entitám použitým v plošných výkresech. Některé parametry jsou povinné (geometrické informace), další se v souborech typu DXF nemusí objevit – potom je zapotřebí použít inicializační hodnoty, načtené například z hlavičky DXF.
Entita typu „POINT“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice bodu |
20 | y-ová souřadnice bodu |
30 | z-ová souřadnice bodu |
50 | úhel natočení bodu |
Entita typu „LINE“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice prvního vrcholu |
20 | y-ová souřadnice prvního vrcholu |
30 | z-ová souřadnice prvního vrcholu |
11 | x-ová souřadnice druhého vrcholu |
21 | y-ová souřadnice druhého vrcholu |
31 | z-ová souřadnice druhého vrcholu |
Entita typu „CIRCLE“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice středu |
20 | y-ová souřadnice středu |
30 | z-ová souřadnice středu |
40 | poloměr kružnice |
Entita typu „ARC“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice středu |
20 | y-ová souřadnice středu |
30 | z-ová souřadnice středu |
40 | poloměr oblouku |
50 | počáteční úhel |
51 | koncový úhel |
Entita typu „TEXT“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice bodu vložení |
20 | y-ová souřadnice bodu vložení |
30 | z-ová souřadnice bodu vložení |
40 | výška textu |
50 | úhel natočení |
1 | vlastní řetězec (maximálně 256 znaků) |
Entita typu „TRACE“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice prvního vrcholu |
20 | y-ová souřadnice prvního vrcholu |
30 | z-ová souřadnice prvního vrcholu |
11 | x-ová souřadnice druhého vrcholu |
21 | y-ová souřadnice druhého vrcholu |
31 | z-ová souřadnice druhého vrcholu |
12 | x-ová souřadnice třetího vrcholu |
22 | y-ová souřadnice třetího vrcholu |
32 | z-ová souřadnice třetího vrcholu |
13 | x-ová souřadnice čtvrtého vrcholu |
23 | y-ová souřadnice čtvrtého vrcholu |
33 | z-ová souřadnice čtvrtého vrcholu |
Entita typu „SHAPE“
Číslo skupiny | Význam |
---|---|
8 | označení hladiny |
62 | barva entity |
10 | x-ová souřadnice bodu vložení |
20 | y-ová souřadnice bodu vložení |
30 | z-ová souřadnice bodu vložení |
40 | velikost |
50 | úhel natočení |
51 | úhel sklonu |
41 | relativní měřítko ve směru osy x |
2 | jméno tvaru |
Entita typu „POLYLINE“
Číslo skupiny | Význam |
---|---|
40 | počáteční tloušťka polyčáry |
41 | koncová tloušťka polyčáry |
70 | návěstí – bitové pole s dalšími informacemi o polyčáře |
71 | počet vrcholů ve směru M (3D plocha) |
72 | počet vrcholů ve směru N (3D plocha) |
73 | vyhlazení plochy ve směru M |
74 | vyhlazení plochy ve směru N |
75 | druh vyhlazení plochy |
66 | návěstí křivky |
Entita typu „INSERT“
Číslo skupiny | Význam |
---|---|
10 | x-ová souřadnice bodu vložení |
20 | y-ová souřadnice bodu vložení |
30 | z-ová souřadnice bodu vložení |
41 | měřítko ve směru osy x |
42 | měřítko ve směru osy y |
43 | měřítko ve směru osy z |
50 | úhel natočení |
44 | mezery mezi řádky |
45 | mezery mezi sloupci |
70 | počet sloupců – 1 |
71 | počet řádků – 1 |
2 | jméno bloku |
66 | bitové pole s dalšími informacemi o bloku |
4. Jednoduchý prohlížeč 2D výkresů uložených ve formátu DXF
Prohlížeč DXF souborů, jehož zdrojový soubor je dostupný pod tímto odkazem (HTML verze se zvýrazněním syntaxe), je naprogramovaný v céčku a pro zobrazování využívá možnosti grafické knihovny OpenGL. Při spuštění aplikace se musí jako první parametr zadat jméno souboru uloženého ve formátu DXF. Následně je soubor načten, zanalyzován a podporované grafické entity (úsečka, kružnice, oblouk, text) jsou uloženy do lineárního seznamu pro další zpracování.
Ovládání zobrazení je jednoduché. Levým tlačítkem myši se výkresem pohybuje, pravým tlačítkem se mění měřítko. Místo myši lze použít i kurzorové klávesy spolu s klávesami [Page Up] a [Page Down] (změna měřítka). Klávesami [C] a [L] se přepíná mezi barevným zvýrazněním hladin (layers) nebo přímo uložených barev entit (v obou případech se jedná o nepravé barvy). Tlačítkem Q a ESC se program ukončí.
Obrázek 3: Další screenshot demonstrační aplikace se zobrazeným výkresem (obsahující pouze entity LINE)
5. Parsing údajů uložených v DXF a interní reprezentace vybraných 2D entit
Nejsložitější částí celého demonstračního příkladu je parser a analyzér DXF formátu. Samotný parsing na té nejnižší úrovni složitý není, protože jsou všechny údaje rozloženy do skupin (groups), což je dvojice řádků, kde na prvním řádku je uloženo číslo skupiny a na řádku druhém hodnota daného parametru či atributu. Složitější je však analýza na vyšší úrovni, kdy musí program z kontextu určit, ke které entitě se daná skupina vztahuje. Kontextovost je možné programově zařídit mnoha způsoby; já jsem zvolil jednu z forem konečného automatu (KA), který na základě načtených čísel skupin mění svůj stav a přitom volá registrované callback funkce. Přechod mezi stavy konečného automatu a ukazatel na callback funkci je uložen ve struktuře se jménem t_transition:
typedef struct t_transition {
t_dxf_status status; // předchozí stav konečného automatu
char *input; // načtený kód skupiny
t_dxf_status newStatus; // nový stav konečného automatu
void (*fce)(char *input); // volaná callback funkce
} t_transition;
Ve skutečnosti implementovaný konečný automat mění svůj stav i při přijetí parametru/atributu skupiny, to je zajištěno speciálními callback funkcemi. Tyto funkce jsou určeny zejména pro ukládání načtených atributů do pomocných struktur. Aktuální konfigurace konečného automatu vypadá následovně:
static const t_transition transitions[]={
{Unknown, " 0", Delimiter, NULL},
{Delimiter, "SECTION", Section, NULL},
{Section, " 2", SecName, NULL},
{SecName, "HEADER", Header, callback_begin_header},
{SecName, "BLOCKS", Blocks, callback_begin_blocks},
{SecName, "ENTITIES", Entities, callback_begin_entities},
{Header, " 0", Header, NULL},
{Header, "ENDSEC", Unknown, callback_end_header},
{Blocks, " 0", Blocks, NULL},
{Blocks, "ENDSEC", Unknown, callback_end_blocks},
{Entities, " 0", Entities, NULL},
{Entities, "ENDSEC", Unknown, callback_end_entities},
{Entities, "LINE", Line, NULL},
{Line, " 8", LineLayer, callback_line_layer},
{Line, " 62", LineColor, callback_line_color},
{Line, " 10", LineX1, callback_line_x1},
{Line, " 20", LineY1, callback_line_y1},
{Line, " 11", LineX2, callback_line_x2},
{Line, " 21", LineY2, callback_line_y2},
{Line, " 0", Entities, callback_line_store},
{Entities, "CIRCLE", Circle, NULL},
{Circle, " 8", CircleLayer, callback_circle_layer},
{Circle, " 62", CircleColor, callback_circle_color},
{Circle, " 10", CircleX, callback_circle_x},
{Circle, " 20", CircleY, callback_circle_y},
{Circle, " 40", CircleR, callback_circle_r},
{Circle, " 0", Entities, callback_circle_store},
{Entities, "ARC", ArcEntity, NULL},
{ArcEntity, " 8", ArcLayer, callback_arc_layer},
{ArcEntity, " 62", ArcColor, callback_arc_color},
{ArcEntity, " 10", ArcX, callback_arc_x},
{ArcEntity, " 20", ArcY, callback_arc_y},
{ArcEntity, " 40", ArcR, callback_arc_r},
{ArcEntity, " 50", ArcU1, callback_arc_u1},
{ArcEntity, " 51", ArcU2, callback_arc_u2},
{ArcEntity, " 0", Entities, callback_arc_store},
{Entities, "TEXT", TextEntity, NULL},
{TextEntity, " 8", TextLayer, callback_text_layer},
{TextEntity, " 62", TextColor, callback_text_color},
{TextEntity, " 10", TextX, callback_text_x},
{TextEntity, " 20", TextY, callback_text_y},
{TextEntity, " 1", TextText, callback_text_text},
{TextEntity, " 0", Entities, callback_text_store},
{End, "", End, NULL} // jen ukonceni pro pruchod stavy KA
};
Poslední stav slouží pouze jako zarážka při procházení funkcí konečného automatu. Místo ukazatelů na callback funkce může být zadána i hodnota NULL, v tomto případě se samozřejmě žádná callback funkce nezavolá. Všimněte si také toho, že na ukončení definice entity v souboru DXF reagujeme tak, že se zavolá callback funkce callback_*_store, která právě načtenou a zrekonstruovanou entitu uloží do lineárního seznamu pro následné vykreslení. Procedura, která provádí přechod konečného automatu z jednoho stavu do druhého na základě načteného textového řádku ze souboru, vypadá následovně:
while (fgets(line, MAX_LINE_LENGTH, fin)) { // projit vsechny radky
lines++;
line[strlen(line)-1]=0; // oriznout <CR>
if (special_callback!=NULL) { // specialni callback funkce -> nacteni atributu
special_callback(line);
}
else { // normalni callback funkce -> prechod KA
for (t=0; transitions[t].status!=End; t++) {
if ((status==transitions[t].status) && (!strcmp(transitions[t].input, line))) {
if (transitions[t].fce!=NULL) transitions[t].fce(line);
status=transitions[t].newStatus;
break;
}
}
}
}
6. Vykreslení 2D entit s pomocí funkcí OpenGL
Pokud již máme naplněný seznam grafických entit, je jejich vykreslení poměrně jednoduché. Bod a úsečka se vykreslí přímo pomocí příslušných funkcí grafické knihovny OpenGL. Kružnice a kruhový oblouk nemají přímou podporu OpenGL (grafické akcelerátory totiž pracují pouze s lineárními prvky, protože jsou založeny na lineárních interpolátorech), proto jsou tyto entity vykresleny lomenou čarou – v případě kružnice se jedná o LINE_LOOP (uzavřená lomená čára), v případě oblouku a LINE_STRIP (neuzavřená lomená čára).
Přesnost vykreslení, tj. počet liniových částí, ze kterých se kružnice či oblouk skládají, je zadaný symbolickými konstantami. Text je pro jednoduchost vykreslován pouze v jedné velikosti pomocí funkcí z nadstavbové knihovny GLUT. Část zdrojového kódu, která se stará o vykreslování, vypadá následovně (funkce setColor() pouze simuluje barvovou paletu pomocí osmi základních barev):
//-----------------------------------------------------------------------------
// nastaveni barvy vykreslovani
//-----------------------------------------------------------------------------
void setColor(int color)
{
// velmi zjednodusena paleta
static float palette[][3]={
{1.0, 1.0, 1.0},
{0.0, 0.0, 1.0},
{0.0, 1.0, 0.0},
{0.0, 1.0, 1.0},
{1.0, 0.0, 0.0},
{1.0, 0.0, 1.0},
{1.0, 1.0, 0.0},
{0.5, 0.5, 0.5},
};
float r, g, b;
r=palette[color & 0x07][0];
g=palette[color & 0x07][1];
b=palette[color & 0x07][2];
glColor3f(r, g, b);
}
//-----------------------------------------------------------------------------
// vykresleni bodu
//-----------------------------------------------------------------------------
void drawPoint(void *entity)
{
GfxPoint *p=(GfxPoint*)entity;
if (colorStyle)
setColor(p->color);
else
setColor(p->layer);
glBegin(GL_POINTS);
glVertex2d(p->x, p->y);
glEnd();
}
//-----------------------------------------------------------------------------
// vykresleni usecky
//-----------------------------------------------------------------------------
void drawLine(void *entity)
{
GfxLine *l=(GfxLine*)entity;
if (colorStyle)
setColor(l->color);
else
setColor(l->layer);
glBegin(GL_LINES);
glVertex2d(l->x1, l->y1);
glVertex2d(l->x2, l->y2);
glEnd();
}
//-----------------------------------------------------------------------------
// vykresleni kruznice
//-----------------------------------------------------------------------------
void drawCircle(void *entity)
{
#define SEGMENTS 100
int i;
GfxCircle *c=(GfxCircle*)entity;
if (colorStyle)
setColor(c->color);
else
setColor(c->layer);
glBegin(GL_LINE_LOOP);
for (i=0; i<SEGMENTS; i++)
glVertex2d(c->x+c->r*cos(i*6.28/SEGMENTS), c->y+c->r*sin(i*6.28/SEGMENTS));
glEnd();
}
//-----------------------------------------------------------------------------
// vykresleni oblouku
//-----------------------------------------------------------------------------
void drawArc(void *entity)
{
#define SEGMENTS_A 50
#ifndef M_PI
#define M_PI 3.1415927
#endif
float u;
float a, b;
GfxArc *arc=(GfxArc*)entity;
if (colorStyle)
setColor(arc->color);
else
setColor(arc->layer);
if (arc->u1 > arc->u2) {
a=(arc->u2)*M_PI/180.0+M_PI;
b=(arc->u1)*M_PI/180.0+M_PI;
}
else {
a=(arc->u1)*M_PI/180.0;
b=(arc->u2)*M_PI/180.0;
}
glBegin(GL_LINE_STRIP);
for (u=a; u<=b; u+=(b-a)/SEGMENTS_A) {
glVertex2d(arc->x+arc->r*cos(u), arc->y+arc->r*sin(u));
}
glEnd();
}
//-----------------------------------------------------------------------------
// vykresleni textu
//-----------------------------------------------------------------------------
void drawText(void *entity)
{
char *c;
GfxText *t=(GfxText*)entity;
if (colorStyle)
setColor(t->color);
else
setColor(t->layer);
glPushMatrix();
glTranslatef(t->x, t->y, 0);
glScalef(0.05, 0.05, 0.05); // meritko (velikost) neodpovida skutecnosti!
for (c=t->str; *c!=0; c++) {
glutStrokeCharacter(GLUT_STROKE_ROMAN, *c);
}
glPopMatrix();
}
//-----------------------------------------------------------------------------
// prekresleni cele vektorove kresby
//-----------------------------------------------------------------------------
void redrawDrawing(double scale, // meritko obrazce
double xpos, // posun obrazce
double ypos)
{
Item *item=p_first;
float scale2=mouse.ztran0/100.0+1.0; // zmena meritka zadana mysi
// posun a zmena meritka kresby
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(xpos, ypos, 0); // posun zadany klavesnici
glTranslatef(mouse.xtran0, -mouse.ytran0, 0); // posun zadany mysi
// posunuti pocatku (pro zmenu meritka vuci stredu okna
// je nutna mala programova uprava)
glTranslatef(window.width/2, window.height/2, 0);
glScalef(scale, scale, scale); // zmena meritka zadana klavesnici
glScalef(scale2, scale2, scale2); // zmena meritka zadana mysi
glTranslatef(-window.width/2, -window.height/2, 0);
// projit celym seznamem a aplikovat v nem ulozene prikazy
while (item!=NULL) {
Type type=item->type;
switch (type) {
case T_POINT: drawPoint(item->entity); break;
case T_LINE: drawLine(item->entity); break;
case T_CIRCLE: drawCircle(item->entity); break;
case T_ARC: drawArc(item->entity); break;
case T_TEXT: drawText(item->entity); break;
default: break;
}
item=item->next;
}
}
Obrázek 4: Poslední screenshot demonstrační aplikace se zobrazeným výkresem (testování funkčnosti entit LINE a CIRCLE, výkres vytvořen v QCADu)
7. Možné další rozšíření funkcionality prohlížeče
Zde prezentovaný interaktivní prohlížeč DXF souborů je opravdu velmi jednoduchý a v žádném případě nenačítá a nezobrazuje všechny grafické entity, které se v těchto souborech mohou nacházet. Vzhledem ke způsobu konstrukce konečného automatu (KA) použitého při analýze DXF a pružným callback funkcím je však možné tento prohlížeč dále rozšiřovat a upravovat tak jeho funkcionalitu. Mezi užitečná rozšíření patří zejména:
- Načítání a zobrazování dalších 2D entit: stopy TRACE, elipsy (ELLIPSE) a zejména polyčáry (POLYLINE), které jsou velmi často používanou entitou.
- Podpora pro tvary (SHAPE), což jsou vlastně malé pojmenované vektorové objekty vkládané do výkresu.
- Načítání bloků a jejich rozklad na jednotlivé entity (INSERT, BLOCK).
- Zapínání a vypínání jednotlivých hladin ve výkrese.
- Načítání dalších atributů entit, zejména typů čar.
8. Odkazy na další informační zdroje
- Aaron A. Collins:
AutoCAD DXF file to DKB Data File Converter,
version 1.0 - Autodesk, Inc.:
AutoLISP Release 9, Programmer's reference,
Autodesk Ltd., Oct. 1987 - Autodesk, Inc.:
AutoLISP Release 10, Programmer's reference,
Autodesk Ltd., Sept. 1988 - Autodesk, Inc.:
Drawing Interchange and File Formats, AutoCAD Release 12
Copyright © 1982–1990, 1992, Autodesk, Inc. - Autodesk, Inc.:
AutoCAD Reference Manual
Autodesk, Inc., 1995 - AutoCAD DXF (Wikipedia):
http://en.wikipedia.org/wiki/AutoCAD_DXF - Open Design Aliance:
http://www.opendesign.com/
9. Obsah dalšího pokračování tohoto seriálu
V následujícím pokračování seriálu o grafických formátech dokončíme rozsáhlou kapitolu věnovanou vektorovému formátu DXF. Ukážeme si strukturu DXF použitou při práci s trojrozměrnými modely, samozřejmě spolu s jednoduchým demonstračním příkladem (bude se jednat o interaktivní prohlížeč 3D modelů). Uvidíme, že v případě trojrozměrných modelů soubory typu DXF obsahují odlišnou množinu entit, než tomu je v případě uložených 2D výkresů.