Неквадратный компонент
Если нарисвать много стрелок, то компоненты пересекаются и потом сложно редакировать все это. Хватаешься за один компонент, а тащется другой.. и т.п.
Возможно ли создать компонент с неквадратной(прямоугольной) рабочей областью?
можно...
R: HRgn;
P: Array[0..2] of TPoint;
begin
P[0] := Point(0,0); // определим точки для полигона и
P[1] := Point(200,0);
P[2] := Point(100,200);
R := CreatePolygonRgn(P, 3, WINDING); // зададим его
SetWindowRgn(Handle, R, True);
end;
а вообще прочитай в MSDN про функцию SetWindowRgn, насколько я помню, там не только полигоны можно использовать для обрезания.. ))
p.s. этот метод работает только для TWinControl и его наследников... вот... вобщем если хочешь сам отрисовывать канву компонента - пользуй TCustomControl - у него есть свойство Canvas и процедура Paint...
можно...
R: HRgn;
P: Array[0..2] of TPoint;
begin
P[0] := Point(0,0); // определим точки для полигона и
P[1] := Point(200,0);
P[2] := Point(100,200);
R := CreatePolygonRgn(P, 3, WINDING); // зададим его
SetWindowRgn(Handle, R, True);
end;
а вообще прочитай в MSDN про функцию SetWindowRgn, насколько я помню, там не только полигоны можно использовать для обрезания.. ))
p.s. этот метод работает только для TWinControl и его наследников... вот... вобщем если хочешь сам отрисовывать канву компонента - пользуй TCustomControl - у него есть свойство Canvas и процедура Paint...
Все это очень хорошо, но хотелось бы добавить, что как правило основной компонент который хотят обрезать - это TImage, чтоб по контуру картинки края были. Но TImage это наследник TControl, а не TWinControl и потому не имеет дискриптора окна, что не позволяет применить к нему SetWindowRgn. Проще всего решить эту проблему - взять DBTImage, выставить у него BorderStyle=bsNone и использовать как обычный TImage. DBTImage является наследником TWinControl и к нему можно применять SetWindowRgn.
Все это очень хорошо, но хотелось бы добавить, что как правило основной компонент который хотят обрезать - это TImage, чтоб по контуру картинки края были. Но TImage это наследник TControl, а не TWinControl и потому не имеет дискриптора окна, что не позволяет применить к нему SetWindowRgn. Проще всего решить эту проблему - взять DBTImage, выставить у него BorderStyle=bsNone и использовать как обычный TImage. DBTImage является наследником TWinControl и к нему можно применять SetWindowRgn.
А по-моему, использование регионов приведет к жутким тормозам - лучше все рисовать на одном имидже и разруливать все с помощью геометрии.
А по-моему, использование регионов приведет к жутким тормозам - лучше все рисовать на одном имидже и разруливать все с помощью геометрии.
Поясни, пжл., насчет геометрии ?
Поясни, пжл., насчет геометрии ?
Вот что-то вроде этого:
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
const
N = 10; {количество точек}
L = 2; {допустимый зазор}
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
TMyLine = record
BP, EP: TPoint;
end;
var
Form1: TForm1;
MyLines: array[0..N - 1] of TMyLine;
NML: Integer;
OldX, OldY: Integer;
OldBP, OldEP: TPoint;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
for i := 0 to N - 1 do
with MyLines do begin
with BP do begin
X := Random(ClientWidth);
Y := Random(ClientHeight);
end;
with EP do begin
X := Random(ClientWidth);
Y := Random(ClientHeight);
end;
end;
NML := -1;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
i: Integer;
begin
with Canvas do
for i := 0 to N - 1 do
with MyLines do begin
MoveTo(BP.X, BP.Y);
LineTo(EP.X, EP.Y);
end;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
i: Integer;
d: Extended;
begin
for i := 0 to N - 1 do
with MyLines do
if (((X > BP.X - L) and (X < EP.X + L)) or ((X > EP.X - L) and (X < BP.X + L))) and
(((Y > BP.Y - L) and (Y < EP.Y + L)) or ((Y > EP.Y - L) and (Y < BP.Y + L))) then begin
d := Abs(((BP.Y - EP.Y) * X + (EP.X - BP.X) * Y + (BP.X * EP.Y - EP.X * BP.Y)) /
Sqrt(Sqr(EP.X - BP.X) + Sqr(EP.Y - BP.Y)));
if d <= L then begin
NML := i;
Break;
end;
end;
if NML >= 0 then begin
OldX := X;
OldY := Y;
OldBP := MyLines[NML].BP;
OldEP := MyLines[NML].EP;
end;
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
NML := -1;
end;
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
var
dX, dY: Integer;
begin
if NML >= 0 then begin
dX := X - OldX;
dY := Y - OldY;
MyLines[NML].BP.X := OldBP.X + dX;
MyLines[NML].BP.Y := OldBP.Y + dY;
MyLines[NML].EP.X := OldEP.X + dX;
MyLines[NML].EP.Y := OldEP.Y + dY;
Repaint;
end;
end;
end.
На счет тормозов. Регионы сами по себе тормоза не создают и немогут создавать, т.к. прорисовка идет на низком уровне, а там как говорится все пиксели одиноковы и не важно кружком их поставили или квадритиком. Проблема тормозов в плотности визуальных компонентов на 1 кв. см. экрана. Чем больше пользователь кидает элементов на видимую часть формы, например форма - это поле схемы, а имейджи с картинками элементы схемы, тем сильнее будет тормозить.
У меня вопрос: ну с линиями в примере мне понятно, а если у меня сложные разноцветные геометрическая фигуры разной формы. Как с ними оперировать?
Просто на канве компонента это реализовать, как мне кажется, проще.
Я создал этот компонент на основе Tshape.
Просто я думаю, что на геометрии будет немного тормознее, чем на компонентах...
Большое спасибо за пример.
На счет тормозов. Регионы сами по себе тормоза не создают и немогут создавать, т.к. прорисовка идет на низком уровне, а там как говорится все пиксели одиноковы и не важно кружком их поставили или квадритиком. Проблема тормозов в плотности визуальных компонентов на 1 кв. см. экрана. Чем больше пользователь кидает элементов на видимую часть формы, например форма - это поле схемы, а имейджи с картинками элементы схемы, тем сильнее будет тормозить.
У меня вопрос: ну с линиями в примере мне понятно, а если у меня сложные разноцветные геометрическая фигуры разной формы. Как с ними оперировать?
Насчет тормозов предлагаю проверить.
Просто я пытался писать игрушку навроде Professional Miner (отличается от обычного минера тем, что кнопки на минном поле непрямоугольные), написал свой класс кнопок с использованием регионов - прорисовка всего поля происходила не мгновенно, а несколько секунд.
Насчет других фигур, так ведь я дал общий принцип: определить при нажатии, что курсор мыши на фигуре или хотя бы рядом с ее границей, зафиксировать координаты нажатия и двигать.
В книжке по DirectX читал еще про один прием:
заводите еще один имидж, на котором все фигуры рисуете на тех же местах, но разными цветами. В момент клика определяешь цвет пиксела на том имидже с координатами курсора.
Насчет тормозов предлагаю проверить.
...
Я тут пимерчик сделал для теста и скомпилил его на D6.0. В архиве 4-ре .exe. Смотреть нужно в следующем порядке:
- 60 c Регионами.exe
- 60 без Регионов.exe
- далее бе разницы
Почему так, что бы сами все поняли, что там сделано, без моих коментариев.
Вообщем регионы тормоза дают, но и число видимых компонентов тоже влияет.
ЗЫ: "1000 ..".exe грузиться около 6-7 с. Потому не нервничать.
потому на коментарии внимания НЕ ОБРАЩАЙТЕ:
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, DBCtrls, Menus, StdCtrls;
type
{Объявляем свой класс как дочерний от TImage}
TMyImage1=class(TDBImage) //используем DBImage т.к. он явялется потомком TWinControl а не TControl и ->
public //-> следовательно имеет дескриптор Handle для ипользования регионов !!!
Number:Integer;//Порядковый номер элемента
NameImageActive:String; //Название активной картинки в .res
NameImageDeactive:String; //Название неактивной картинки в .res
{и т.д. ...}
procedure MouseLeave(var msg: TMessage); message CM_MOUSELEAVE;//процедура перехватывает покидание элемента мышью
procedure MouseEnter(var msg: TMessage); message CM_MOUSEENTER;//процедура перехватывает переход мыши на элемент
end;
{*******************************************}
TForm1=class(TForm)
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
procedure MyImageClick(Sender: TObject);
procedure MyImageDblClick(Sender: TObject);
procedure MyImageMouseDown(Sender: TObject;Button: TMouseButton;Shift: TShiftState; X,Y: Integer);
procedure MyImageMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
procedure MyImageMouseUp(Sender: TObject;Button: TMouseButton;Shift: TShiftState; X,Y: Integer);
public
MasMyImage:array of TMyImage1;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
var {Для перемещения элементов}
POS_X,POS_Y:Integer;
FMyImageMove:Boolean;//флаг режима перемещения
procedure TMyImage1.MouseLeave(var msg: TMessage);
begin
//Деактивируем элемент
Picture.Bitmap.LoadFromResourceName(HInstance, NameImageDeactive);
end;
procedure TMyImage1.MouseEnter(var msg: TMessage);
begin
//Активируем элемент
Picture.Bitmap.LoadFromResourceName(HInstance, NameImageActive);
BringToFront;
end;
procedure TForm1.FormShow(Sender: TObject);
//Формирует регион по контуру картинки в битмэпе
function CreateRgnFromBitmap(RgnBitmap:TBitmap):HRGN;
var
TransColor:TColor;
i,j: Integer;
i_width,i_height:Integer;
i_left,i_right:Integer;
rectRgn: HRGN;
begin
Result:=0;
i_width:=rgnBitmap.Width;
i_height:=rgnBitmap.Height;
transColor := rgnBitmap.Canvas.Pixels[0, 0];
for i:=0 to i_height-1 do
begin
i_left:=-1;
for j:=0 to i_width-1 do
begin
if i_left<0 then
begin
if rgnBitmap.Canvas.Pixels[j,i]<>transColor then
i_left :=j;
end
else
if rgnBitmap.Canvas.Pixels[j,i]=transColor then
begin
i_right:=j;
rectRgn:=CreateRectRgn(i_left, i, i_right, i+1);
if Result=0 then
Result:=rectRgn
else
begin
CombineRgn(Result, Result, rectRgn, RGN_OR);
DeleteObject(rectRgn);
end;
i_left:=-1;
end;
end;
if i_left>=0 then
begin
rectRgn:=CreateRectRgn(i_left, i, i_width, i + 1);
if Result=0 then
Result:=rectRgn
else
begin
CombineRgn(Result,Result,rectRgn,RGN_OR);
DeleteObject(rectRgn);
end;
end;
end;
end;
var BitBuffer:TBitmap;
WindowRgn:HRGN; //переменная типа регион
i:SmallInt;
begin
SetLength(MasMyImage, 60); //инициализируем массив элементов: 60 шт.
for i:=1 to Length(MasMyImage) do {Для примера делаем 60 одинаковых элементов}
begin
{Создаем динамически элемент-1 типа TMyImage1 на форме-1}
//1. Предварительно: Формируем регион по контуру картинки, которая будет потом загружена в элемент-1
BitBuffer:=TBitmap.Create; //инициализируем битмап
BitBuffer.LoadFromResourceName(HInstance, 'Pic1_Active'); //загружаем из ресурса картинку "Pic1" в битмап
WindowRgn:=CreateRgnFromBitmap(BitBuffer); //Используем самодельную процедуру создания региона по контуру битмапа
//2. Создаем динамически элемент-1 на форме-1 и настраиваем его
MasMyImage[i-1]:=TMyImage1.Create(Form1);//создаем компенент типа TMyImage1 динамически на первой форме
with MasMyImage[i-1] do
begin
{ПАРАМЕТРЫ}
Visible:=false; //пока настраиваем пусть его никто не видит
Parent:=Form1; //указываем имя компонета-родителя
Name:='MyImage'+IntToStr(i); //задаем имя элементу
BorderStyle:=bsNone; //ОБЯЗАТЕЛЬНО - сброс стиля !!!
SetBounds(10*i-form1.HorzScrollBar.Position, {позиция верхнего левого угла по X с учетом прокрутки. Покрутку нужно учитывать при сохр. и загрузки данных из INI-файла}
10*i-form1.VertScrollBar.Position, {позиция верхнего левого угла по Y с учетом прокрутки. Покрутку нужно учитывать при сохр. и загрузки данных из INI-файла}
BitBuffer.Width, {задаем ширину элемента}
BitBuffer.Height); {задаем высоту элемента}
SetWindowRgn(MasMyImage[i-1].Handle,WindowRgn,true); //применяем регион к элементу как окну
Picture.Bitmap.LoadFromResourceName(HInstance, 'Pic1_Deactive');
TabStop:=false; //запрет табуляции
{СПЕЦИАЛЬНЫЕ ПАРАМЕТРЫ ЗАЯВЛЕННЫЕ НАМИ их нужно заполнять информацей из INI-файла}
Number:=i;
NameImageActive:='Pic1_Active';
NameImageDeactive:='Pic1_Deactive';
{СОБЫТИЯ}
OnClick:=form1.MyImageClick; //назначем свои процедуры, если конечно эти события обрабатываются
OnDblClick:=form1.MyImageDblClick;
OnMouseDown:=form1.MyImageMouseDown;
OnMouseMove:=form1.MyImageMouseMove;
OnMouseUp:=form1.MyImageMouseUp;
//и т.д. ...
{ОСТАЛЬНОЕ}
Hint:='Delph-еры всех стран объединяйтесь'+IntToStr(i); //заполням всплывающий ярлычок
ShowHint:=true;
//и т.д. ...
Visible:=true; //делаем элемент видимым
end;
end;
end;
procedure TForm1.MyImageClick(Sender: TObject);
begin
{...}
end;
procedure TForm1.MyImageDblClick(Sender: TObject);
begin
{...}
end;
procedure TForm1.MyImageMouseDown(Sender: TObject;Button: TMouseButton;Shift: TShiftState; X,Y: Integer);
begin
//Запоминаем коорд. мыши
POS_X:=X+form1.HorzScrollBar.Position;
POS_Y:=Y+form1.VertScrollBar.Position;
FMyImageMove:=true;//Актив. флаг перемешения
end;
procedure TForm1.MyImageMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);
begin
if FMyImageMove=true then
begin
//смещаем элемент
(Sender as TMyImage1).Left:=(Sender as TMyImage1).Left+X-(POS_X-form1.HorzScrollBar.Position);
(Sender as TMyImage1).Top:=(Sender as TMyImage1).Top+Y-(POS_Y-form1.VertScrollBar.Position);
end;
end;
procedure TForm1.MyImageMouseUp(Sender: TObject;Button: TMouseButton;Shift: TShiftState; X,Y: Integer);
begin
if FMyImageMove=true then
begin
//конец перемещениям элемента
FMyImageMove:=false;
POS_X:=0;
POS_Y:=0;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var i:SmallInt;
begin
//Пример удаления всех элементов
if MasMyImage<>nil then
for i:=1 to Length(MasMyImage) do
if MasMyImage[0]<>nil then begin
MasMyImage[0].Destroy;
MasMyImage[0]:=nil;
end;
end;
end.
Я тут пимерчик сделал для теста и скомпилил его на D6.0. В архиве 4-ре .exe. Смотреть нужно в следующем порядке:
- 60 c Регионами.exe
- 60 без Регионов.exe
- далее бе разницы
Почему так, что бы сами все поняли, что там сделано, без моих коментариев.
Вообщем регионы тормоза дают, но и число видимых компонентов тоже влияет.
ЗЫ: "1000 ..".exe грузиться около 6-7 с. Потому не нервничать.
Да, действительно, разницы не чувствуется.
В моей проге наверное тормоза были из-за количества компонентов...