ПРИЛОЖЕНИЕ Г. АДАПТАЦИЯ КОМПЛЕКСА ГРАФИЧЕСКИХ ПРОГРАММ ГРАФОР В ОПЕРАЦИОННЫХ СИСТЕМАХ WINDOWS И LINUX


Реализация средств отображения в пакете ГРАФОР

Формат файлов HP-GL, используемый в пакете ГРАФОР

Возращаемое значение:

TRUE – в случае благополучного завершения,
FALSE – в случае неудачи.

Для повышения мобильности пакета ГРАФОР было сделано следующее. Поскольку средства непосредственного вывода изображения в различных операционных системах и/или средах различны, вывод изображения на экран осуществляется так: сначала программа, использующая пакет, записывает файл в подмножество языка HP-GL и файл закрывается, а затем вызывается подпрограмма, читающая этой файл, интерпретирующая команды HP-GL и выводящая соответствующую графику на экран. Заметим, что при этом вся "вычислительная часть" пакета (т.е. получение изображения, а ещё точнее – координат отрезков) остается неизменной, а подпрограмма, реализующая непосредственный вывод на дисплей, может использовать различные средства для этого вывода, а также может быть написана и на другом языке программирования.

Остановимся более подробно на языке описания данных HP-GL, созданным более четверти века назад (1976 г.) и поддерживаемом (наряду с более поздним стандартом HP-GL/2) многими производителями (как оборудования, в основном плоттеров и принтеров, так и программных средств, в первую очередь CAD-систем).

Язык использует только коды ASCII. Оператор языка обязательно включает мнемонический код операции (mnemonics), который состоит из двух прописных либо строчных букв, и несколько параметров, разделенных запятыми и/или пробелами. В качестве разделителя между мнемоническим кодом и первым параметром используется пробел, который может отсутствовать. Параметры могут быть необязательными. Если опущен какой либо из них, то должны отсутствовать и все параметры, следующие за ним.

Для разделения операторов используется точка с запятой, в случае отсутствия разделителя концом оператора считается начало следующего. Управляющие символы CR и LF при интерпретации игнорируются.

Задаваемые к качестве параметров в операторе числовые значения записываются в виде цепочек ASCII-символов. Допускается до пяти знаков дробной части. Если дробная часть отсутствует, десятичная точка может опускаться. В качестве параметров других типов допускаются также только цепочки символов. Целые значения типа clamped integer должны удовлетворять неравенству

(–32767) £ ClampedIntegerType £ 32767,
а вещественные
(–32767.9999) £ ClampedRealType £ 32767.9999.

Таким образом, структура оператора (назовем его ХХ) выглядит следующим образом:

ХХ <параметр>… <разделитель>< параметр>.

Пример вариантов записи конкретного оператора:

"PDPU10,20" или "PD;PU10,20;" или "PD PU 10 20;".

Ядро языка HP-GL/2 содержит 55 операторов, которые должны поддерживаться всеми устройствами вывода изображения. Заметим, что даже при этом возможны случаи "разночтения" разными устройствами. Например, команда LT(LineType – тип линии) в плоттере HP7475A воспринимает первый параметр, принимающий значения 0, 1, 2, 3, 4, 5 и 6. А для плоттера HP7550A он может быть и отрицательным (0, ±1, ±2, ±3, ±4, ±5, ±6). К тому же в перьевых плоттерах карусели имеют различное количество гнезд (≡цветов).

Таким образом, лучше выбрать и использовать только необходимый минимум команд, позволяющих реализовывать все графические операции. Приведем их краткое описание.

IN Инициализация. Восстанавливает стандартные значения всех параметров устройства.
IP ЛНx ЛНy ПВx ПВy Определение значений масштабных точек (левого нижнего и правого верхнего углов чертежа).
SC Xmin, Xmax, Ymin, Ymax Задание системы координат пользователя.
SP N Установить цвет, определяемый номером.
PU X, Y Переместиться в указанную точку.
PD X, Y Провести линию в указанную точку.

Для наглядности приведем фрагмент файла, записываемого в результате работы пакета:

IN;
IP    0, 4019, 6028,    0;
SC    0,  300,    0,  200;
SP
1
;PU
139
139
;PD
139
20
;SP
2
;PU
139
25
;PD
278
25
. . . . . . . .
;SP
3
;PU
265
101
;PD
267
106
;SP0;

 

Программный модуль пакета ГРАФОР для записи файлов в формате HP-GL.

Создана подпрограмма пакета, осуществляющая запись результатов работы в файл, формат которого является подмножеством (используется минимум команд, достаточный для реализации всех графических элементов) языка HP-GL (Hewlett-Packard Graphic Language). Поскольку формат таких файлов чисто текстовый (plain ASCII), обеспечивается их передача на другие платформы и операционные системы.

 

Программный модуль отображения результатов работы ГРАФОРа в Windows.

Для повышения мобильности программ отображения (обычно эта часть программное обеспечения наиболее трудно переносится на другие платформы) их реализация в системах семейства Windows (95/98/ME/NT/2000) теперь осуществляется средствами OpenGL. В новой версии экранного драйвера она вызывается вместо подпрограммы, в которой отображение на экран осуществляется с помощью встроенных средств MS Fortran Power Station/Compaq Visual Fortran.

Относительно старой версии также добавились возможности: более простой передачи изображения в буфер обмена, масштабирования и подстраивания под размер окна.

 

Взаимодействие подпрограмм, написанных на разных алгоритмических языках.

Также реализована отдельная динамическая библиотека HglView.dll, которая содержит функции, позволяющие просматривать HP-GL-файлы формата, описанного выше. Универсальность модуля позволяет вызывать его функции как из приложений, написанных на Фортране, так и на Си. HglView.dll создает собственное меню, через которое можно управлять окнами просмотра HP-GL файлами. Меню содержит следующие команды:

File Close – Закрыть активное окно просмотра HP-GL файла
Edit Copy .hgl file to the Clipboard – Скопировать содержимое активного окна в буфер обмена
View Size To Fit – Изменить масштаб и размеры изображения в соответствии с реальными размерами HP-GL файла
Zoom In – Увеличить размер изображения
Zoom Out – Уменьшить размер изображения
Window Cascade – Расположить окна с перекрытием
Tile – Расположить окна без перекрытия
Close All – Закрыть все окна просмотра HP-GL файлов

 

СПИСОК ФУНКЦИЙ:
READHGLFILE – выбрать HP-GL файл через стандартный OPEN диалог и показать его.
VIEWHGLFILE – показать заданный HP-GL файл.
SIZETOFIT – изменить размеры окна HP-GL изображения в соответствии с его реальным размером.
ZOOM – осуществить масштабирование HP-GL изображения в сторону увеличения/уменьшения; масштабирование осуществляется по следующей шкале: ... 12,5% 25% 50% 100% 150% 200% 250% ...
ISHGLVIEW – определить, содержит ли заданное окно HP-GL изображение.

 

СОСТАВ ПАКЕТА:

1) HglView.dll – следует разместить либо в системной директории, либо в ту, где будет находиться приложение, обращающееся к данному модулю.

2) HglView.lib – библиотека, необходимая для linker'а.

3) HglViewExt.h – файл, содержащий объявление функций; необходим для нормальной работы модулей на СИ.

4) Cview файлы – Пример работы с библиотекой на СИ.

 

ВЫЗОВ ИЗ ФОРТРАНА:

Для работы в ФОРТРАНЕ требуется наличие Compaq Visual Fortran 6 или аналогичного продукта. Необходимо выполнить следующие шаги:

1) Создать проект QuickWin (multiple windows).

2) Добавить в созданный проект библиотеку HglView.lib .

3) Добавить в файл на ФОРТРАНЕ вызов нужной фукции.

 

ВЫЗОВ ИЗ СИ (СИ++):

Для работы в СИ требуется наличие MS Visual C++ 6.0. Необходимо выполнить следующие шаги:

1) Создать проект Win32 Application.

2) Добавить в созданный проект файл HglViewExt.h и библиотеку HglView.lib.

3) Создать обычное MDI приложение.

 

Пример Cview позволяет увидеть, что нужно добавить для работы с данной библиотекой.

Чтобы иметь возможность работать с меню, создаваемым для HP-GL окна надо в процедуру обработки сообщения (message) WM_COMMAND добавить следующие строки:

  LONG   lResult = 0;
  HWND   hChild;
  hChild =(HWND)SendMessage(hwndMDIClient,WM_MDIGETACTIVE,0,(LPARAM)NULL);
  if (ISHGLVIEW (hChild)) //если handle принадлежит HP-GL окну, то передать
  {                       // команду меню на обработку процедуре HP-GL окна
     lResult = SendMessage (hChild, WM_COMMAND, wParam, lParam);
     if (!lResult) return 0;
  }

 

ОПИСАНИЕ ФУНКЦИЙ:

__declspec(dllimport) extern BOOL __stdcall READHGLFILE(void);

С помощью этой функции пользователь может выбрать HP-GL файл для просмотра через стандартное диалоговое окно OPEN

Аргументы:      - нет.

Возращаемое значение:
      TRUE – в случае благополучного завершения,
      FALSE – в случае неудачи.

__declspec(dllimport) extern BOOL __stdcall VIEWHGLFILE(LPINT pnFile);

Данная функция открывает для просмотра предварительно заданный HP-GL файл

Аргументы:

pnFile
указатель на номер HP-GL файла. По умолчанию имя HP-GL файла - HPxx.HGL, где xx ::= 1|2|...|99.

Возращаемое значение:

      TRUE – в случае благополучного завершения,
      FALSE – в случае неудачи.

__declspec(dllimport) extern void __stdcall SIZETOFIT (void);

Функция восстанавливает реальные размеры и масштаб HP-GL изображения.

 

Аргументы:

нет.

Возращаемое значение:

нет .

 

 

 

__declspec (dllimport) extern void __stdcall ZOOM (int nDirection);

 

Функция осуществляет масштабирование HP-GL изображения; масштабирование осуществляется по следующей шкале: ... 12,5% 25% 50% 100% 150% 200% 250% ...

 

Аргументы: nDirection – направление 'зуммирования':

 

 

1

– в сторону увеличения

   

– 1

– в сторону уменьшения

Возращаемое значение:

нет.

 

 

 

 

__declspec (dllimport) extern BOOL __stdcall ISHGLVIEW (HWND hWnd);

 

Функция проверяет является ли данное окно окном HP-GL изображения.

 

Аргументы:

hWnd – идентификатор (handle) тестируемого окна

Возращаемое значение:

TRUE

– если указанный идентификатор принадлежит окну HP-GL изображения.

 

FALSE

– в противном случае.

           

 

 

Программа просмотра ( Viewer ).

 

Создана отдельная программа просмотра, которой можно просматривать файлы и после окончания работы с пакетом, поскольку по умолчанию они не уничтожаются и остаются на жестком диске в директории проекта. Пример работы приведен на рисунке.

 

 

            Здесь, помимо возможностей передачи изображения в буфер обмена, масштабирования, подстраивания под размер, имеются возможности посмотреть, какая директория является текущей, задать просмотр одного конкретного файла или всех файлов из текущей директории, а также узнать полное имя текущего файла.

ОПИСАНИЕ МЕНЮ

Меню содержит команды работы с окнами просмотра HP - GL файлов.

 

–>
File –>

Open – Выбрать HP - GL файл для просмотра через стандартное диалоговое окно Open

 

–>

Open All

– Открыть для просмотра все HP - GL файлы из текущей директории

 

–>

Export PostScript

– Сохранить текущее изображение в файле PS -формата

 

–>

Close

– Закрыть активное окно просмотра HP-GL файла

 

–>

Exit

– Закрыть все окна и завершить работу

Edit

–>

Copy .hgl file to the Clipboard

solid windowtext .75pt;solid windowtext .75pt;padding: 0cm 5.4pt 0cm 5.4pt">

– Скопировать содержимое активного окна в буфер обмена

View

margin-left:36.0pt;text-indent:-1mso-list: l0 level1 lfo9;tab-stops:36.0pt"> –font:7.0pt 'Times New Roman'">        полное имя,

margin-left:36.0pt;text-indent:-1mso-list: l0 level1 lfo9;tab-stops:36.0pt"> –font:7.0pt 'Times New Roman'">        размер файла,

margin-left:36.0pt;text-indent:-1mso-list: l0 level1 lfo9;tab-stops:36.0pt"> –font:7.0pt 'Times New Roman'">        дату создания и

        размер изображения в точках X на Y .

  Current Directory – Показать текущую директорию

Window –>

Cascade

– Расположить окна с перекрытием

 

–>

Tile

– Расположить окна без перекрытия

 

–>

Close All

– Закрыть все окна просмотра HP-GL файлов

Help

–>

About…

solid windowtext .75pt;solid windowtext .75pt;padding: 0cm 5.4pt 0cm 5.4pt"> – Показать диалоговое окно с информацией о программе

 

Работа пакета ГРАФОР в Linux

 

Пакет ГРАФОР перенесен на платформу Linux . При написании программы визуализации для пакета ГРАФОР использовалась графическая библиотека QT версии 2.3.1.

Исполнимый файл собирается из 3-х модулей :

1) Модуль, написанный на Fortran - callwin.f,  реализует функции, необходимые пользователю .

2) Модуль, написанный на C – sig .c,  нужен для связи с модулем main . cpp .

3) Модуль, написанный на C++ – main . cpp , реализует визуализацию результатов, полученных в callwin.f.

 

Взаимодействие модулей осуществляется так :

1. Запускается функция main () модуля main . cpp . Она назначает процедуру signal 1() обработчиком сигнала SIG _ USR 1, подготавливает среду для создания окон приложения и вызывает главную процедуру модуля callwin . f .

2. В модуле callwin . f после создания файла-результата для его отображения вызывается процедура dowin1() модуля sig . c ,

3. Процедура dowin 1() модуля sig . c изменяет глобальную переменную num _ win ( в данном случае в 1) и посылает приложению ( то есть себе) сигнал SIG_USR1.

4. Процедура signal 1() модуля main . cpp создает динамический объект класса MWidget , вызывая его конструктор и передавая num _ win в качестве параметра . Конструктор читает файл-результат, создает цепочку, каждый элемент которой – отрезок ( цвет+координаты) в окне и инициализирует окно . Функция прорисовки окна берет информацию из цепочки.

 

Об ' ект класса Mwidget c оздает окно, меню которого содержит пункты :

36.0pt"> ' Close ' -  закрывает окно,

36.0pt"> '1 x 1'   -  устанавливает масштаб 1х1,

36.0pt"> ' In '    -  увеличивает изображение в 2 раза,

36.0pt"> ' Out '   -  уменьшает изображение в 2 раза.

36.0pt">  

Если изображение не помещается в окне, то повляется соответствующая  (Х и/или У) полоса прокрутки. При закрытии последнего окна приложение заканчивает работу. Имена процедур в С/С++ не совпадают ( добавлен символ подчеркивания в конце ) c именами в Fortran’ е .

 

Пример окна :

 

Исходные тексты :

/********** 'Times New Roman';begin 'Times New Roman';callwin'Times New Roman'">.'Times New Roman'; f'Times New Roman'"> ***************/

yes; TIMES NEW ROMAN CYR" lang="         subroutine fortran

         integer argc

         character(*) argv

         call dowin1

         call dowin2

         end

/********** 'Times New Roman'; end 'Times New Roman'; callwin.f ***************/

/************* begin sig.c *************/

#include <signal.h>

char num_win;

void dowin1_()

{

  num_win=1; kill( getpid(), SIGUSR1);

}

void dowin2_()

{

  num_win=2; kill( getpid(), SIGUSR1);

}

/************* end sig.c **************/

// begin  main.cpp

#include <signal.h>

#include <stdlib.h>

#include <stdio.h>

#include <iostream.h>

#include <qapp.h>

#include <qmenubar.h>

#include <qpainter.h>

#include <qscrollbar.h>

#include <qstring.h>

#include <qfile.h>

#include <qtstream.h>

struct TChain { unsigned short int x; unsigned short int y;

                unsigned short int x2; unsigned short int y2;

2">              unsigned int cl:8;      struct TChain *next; };

class MWidget : public QWidget

{

    Q_OBJECT

public:

    MWidget( QWidget *parent=0, const char *name=0, uint number=0, uint w=0, uint h=0 );

    ~MWidget();

private slots:

    void     fzoomin(){ scale.x*=2; scale.y*=2; setMaxH();setMaxV(); repaint(FALSE); };

    void     fzoomout(){ scale.x/=2; scale.y/=2; setMaxH();setMaxV(); repaint(FALSE); };

    void     fzoom1x1(){ scale.x=scale.y=1.0; setMaxH();setMaxV(); repaint(FALSE); };

    void     setH( int val ){ shft.x=val; repaint(FALSE); }

    void     setV( int val ){ shft.y=val; repaint(FALSE); }

    void     setMaxH(){

2">                if(size.x*scale.x>hsb->width()){ 

2">                  hsb->setMaxValue( size.x*scale.x-hsb->width() ); hsb->show();

2">                }else{ hsb->hide(); shft.x=0; hsb->setValue(0);}

   2">          }

    void     setMaxV(){

2">                if(size.y*scale.y>vsb->height()){ 

2">                  vsb->setMaxValue( size.y*scale.y-vsb->height() ); vsb->show();

2">                }else{ vsb->hide(); shft.y=0; vsb->setValue(0); }

   2">          }

private:   

    void     paintEvent( QPaintEvent * );

    void     resizeEvent( QResizeEvent * );

 

    QMenuBar *menubar;

    QScrollBar       *hsb, *vsb;

    uint     h,w, i,j, n;

    struct TChain *tree, *lnk;

    struct Tscale { float x; float y; } scale;

    struct { uint x; uint y; } size, shft;

};

 

MWidget::MWidget( QWidget *parent, const char *name, uint number,uint w, uint h )

    : QWidget( parent, name)

{  char s[]='Window N     '; char fname[]='HP00.HGL'; bool ok;

   int *pint, a,b,c,d;  struct TChain e;

 

    scale.x=scale.y=1.0; shft.x=shft.y=0; setWFlags(WDestructiveClose);

 

    menubar = new QMenuBar( this, 'menu' );    CHECK_PTR( menubar );

    menubar->insertItem( '&Close', this, SLOT(close()) );

    menubar->insertItem( '1x1', this, SLOT(fzoom1x1()) );

    menubar->insertItem( 'In', this, SLOT(fzoomin()) );

    menubar->insertItem( 'Out', this, SLOT(fzoomout()) );

    hsb = new QScrollBar( 0,255,10,40,0,Horizontal, this, 0 );

    connect( hsb, SIGNAL(valueChanged(int)), SLOT(setH(int)) );

    vsb = new QScrollBar( 0,255,1,10,0,Vertical, this, 0 );

    connect( vsb, SIGNAL(valueChanged(int)), SLOT(setV(int)) );

 

    n=number; tree=NULL;

    fname[2]='0'+number/10; fname[3]='0'+number%10;

    QFile f(fname);

    if(f.open( IO_ReadOnly )){

       QTextStream t( &f );

       QString s;  s=t.readLine(); s=t.readLine(); s=t.readLine();

       if(s[0]=='S' && s[1]=='C'){

         if((sscanf((const char*)s,'SC %d, %d, %d, %d;',&a,&b,&c,&d))==4){

           size.x=b; size.y=d;

         }else  return;

       }else return;

 

       e.x=e.y=e.x2=e.y2=e.cl=0; e.next=NULL;

       while(!t.eof()){

         s=t.readLine();

//SP

         if(!strncmp(s,' ;SP',4)){

           s=t.readLine();

           e.cl=s.toUInt(&ok);  if(!ok) break;

//PU

           s=t.readLine();

           if(s.contains(';PU',FALSE)){

             s=t.readLine();

             e.x=s.toUInt(&ok); if(!ok) break;

            s=t.readLine();

             e.y=s.toUInt(&ok); if(!ok) break;

//PD

             s=t.readLine();

1">         }else{

1">           e.x=e.x2; e.y=e.y2;

1">         }

 

           if(!s.contains(';PD',FALSE)) break;

           s=t.readLine();

           e.x2=s.toUInt(&ok); if(!ok) break;

           s=t.readLine();

           e.y2=s.toUInt(&ok); if(!ok) break;

//LNK

           lnk=new TChain[1]; lnk->next=tree; tree=lnk;

           lnk->x=e.x;lnk->y=e.y; lnk->x2=e.x2;lnk->y2=e.y2; lnk->cl=e.cl;

         }//else           printf('s= %s\n',s2);

       }

       if(!t.eof()){//         printf('Error: s=%s\n',(const char*)s);

1">      cout << 'Error: s=' << s << '\n';

1">      f.close(); close(); return;

       }

       f.close();     

    }else{

      printf('Error: number= %d\n',number); close(); return;

    }

 

    setMinimumSize(150,100); resize( w, h );

    s[11]=number/10+'0'; s[12]=number%10+'0';   setCaption(s);

    show();

}

 

MWidget::~MWidget()

{// int n=0;

  lnk=tree;

  while(lnk){

    tree=lnk->next;

    free(lnk);// n++;

    lnk=tree;

  }// printf('freed %d\n',n);

}

void MWidget::resizeEvent( QResizeEvent * )

{  int mh=menubar->heightForWidth(width());

 

   hsb->setGeometry( 0, height()-15, width()-15, 15 );

   setMaxH();

   vsb->setGeometry( width()-15, mh, 15,height()-15-mh );

   setMaxV();

 

   repaint(FALSE);

}

void MWidget::paintEvent( QPaintEvent * )

{  QColor colors[]={ QColor(0,0,0), QColor(255,0,0), QColor(0,255,0), QColor(0,0,255), QColor(136,0,136), QColor(128,128,128) };

 

   QPainter paint;

   paint.begin( this ); paint.setBrush( 0xffffff );

   paint.drawRect(0,menubar->height(),width()-15,height()-menubar->height()-15);

 

   lnk=tree;

   while(lnk){

     paint.setPen( colors[lnk->cl]);

     paint.drawLine((int)(scale.x*lnk->x)-shft.x, (int)(scale.y*lnk->y)+menubar->height()-shft.y,

                    (int)(scale.x*lnk->x2)-shft.x, (int)(scale.y*lnk->y2)+menubar->height()-shft.y);

     lnk=lnk->next;

   }

 

   paint.setPen( 0xc0c0c0 ); paint.setBrush( 0xc0c0c0 );

   paint.drawRect( width()-15,height()-15,15,15 );

   if(!hsb->isVisible())

     paint.drawRect( 0,height()-15,width()-15,15 );

   if(!vsb->isVisible())

     paint.drawRect( width()-15,menubar->height(),15,height()-menubar->height()-15 );

    

   paint.end();

}

 

#include 'main.moc'

 

extern char num_win;

extern 'C' void fortran_(int argc, char **argv);

 

void signal1(int sig)

{  MWidget *m=new MWidget(0,0, num_win, 300, 200 ); }

 

int main( int argc, char **argv )

{

    signal( SIGUSR1, signal1 );

 

    QApplication a( argc, argv );

    QObject::connect( qApp, SIGNAL( lastWindowClosed() ), qApp, SLOT( quit() ) );

    fortran_( argc, argv);

 

    return a.exec();

}

 

//  end main.cpp

 

Формат Post Script

 

Наряду с файлами на языке HP - GL удобно получать файлы на языке PostScript , который является межплатформенным и также имеет текстовый формат. Кратко опишем особенности этого языка.

       Файл EPS состоит из двух частей – Prolog и Script , каждая из которых может включать в себя управляющие опреаторы EPS . Содержащиеся в теле файла управляющие операторы EPS начинаются, как и операторы языка PostScript , с символа процента – “%”. Таким образом, EPS -файлы могут обрабатываться как обычным транслятором с языка PostScript , который такие опреаторы будет игнорировать, так и более сложными программами обработки, которыми операторы EPS будут интерпретироваться. Признаком опратора EPS , позволяющего отличить его от комментария, служит второй символ оператора, следующий за “%”. Управляющие операторы начинаются символами “%!” или ”%%”, за которыми следует ASCII -строка. Первый оператор выглядит так ( W , X , Y , Z – конкретные номера версий и подверсий ) :

 

%!PS-Adobe-W.X EPSF-Y.Z

 

1;             При записи операторов языка PostScript