jestem programistą "z doskoku". programowałem w wielu językach zaczynając od BASICa i assemblera na C64 a na ActionScript kończąc. zwykle uczyłem się języka do wykonania konkretnego zadania nie zagłębiając się zanadto w jego zawiłościach. obecnie przyswajam programowanie obiektowe w C++ i nie ukrywam, że dzięki Waszemu serwisowi udało mi się niemal osiągnąć zamierzony cel. jednak o ile samo napisanie działającej aplikacji nie jest szczególnie trudne, to jednak jej szybkość działania czy "piękno kodu" wymaga jednak czegoś więcej niż analiza kodu a'la Bill Gates ze stopki Barona. postanowiłem więc zapytać o kilka spraw licząc na dopracowanie programu. jednocześnie przepraszam za niefachowe określenia, które na pewno się zdarzą, gdyż nie jestem jeszcze zaznajomiony ze słownictwem.
poniżej zamieszczam kod okrojonej wersji programu, a w załącznikach exe'ka i źródło (BCB 6).
#include <vcl.h>
#include <vector>
#pragma hdrstop
using namespace std;
#include <math.h>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
vector<Clip> klip;
Clip tmp(100,4);
int akcja = -1;
int X0, S0;
int dX, dY, selected = 0;
//---------------------------------------------------------------------------
void upd_status ()
{
Form1->Edit1->Text = tmp.s;
Form1->Edit2->Text = tmp.t;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
DoubleBuffered = true;
Edit1->Parent = StatusBar1;
Edit2->Parent = StatusBar1;
BitBtn1->Parent = StatusBar1;
klip.push_back(tmp);
if (klip.size()) klip.pop_back();
upd_status();
}
//---------------------------------------------------------------------------
void rysuj ()
{
int Y=Form1->Image2->Picture->Bitmap->Height+dY;
Form1->Image2->Picture->Bitmap->PixelFormat = pf32bit;
Form1->Image2->Canvas->Brush->Color = clWhite;
Form1->Image2->Canvas->FillRect(Form1->Image2->BoundsRect);
Form1->Image2->Canvas->Brush->Style = bsSolid;
Form1->Image2->Canvas->Brush->Color = clYellow;
for (unsigned int i=0; i<klip.size(); i++)
{
Form1->Image2->Canvas->Rectangle(klip[i].s-dX, Y-klip[i].t*32, klip[i].s-dX+100, Y-(klip[i].t*32+31));
}
Form1->Image2->Canvas->Brush->Color = clAqua;
if (akcja>=0)
{
Form1->Image2->Canvas->Rectangle(tmp.s-dX, Y-tmp.t*32, tmp.s-dX+100, Y-(tmp.t*32+31));
}
}
//---------------------------------------------------------------------------
int ktory(int X, int Y)
{
int i=klip.size()-1;
while (!(klip[i].t==floor(Y/32) && klip[i].s<=X && klip[i].s+100>=X)&&(i>=0)) i--;
return i;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Image2MouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
Form1->BitBtn1->SetFocus();
if (akcja>=0) klip.push_back(tmp);
selected=ktory(X+dX,Image2->Picture->Bitmap->Height-Y+dY);
if (selected>=0)
{
tmp=klip[selected];
klip.erase(klip.begin() + selected);
X0=X;
S0=tmp.s;
akcja=4;
}
else akcja=-1;
rysuj();
upd_status();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Image2MouseMove(TObject *Sender, TShiftState Shift,
int X, int Y)
{
if (akcja == 4)
{
tmp.s = S0+(X-X0);
tmp.t=floor((Image2->Picture->Bitmap->Height-Y+dY)/32);
rysuj();
upd_status();
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Image2MouseUp(TObject *Sender, TMouseButton Button,
TShiftState Shift, int X, int Y)
{
if (akcja>-1) akcja = 0;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ScrollBar1Change(TObject *Sender)
{
dX=ScrollBar1->Position;
rysuj();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
Image2->Picture->Bitmap->Width = this->Width-8;
Image2->Picture->Bitmap->Height = this->Height-65;
Edit2->Width = this->Width-120;
rysuj();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1Change(TObject *Sender)
{
tmp.s=StrToInt(Edit1->Text);
rysuj();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit2Change(TObject *Sender)
{
tmp.t=StrToInt(Edit2->Text);
rysuj();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn1KeyDown(TObject *Sender, WORD &Key,
TShiftState Shift)
{
if (Key==VK_INSERT) klip.push_back(tmp);
rysuj();
upd_status;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
MessageBox(Handle,"Zapis danych do pliku.","BitBtn1Click",0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelDown(TObject *Sender,
TShiftState Shift, TPoint &MousePos, bool &Handled)
{
if (dY>0) dY-=16;
rysuj();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormMouseWheelUp(TObject *Sender,
TShiftState Shift, TPoint &MousePos, bool &Handled)
{
dY+=16;
rysuj();
}
//---------------------------------------------------------------------------
najpierw mała instrukcja obsługi. po uruchomieniu programu należy kliknąć na białe pole a następnie wcisnąć klawisz Insert. zostanie wstawiony klocek, który można przesuwać. każde wciśnięcie Insert wstawia kolejny klocek. klocek aktywny ma błękitny kolor i można zmienić jego pozycję klikając na Edity na pasku statusu. jeśli żaden nie jest aktywny to zmiana dotyczy parametrów klocka, który pojawi się po wciśnięciu Insert. kliknięcie ikonki z monitorkiem bądź Enter przy aktywnym trybie "klockowania" powoduje wyskoczenie okna (w pełnej wersji zapis danych).
w wersji okrojonej program wydaje się nie mieć sensu, jednak zawiera kluczowe dla moich pytań elementy pełnej wersji.
moje pytania:
program działa szybko, dopóki wielkość pola dla klocków jest nieduża. po powiększeniu lub zmaksymalizowaniu całość zauważalnie zwalnia. można to sprawdzić szybko ruszając klockiem i obserwując użycie procesora. dodatkowo współrzędne na StatusBar nie odświeżają się wtedy płynnie. początkowo myślałem, że chodzi o rysowanie klocków, ale okazało się, że wąskim gardłem jest DoubleBuffering. niestety bez niego, wiadomo, obraz migocze. próbowałem wykorzystać rysowanie po Canvas, jednak obraz znika po przesłonięciu, a odświeżanie jeszcze bardziej obciąża procesor. próbowałem wykorzystać Panel i inne, a nawet DirectDraw, jednak takie błądzenie po omacku nie na wiele się zdało. stąd moje pytanie - jak najlepiej zrobić, aby obciążenie procesora przy pracy na pełnym ekranie było jak najmniejsze?
następna sprawa - ponieważ Image nie posiada Focusa, wykorzystałem ukryty BitBtn (ikonka monitorka), na którego ustawiam Focus w momencie kliknięcia w Image. dzięki temu mogę obsłużyć OnKeyDown. jednak nie jest to, wydaje mi się, rozwiązanie eleganckie. wolałbym się obyć bez niego, a przynajmniej nauczyć się jak można zrobić (czy też zasymulować) obsługę klawiatury i Focus na obiekcie Image. próbowałem za pomocą PageControl, ale zaprowadziło mnie to na manowce, choć czegoś też przy okazji nauczyło. w związku z tym proszę o poradę.
tyle pytań "głównych", choć będę na pewno pytał o wiele rzeczy w miarę padania odpowiedzi. teraz jednak chciałbym jeszcze zadać kilka mniej istotnych pytań:
kiedy obszar roboczy jest dość duży to współrzędne klocka na StatusBar odświeżają się z opóźnieniem lub przez chwilę nie odświeżają się.
-czy można jakoś wymusić natychmiastowe odświeżanie Edita przy każdym ruchu? bo mimo, iż procedura upd_status wywoływana jest przy każdym ruchu to jest to opóźnienie...
-gdzie należy wpisać, żeby Focus ustawiał się na żądanym elemencie? w tym konkretnym przypadku na BitBlt1. próbowałem w OnCreate formy i innych miejscach ale dostaję błąd, że nie można ustawiać na disabled i na invisible window. wnioskuję, że trzeba to ustawiać już po utworzeniu elementu, ale jak i gdzie?
-czy można zrobić tak, by napisy na StatusBar'ze nie mrugały przy zmianie rozmiarów okna? są to Edity ze zmienionym kolorem tła, gdyż nie znalazłem innego sposobu. czy można to zrobić inaczej?
obecnie rysowanie klocków odbywa się w procedurze rysuj(), gdzie wykorzystuję metodę Rectangle przekazując współrzędne obiektu tmp bądź obiektów z vectora.
-czy jakieś korzyści dałoby umieszczenie rysowania pojedynczego klocka jako metody? nadmienię, że w pełnej wersji programu rysowanie jest bardziej skomplikowane (nakładanie klocków z przezroczystością 50%) i tu następne pytania:
-czy możliwe jest rysowanie obiektów z przezroczystością 50% za pomocą standardowych metod? w tej chwili wykorzystuję swoją funkcję mieszania kolorów.
-czy wykorzystanie Scanline jest konieczne, czy można to obejść? chodzi mi o taką sprawę, że mógłbym wykorzystać instrukcje MMX dla jednej linii i wyniki wstawić do kolejnych 29 linii używając pętli i pitch'a. jednak być może organizacja Bitmap jest inna niż standardowa i spowodowałoby to błędy? pytam, bo nie zdążyłem jeszcze tego sprawdzić.
chciałbym również przerobić nieco program, aby vector zawierał wskaźniki do obiektów, gdyż w pełnej wersji programu obiekty te zawierają nie tylko współrzędne, ale i Stringi, a ponieważ stale elementy są z vectora usuwane i dodawane przez insert, to przy dużej ilości klocków może to niepotrzebnie zajmować procesor. ponieważ wskaźniki nie są dla mnie jeszcze środowiskiem naturalnym chciałbym się dowiedzieć
-jak należy przerobić program, aby wykorzystać w vectorze wskaźniki zamiast obiektów? rozumiem, że usunięcie z vectora wskaźnika nie powoduje usunięcia obiektu z pamięci?
-czy istnieje jakiś elegancki sposób na uniknięcie błędu przy pierwszym sprawdzaniu wielkości pustego vectora niż użyte przeze mnie "klip.push_back(tmp); if (klip.size()) klip.pop_back();"?
to na razie tyle. mam nadzieję, że wyraziłem się dosyć zrozumiale, jednak w razie czego proszę o wskazanie właściwego słownictwa.
pozdrawiam




grafika 
