heh I hate frameworks so this is easy as pie for me...
this is what I coded for my students during my lectures few years back:
This is the main engine code (complete project + exe is in that zip above):
//---------------------------------------------------------------------------
//--- Constants: ------------------------------------------------------------
//---------------------------------------------------------------------------
const int _select_max_l=16;
const int _select_max_ll=_select_max_l*_select_max_l;
const int _half_size=10;
const int _atoms_max=32;
//---------------------------------------------------------------------------
enum _atom_type_enum //++++
{
_atom_type_non=0,
_atom_type_kruh,
_atom_type_stvorec,
_atom_type_enum_end
};
//---------------------------------------------------------------------------
enum _editor_edit_mode_enum //****
{
_editor_edit_mode_non=0,
_editor_edit_mode_move,
_editor_edit_mode_mov,
_editor_edit_mode_add_kruh,
_editor_edit_mode_add_stvorec,
_editor_edit_mode_del,
_editor_edit_mode_enum_end
};
//---------------------------------------------------------------------------
//--- viewer: ---------------------------------------------------------------
//---------------------------------------------------------------------------
class viewer
{
public: int x0,y0;
viewer() { x0=0; y0=0; }
void world2screen(int &sx,int &sy,int wx,int wy) { sx=wx-x0; sy=wy-y0; }
void screen2world(int &wx,int &wy,int sx,int sy) { wx=sx+x0; wy=sy+y0; }
void world2screen(int &sl,int wl) { sl=wl; }
void screen2world(int &wl,int sl) { wl=sl; }
};
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_kruh
{
public: int x,y,r; // world coordinates
TColor col0,col1,col2;
AnsiString str;
atom_kruh() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
void draw(TCanvas *scr,const viewer &view)
{
int xx,yy,rr;
view.world2screen(xx,yy,x,y);
view.world2screen(rr,r);
scr->Brush->Color=col0;
scr->Pen ->Color=col1;
scr->Font ->Color=col2;
scr->Ellipse(xx-rr,yy-rr,xx+rr,yy+rr);
scr->Brush->Style=bsClear;
xx-=scr->TextWidth(str)>>1;
yy-=scr->TextHeight(str)>>1;
scr->TextOutA(xx,yy,str);
scr->Brush->Style=bsSolid;
}
bool select(int &ll,int wx,int wy)
{
int qq,xx,yy;
xx=wx-x; xx*=xx;
yy=wy-y; yy*=yy;
qq=xx+yy;
if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
return false;
}
};
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_stvorec
{
public: int x,y,r; // world coordinates
TColor col0,col1,col2;
AnsiString str;
atom_stvorec() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
void draw(TCanvas *scr,const viewer &view)
{
int xx,yy,rr;
view.world2screen(xx,yy,x,y);
view.world2screen(rr,r);
scr->Brush->Color=col0;
scr->Pen ->Color=col1;
scr->Font ->Color=col2;
scr->Rectangle(xx-rr,yy-rr,xx+rr,yy+rr);
scr->Brush->Style=bsClear;
xx-=scr->TextWidth(str)>>1;
yy-=scr->TextHeight(str)>>1;
scr->TextOutA(xx,yy,str);
scr->Brush->Style=bsSolid;
}
bool select(int &ll,int wx,int wy)
{
int qq,xx,yy;
xx=wx-x; xx*=xx;
yy=wy-y; yy*=yy;
qq=xx+yy;
if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
return false;
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class editor
{
public: Graphics::TBitmap *bmp; // back buffer
int xs,ys;
int sel_ix,sel_tp; // actual mouse selected item
int edit_mode; // selected edit tool
viewer view; // view
bool redraw; // redraw needed?
bool locked; // edit in progress?
WORD key,key0;
int mx,my,mx0,my0;
TShiftState sh,sh0;
atom_kruh kruh[_atoms_max]; // all object lists
atom_stvorec stvorec[_atoms_max];
int kruhov;
int stvorcov;
editor();
~editor();
void resize(int _xs,int _ys); // interface with window
void draw();
void mouse(int x,int y,TShiftState s) { mx0=mx; my0=my; sh0=sh; mx=x; my=y; sh=s; edit(); }
void keys(WORD k,TShiftState s) { key0=key; sh0=sh; key=k; sh=s; edit(); }
void select(); // helper functions
void edit();
void move (bool q0,bool q1,int x,int y,int dx,int dy);
void mov (bool q0,bool q1,int x,int y,int dx,int dy);
void add_kruh (bool q0,bool q1,int x,int y,int dx,int dy);
void add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy);
void del (bool q0,bool q1,int x,int y,int dx,int dy);
};
//---------------------------------------------------------------------------
editor::editor()
{
bmp=new Graphics::TBitmap;
resize(1,1);
sel_ix=-1;
sel_tp=_atom_type_non;
edit_mode=_editor_edit_mode_non;
key=0; key0=0;
mx=0; mx0=0;
my=0; my0=0;
locked=false;
kruhov=0;
stvorcov=0;
}
//---------------------------------------------------------------------------
editor::~editor()
{
delete bmp;
}
//---------------------------------------------------------------------------
void editor::resize(int _xs,int _ys)
{
bmp->Width=_xs;
bmp->Height=_ys;
xs=bmp->Width;
ys=bmp->Height;
redraw=true;
}
//---------------------------------------------------------------------------
void editor::draw()
{
int i;
if (!redraw) return;
redraw=false;
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(Rect(0,0,xs,ys));
//++++
for (i=0;i<kruhov ;i++) kruh[i] .draw(bmp->Canvas,view);
for (i=0;i<stvorcov;i++) stvorec[i].draw(bmp->Canvas,view);
}
//---------------------------------------------------------------------------
void editor::select()
{
int i,wx,wy,ll;
int sel_tp0=sel_tp; sel_tp=_atom_type_non;
int sel_ix0=sel_ix; sel_ix=-1;
view.screen2world(wx,wy,mx,my);
//++++
ll=-1;
for (i=0;i<kruhov ;i++) if (kruh[i] .select(ll,wx,wy)) { sel_tp=_atom_type_kruh; sel_ix=i; };
for (i=0;i<stvorcov;i++) if (stvorec[i].select(ll,wx,wy)) { sel_tp=_atom_type_stvorec; sel_ix=i; };
if (sel_tp!=sel_tp0) redraw=true;
if (sel_ix!=sel_ix0) redraw=true;
}
//---------------------------------------------------------------------------
void editor::edit()
{
bool q0,q1;
int x,y,dx,dy;
x=mx; dx=mx-mx0;
y=my; dy=my-my0;
view.screen2world( x, y, x, y);
view.screen2world(dx,dx);
view.screen2world(dy,dy);
q0=sh0.Contains(ssLeft);
q1=sh .Contains(ssLeft);
if (!locked) select();
//****
if(edit_mode==_editor_edit_mode_mov) mov (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_add_kruh) add_kruh (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_add_stvorec)add_stvorec (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_del) del (q0,q1,x,y,dx,dy);
q0=sh0.Contains(ssRight);
q1=sh .Contains(ssRight);
if (!locked) move(q0,q1,x,y,dx,dy);
}
//---------------------------------------------------------------------------
void editor::move (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
if (q1)
{
view.x0-=dx;
view.y0-=dy;
redraw=true;
}
}
//---------------------------------------------------------------------------
void editor::mov (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&((sel_ix<0)||(sel_tp==_atom_type_non))) return;
locked=false;
if ((q1)||((q0)&&(!q1)))
{
//++++
if (sel_tp==_atom_type_kruh)
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
}
if (sel_tp==_atom_type_stvorec)
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
}
locked=true;
}
if (!q1) locked=false;
redraw=true;
}
//---------------------------------------------------------------------------
void editor::add_kruh (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
locked=false;
if (kruhov>=_atoms_max) return;
if ((!q0)&&( q1))
{
sel_tp=_atom_type_kruh;
sel_ix=kruhov;
kruhov++;
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
kruh[sel_ix].str=kruhov;
locked=true;
}
if (( q0)&&( q1))
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
locked=true;
}
if (( q0)&&(!q1))
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
}
if ((!q0)&&(!q1))
{
}
redraw=true;
}
//---------------------------------------------------------------------------
void editor::add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
locked=false;
if (stvorcov>=_atoms_max) return;
if ((!q0)&&( q1))
{
sel_tp=_atom_type_stvorec;
sel_ix=stvorcov;
stvorcov++;
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
stvorec[sel_ix].str=stvorcov;
locked=true;
}
if (( q0)&&( q1))
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
locked=true;
}
if (( q0)&&(!q1))
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
}
if ((!q0)&&(!q1))
{
}
redraw=true;
}
//---------------------------------------------------------------------------
void editor::del (bool q0,bool q1,int x,int y,int dx,int dy)
{
locked=false;
if ((sel_ix<0)||(sel_tp==_atom_type_non)) return;
if ((!q0)&&( q1))
{
//++++
if (sel_tp==_atom_type_kruh)
if (kruhov>0)
{
kruhov--;
kruh[sel_ix]=kruh[kruhov];
}
if (sel_tp==_atom_type_stvorec)
if (stvorcov>0)
{
stvorcov--;
stvorec[sel_ix]=stvorec[stvorcov];
}
sel_ix=-1;
sel_tp=_atom_type_non;
}
redraw=true;
}
//---------------------------------------------------------------------------
sorry for that its not entirely in English
kruh
means circle
stvorec
means square
This is code for window (BDS2006 VCL style)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "editor.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
editor edit;
int x0,y0;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void draw() // redraw app screen
{
edit.draw();
Form1->Canvas->Draw(x0,y0,edit.bmp);
// here just some info print outs
int dy=16,x=x0,y=y0-dy;
Form1->Canvas->Font->Color=clAqua;
Form1->Canvas->Brush->Style=bsClear;
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("locked: %i",edit.locked));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("Key: %d",edit.key));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_tp: %i",edit.sel_tp));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_ix: %i",edit.sel_ix));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("kruhov: %i",edit.kruhov));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("stvorcov: %i",edit.stvorcov));
Form1->Canvas->Brush->Style=bsSolid;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init app
{
// select tool on app start
bt_tool_kruhClick(this);
}
//--- window events: ---------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) { draw(); }
void __fastcall TForm1::FormResize(TObject *Sender) { x0=pan_top->Left; y0=pan_top->Height; edit.resize(ClientWidth-x0,ClientHeight-y0); draw(); }
void __fastcall TForm1::FormActivate(TObject *Sender) { draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { edit.keys(Key,Shift); draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){ edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { edit.mouse(X-x0,Y-y0,Shift); draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::bt_tool_kruhClick(TObject *Sender) // event on any tool button click
{
// select editor tool mode ...
edit.edit_mode=_editor_edit_mode_non;
if (bt_tool_kruh ->Down) edit.edit_mode=_editor_edit_mode_add_kruh;
if (bt_tool_stvorec->Down) edit.edit_mode=_editor_edit_mode_add_stvorec;
if (bt_tool_move ->Down) edit.edit_mode=_editor_edit_mode_mov;
if (bt_tool_delete ->Down) edit.edit_mode=_editor_edit_mode_del;
}
//---------------------------------------------------------------------------
Window has only 4
tool buttons (locked together by same guid so only one can be down at a time)
- add circle tool
- add square tool
- move tool
- delete tool
Everything is statically allocated for simplicity
[edit1] more info
create gfx object data type/class (atom_xxxx
)
it should hold the size,position,shape
of visual gfx representation of object. Add connection variables (object type
and object index
to what it should be connected). Add the real object/data inside
object ID
I am using int tp,ix;
tp
means type of object
ix
means index in list of object of type tp
editor engine
this should be also class or set of variables and functions. It should hold the whole edited world (lists of objects):
- visualization variables like screen/backbufer bitmap or rendering context, mouse position, selection list
- add functions/events like
onmouse, onkey, draw,
...
- add edit function it should be able to
select,add,del,move
(drag&drop) objects. Ideally controlled by set of command to ease up the undo/redo operation
- add
undo/redo
- add
save,load
- add your desired simulation functionality
and that is all if I did not forgot something.
create Application GUI interface
so create window, add panel with buttons for each tool,menu and whatever you need. Add events for mouse,keyboard,repaint,resize,drag&drop,... My example looks like this:
Add editor edit;
to it globally or as a member of it. Member option is better if you want to have MDI later. Add events interface between edit and window (the second source code).
[Notes]
++++
marks part of code where you need add changes if any atom type is added to the system
****
marks part of code where you need add changes if any edit mode is added to the system
Sorry for not add more commented code if you need clarify something comment me.
Hope it helps a little ...