Рисунок 2. Система координат устройства и логическая система координат
Для обратного преобразования (из системы координат устройства в логическую систему координат), будут применяться такие же формулы, но с переставленными индексами win и view:
Иногда вам может понадобиться самим пересчитать координаты или размеры объекта из одной системы координат в другую. Вместо того, что бы самостоятельно использовать приведенные формулы, удобнее воспользоваться следующими функциями:
BOOL DPtoLP (hDC, lpPoints, nCount);
BOOL LPtoDP (hDC, lpPoints, nCount);
ФункцияDPtoLPпреобразуеткоординатыточек, заданныхмассивомlpPointsизnCountобъектовтипаPOINT, заданныевсистемекоординатустройствавлогическиекоординаты (DPtoLP — Device Point to Logical Point), тоестьиз «view»в «win», афункцияLPtoDP — выполняетобратноепреобразование.
Под координатами устройства может подразумеваться либо непосредственно система координат устройства, если контекст соответствует всему устройству, либо система координат, связанная с окном, если контекст соответствует внутренней или внешней области окна. Так, при выводе в окно, реальное положение какой–либо точки окна на экране может быть вычислено в два этапа — сначала с помощью функции LPtoDP надо пересчитать логические координаты в координаты, связанные с окном, а затем с помощью функции ClientToScreen пересчитать из координат окна в координаты экрана. При работе с устройством в целом, например при выводе на принтер, достаточно одной функции LPtoDP.
При использовании функций DPtoLP и LPtoDP возможно возникновение интересных ошибок. Представим себе, например, что вам надо нарисовать линию шириной 10 пиксель. Так как логическая система координат может не совпадать с координатами устройства, то линия шириной 10 логических единиц вовсе не обязательно будет шириной 10 пиксель. Само собой напрашивается примерно такой фрагмент программы для вычисления ширины линии:
POINTpt;
pt.x = 10; pt.y = 0;
DPtoLP (hdc, &pt, 1); // пересчитаем 10 пиксель (ед. устройства) в логические
// единицы. Далее считаем, что в компоненте pt.x записана нужная нам величина
Ошибка, которая присутствует в этом фрагмента, сразу и не видна. Более того, во многих случаях вы получите вполне приемлемый результат и даже не заподозрите об ошибке — до тех пор, пока у вашей логической системы координат не окажется смещенным начало отсчета по оси x от левой границы контекста. В этом случае вы получите ширину 10 пиксель, преобразованную в логические единицы плюс смещение начала отсчета:
Рисунок 3. Из–за смещения начала отсчета возможно возникновение ошибок.
Что бы избежать подобной ошибки лучше брать не одну точку, а вектор нужной длины:
POINT vector[ 2 ];
vector[0].x = 0; vector[0].y = 0;
vector[1].x = 10; vector[1].y = 0;
DPtoLP (hdc, &vector, 2);
vector[1].x -= vector[0].x;
// Далеесчитаем, чтовкомпоненте vector[1].x записананужнаянамвеличина
Выбор системы координат
Для описания используемой системы координат предназначено пять атрибутов контекста устройства. Четыре атрибута описывают смещение начала отсчета и масштабные коэффициенты. Пятый атрибут — собственно выбранная в настоящий момент система координат.
Название атрибута | Значение по умолчанию | Обозначение в формулах | |
Mapping mode | Система координат | MM_TEXT | |
Window origin | Начало отсчета в логических координатах | 0,0 | Xwin.org, Ywin.org |
Viewport origin | Начало отсчета в координатах устройства | 0,0 | Xview.org, Yview.org |
Window extents | Масштабные коэффициенты системы координат | 1,1 | Xwin.ext, Ywin.ext |
Viewport extents | Масштабные коэффициенты системы координат | 1,1 | Xview.ext, Yview.ext |
Стандартная система координат GDI, выбираемая в контекст устройства при его создании совпадает с системой координат самого устройства (или окна). Такая система координат получила название текстовой (MM_TEXT). Вы можете отказаться от этой системы координат и установить некоторую собственную систему, у которой ориентация осей или масштабные коэффициенты отличаются от стандартной. Очевидно, что чаще всего придется устанавливать какие–либо системы координат, базирующиеся на метрической или английской системах мер. Раз так, то Microsoft предоставляет несколько дополнительных систем координат, так что во многих случаях вы можете просто выбрать подходящую вам метрическую (MM_LOMETRIC, MM_HIMETRIC), английскую систему (MM_LOENGLISH, MM_HIENGLISH) или полиграфическую (MM_TWIPS), не заботясь о точном вычислении масштабных коэффициентов. Более того, используя какую–либо из вышеназванных систем вы вообще не можете изменять масштабные коэффициенты, хотя можете перемещать точку начала отсчета.
В тех же случаях, когда вы хотите самостоятельно настраивать масштабные коэффициенты, вы можете воспользоваться системой координат MM_ANISOTROPIC, в которой вы свободно можете менять все коэффициенты, либо MM_ISOTROPIC, в которой GDI позволит вам произвольно назначать масштабные коэффициенты, но при этом сам их скорректирует, так что масштаб по обеим осям окажется равным. То есть если вы нарисуете прямоугольник с равным логическим размером сторон, то на рисунке он будет выглядеть квадратом.
Название | Единица | Ориентация осей |
MM_TEXT | 1 пиксель | |
MM_LOMETRIC | 0.1 мм | |
MM_HIMETRIC | 0.01 мм | |
MM_LOENGLISH | 0.01" | |
MM_HIENGLISH | 0.001" | |
MM_TWIPS | 1/20 полиграфической точки = 1/1440" (предполагается, что полиграфическая точка = 1/72")[2] | |
MM_ISOTROPIC | x=y цена единицы определяется пользователем | |
MM_ANISOTROPIC | x!=y цена единицы определяется пользователем |
Обратите внимание, что при выборе любой системы координат начало отсчета размещается в верхнем левом углу контекста, даже если ось Y направлена вверх (!). При этом получается, что весь рисунок располагается в области отрицательных значений координаты Y. На практике это значит, что для большинства систем координат (кроме MM_TEXT и MM_TWIPS) вы как правило должны задать новое положение начала отсчета.
Внимание! На 16ти разрядных платформах координаты задаются целым 16ти разрядным числом со знаком, так что минимальное значение -32768, а максимальное +32767.
Для того, что бы определить или изменить текущую систему координат можно воспользоваться функциями GetMapMode, которая возвращает индекс используемой системы координат, или SetMapMode, которая устанавливает новую систему координат:
UINT GetMapMode (hDC);
UINT SetMapMode (hDC, nIndex);
Функции, изменяющие положение точки начала отсчета и масштабные коэффициенты, возвращают информацию о предыдущих или ныне действующих значениях атрибутов различным образом. Существует некоторый «старый» набор функций, изначально ориентированный на 16ти разрядную платформу. Эти функции возвращают обе компоненты атрибута (масштабные коэффициенты по осям X и Y), упакованные в двойное слово; младшее слово содержит компонент X, а старшее — компонент Y. Для получения этих компонент по отдельности можно воспользоваться макросами LOWORD (dw) и HIWORD (dw).
Так как в Win32 API координаты задаются целым числом, то есть 32х разрядным, то упаковать два компонента в одно двойное 32х разрядное слово стало невозможно. В связи с этим GDI предоставляет дополнительные функции, возвращающие необходимую информацию в структуре типа SIZE или POINT. По счастью, необходимые изменения были внесены в WindowsAPI еще во времена Windows 3.1, так что использование большинства функций, типичных для Win32 API возможно и в WindowsAPI.
typedef struct tagSIZE {int cx;int cy;} SIZE; | typedef struct tagPOINT {int x;int y;} POINT; |
При использовании любой стандартной системы координат вы можете самостоятельно установить положение начала отсчета логической системы координат, задав его либо в логических единицах (window origin), либо в единицах устройства (viewport origin) с помощью функций:
// Реализованы только в Windows API
DWORD GetWindowOrg (hDC); 0
DWORD GetViewportOrg (hDC); 0
DWORD SetWindowOrg (hDC, nX, nY); 0
DWORD SetViewportOrg (hDC, nX, nY); 0
// Реализованы в WindowsAPI (начиная с Windows 3.1) и в Win32 API
BOOL GetWindowOrgEx (hDC, lpPoint);
BOOL GetViewportOrgEx (hDC, lpPoint);
BOOL SetWindowOrgEx (hDC, nX, nY, lpPrevPoint);
BOOL SetViewportOrgEx (hDC, nX, nY, lpPrevPoint);
Для задания масштабных коэффициентов вы можете воспользоваться функциями
// Реализованы только в Windows API
DWORD GetWindowExt (hDC); 0
DWORD GetViewportExt (hDC); 0
DWORD SetWindowExt (hDC, nX, nY); 0
DWORD SetViewportExt (hDC, nX, nY); 0
DWORD ScaleWindowExt (hDC, xMul, xDiv, yMul, yDiv); 0
DWORD ScaleViewportExt (hDC, xMul, xDiv, yMul, yDiv); 0
// Реализованы в WindowsAPI (начиная с Windows 3.1) и в Win32 API
BOOL GetWindowExtEx (hDC, lpSize);
BOOL GetViewportExtEx (hDC, lpSize);
BOOL SetWindowExtEx (hDC, nX, nY, lpPrevSize);
BOOL SetViewportExtEx (hDC, nX, nY, lpPrevSize);
BOOL ScaleWindowExtEx (hDC, xMul, xDiv, yMul, yDiv, lpPrevSize);
BOOL ScaleViewportExtEx (hDC, xMul, xDiv, yMul, yDiv, lpPrevSize);