unit KolPolyline; // // purpose: Polyline draw object // author: © 2004, Thaddy de Koning, parts © Dr Steve Evans // Remarks: based on sample code written by Dr Steve Evans (steve@lociuk.com) // I have translated it to KOL and created the component. // KOLPolyline is a unit designed to store, manipulate and draw polylines. // The TPolyLineObject type provides a KOL component derived from Tlist that is // a container within which TPolyline objects can be stored. // The TPolyline type (derived from Tlist) contains a complete description // of an individual polyline object and how to draw it. // (* ************* Example code: ************* program poly; //save as 'poly.dpr' and compile/run {$WARNINGS OFF}//shut up the compiler, it's KOL, not VCL code uses windows, messages, kol,KolPolyLine; type PForm1=^TForm1; TForm1=object(Tobj) Form:pControl; Btn1,btn2,btn3:Pcontrol; PolyStore:PPolyLineObject; public procedure Btn1Click(sender:pobj); procedure Btn2Click(sender:pobj); procedure Btn3Click(sender:pobj); end; procedure NewForm1( var Result: PForm1; AParent: PControl ); begin New(Result,Create); with Result^ do begin Applet :=NewApplet('poly'); Form:= NewForm(Applet,Applet.Caption).SetSize(600,400).centeronparent; Form.Add2AutoFree(Result); btn1:=NewButton(form,'Draw new polyline').autosize(true).placeright; btn2:=NewButton(form,'Select a polyline').autosize(true).placeright; btn3:=NewButton(form,'Delete selected polyline').autosize(true).PlaceRight; btn1.OnClick:=btn1Click; btn2.OnClick:=Btn2Click; btn3.Onclick:=btn3Click; PolyStore:=NewPolyLineObject(Form); end; end; procedure TForm1.Btn1Click(sender: pobj); begin PolyStore.DrawMode:=dmPolyline; end; procedure TForm1.Btn2Click(sender: pobj); begin PolySTore.DrawMode:=dmSelect; end; procedure TForm1.Btn3Click(sender: pobj); begin PolyStore.DeleteSelectedPolylines; end; var Form1:pForm1; begin NewForm1( Form1, Applet); Run(Applet); end. *) interface uses Windows,messages,Kol; type PGeoPt = ^TGeoPt; TGeoPt = object(TObj) public X,Y: Integer; Selected: Boolean; end; function NewGeoPt:PGeoPt; type TDrawingMode = (dmNone, dmPolyline, dmSelect); PPolyLine = ^TPolyLine; TPolyline = object(Tlist) private { Private declarations } FColor:Tcolor; FSelected:Boolean; function getItems(index: integer): PGeopt; procedure SetItems(index: integer; const Value: PGeopt); public procedure SetSelected(Value: Boolean); function AddPointToPolyline(pDPoint: PGeoPt): Integer; procedure DeleteNode(ListIndex: Integer); procedure Draw(aCanvas:PCanvas); procedure MovePolyline(dx,dy: Integer); procedure MoveSelectedPolyPoint(dx,dy: Integer); function GetPointSelRect(Index: Integer): TRect; property Selected : Boolean read FSelected write SetSelected; property Color:Tcolor read Fcolor Write Fcolor; property Items[index:integer]:PGeopt read getItems write SetItems; end; function NewPolyLine:PPolyLine; type PPolyLineObject = ^TPolyLineObject; TPolyLineObject = object(Tlist) private FOwner:PControl; FDrawMode: TDrawingMode; OX,OY: Integer; FDraggingPoint: Boolean; FPointCount: integer; TempPolyline:Array of TPoint; function GetItems(index: integer): PPolyLine; procedure setitems(index: integer; const Value: PPolyLine); procedure SetDraggingPoint(const Value: Boolean); procedure SetDrawMode(const Value: TDrawingMode); procedure SetPointCount(const Value: integer); protected procedure PolyPaint(sender:Pcontrol;DC:HDC); procedure Draw(aCanvas:Pcanvas); public function CheckSelectPoly(x,y: Integer): Integer; function IsPointOnLine(XA,YA,XB,YB,XC,YC, Tolerance: Double): Boolean; procedure MoveSelectedPolyline(dx,dy: Integer); procedure MoveSelectedPolyPoint(dx,dy: Integer); function CursorOverPolyPoint(x,y: Integer): Boolean; procedure DeleteSelectedPolylines; function AddPolyline(PolyPtList: array of TPoint):Integer; overload; procedure EndAPolyLine; procedure DeleteMember(ListIndex: Integer); property Items[index:integer]:PPolyline read GetItems write setitems; property DrawMode:TDrawingMode read FDrawMode write SetDrawMode; end; function NewPolyLineObject(aOwner:Pcontrol):PPolyLineObject; implementation function NewGeoPt:PGeoPt; begin New(Result,Create); end; function NewPolyLine:PPolyLine; begin New(Result,Create); Result.FColor:=clNone; Result.FSelected:=False; end; function WndProcPolyLines( Sender: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean; var dx,dy: Integer; Mouse:TPoint; begin Mouse.X:=0; Mouse.Y:=0; Result:=False; with sender^, PPolyLineObject(sender.CustomObj)^ do begin case Msg.message of WM_MOUSEMOVE: if Msg.wParam AND MK_LBUTTON = MK_LBUTTON then begin Mouse.X:=LoWord(Msg.Lparam); Mouse.Y:=HiWord(Msg.Lparam); dx:=OX-Mouse.X; dy:=OY-Mouse.Y; if FDraggingPoint then MoveSelectedPolyPoint(dx,dy) else MoveSelectedPolyline(dx,dy); Invalidate; OX:=Mouse.X; OY:=Mouse.Y; end; WM_LBUTTONDOWN, WM_RBUTTONDOWN: begin Mouse.X:=LoWord(Msg.Lparam); Mouse.Y:=HiWord(Msg.Lparam); if DrawMode=dmSelect then begin CheckSelectPoly(Mouse.X,Mouse.Y); if CursorOverPolyPoint(Mouse.x,Mouse.y) then FDraggingPoint:=True; Invalidate; end; if DrawMode=dmPolyline then begin if (msg.Wparam AND MK_LBUTTON = MK_LBUTTON) then begin FPointCount:=FPointCount+1; SetLength(TempPolyline,FPointCount); TempPolyline[FPointCount-1].X:=Mouse.X; TempPolyline[FPointCount-1].Y:=Mouse.Y; Invalidate; end; if (Msg.Wparam AND MK_RBUTTON = MK_RBUTTON) then EndAPolyline; end; OX:=Mouse.X; OY:=Mouse.Y; end; WM_LBUTTONUP, WM_RBUTTONUP: FDraggingPoint:=False; end; end; end; function NewPolyLineObject(aOwner:PControl):PPolyLineObject; begin New(Result,Create); with Result^ do begin Fowner:=aOwner; aOwner.CustomObj:=Result; aOwner.AttachProc(WndProcPolyLines); aOwner.OnPaint:=PolyPaint; DrawMode:=dmNone; end; end; function TPolyline.GetPointSelRect(Index: Integer): TRect; begin Result.Left:=Items[Index].X-4; Result.Top:=Items[Index].Y-4; Result.Right:=Result.Left+8; Result.Bottom:=Result.Top+8; end; procedure TPolyline.MovePolyline(dx,dy: Integer); var i: Integer; begin for i:=0 to Count-1 do begin Items[i].X:=Items[i].X-dx; Items[i].Y:=Items[i].Y-dy; end; end; procedure TPolyline.MoveSelectedPolyPoint(dx,dy: Integer); var i: Integer; begin for i:=0 to Count-1 do begin if Items[i].Selected then begin Items[i].X:=Items[i].X-dx; Items[i].Y:=Items[i].Y-dy; end; end; end; procedure TPolyline.SetSelected(Value: Boolean); var i: Integer; begin FSelected:=Value; end; function TPolyline.AddPointToPolyline(pDPoint: PGeoPt):Integer; begin Add(pDPoint); Result:=Count-1; end; procedure TPolyline.DeleteNode(ListIndex: Integer); begin Delete(ListIndex); end; procedure TPolyline.Draw(aCanvas:PCanvas); var i: Integer; begin if (Selected) then aCanvas.Pen.Color:= clRed else aCanvas.Pen.Color:= clBlack; aCanvas.MoveTo(Items[0].X,Items[0].Y); for i:=1 to Count-1 do aCanvas.LineTo(Items[i].X,Items[i].Y); for i:=0 to Count-1 do if Selected then aCanvas.Rectangle(Items[i].X-4,Items[i].Y-4, Items[i].X+4,Items[i].Y+4); aCanvas.Pen.Color:= clBlack; end; function TPolyLineObject.AddPolyline(PolyPtList: array of TPoint):Integer; var i: Integer; aNewPolyline: PPolyline; aNewPoint: PGeoPt; begin aNewPolyline:=NewPolyline; for i:=0 to High(PolyPtList) do begin aNewPoint:=NewGeoPt; aNewPoint.X:=PolyPtList[i].X; aNewPoint.Y:=PolyPtList[i].Y; aNewPolyline.AddPointToPolyline(aNewPoint); end; Add(aNewPolyline); Result:=Count-1; end; procedure TPolyLineObject.DeleteMember(ListIndex: Integer); begin Delete(ListIndex); end; procedure TPolyLineObject.DeleteSelectedPolylines; var i: Integer; begin for i := Count-1 downto 0 do if Items[i].Selected then Delete(i); FOwner.Invalidate; end; procedure TPolyLineObject.Draw(aCanvas: PCanvas); var i: Integer; begin for i := 0 to Count-1 do Items[i].Draw(aCanvas); end; function abs(aValue:Double):double; begin result:=aValue; if result < 0 then result:=result* -1; end; function TPolyLineObject.IsPointOnLine(XA,YA,XB,YB,XC,YC, Tolerance: Double): Boolean; var L,R,S: Double; begin Result:=False; L:=SQRT(((XB-XA)*(XB-XA)+(YB-YA)*(YB-YA))); if l<>0 then begin R:= ((YA-YC)*(YA-YB)-(XA-XC)*(XB-XA))/(L*L); S:= ((YA-YC)*(XB-XA)-(XA-XC)*(YB-YA))/(L*L); if (r>0) and (r<1) then if Abs(S*L)<=Tolerance then Result:=True; end; end; function TPolyLineObject.CheckSelectPoly(x,y: Integer): Integer; var i,j: Integer; aRect: TRect; aPoint: TPoint; PolyPointIndex: Integer; begin Result:=-1; for i:=0 to Count-1 do Items[i].Selected:=False; for i:=0 to Count-1 do begin for j:=0 to PPolyline(Items[i]).Count-2 do begin if IsPointOnLine(Items[i].Items[j].X, Items[i].Items[j].Y, Items[i].Items[j+1].X, Items[i].Items[j+1].Y, X,Y, 4) then Result:=i; end; end; if (Result<>-1) then Items[Result].Selected:=True; end; function TPolyLineObject.CursorOverPolyPoint(x,y: Integer): Boolean; var i,j: Integer; aRect: TRect; aPoint: TPoint; begin aPoint.X:=x; aPoint.Y:=y; Result:=False; for i:=0 to Count-1 do for j:=0 to Items[i].Count-1 do Items[i].Items[j].Selected:=False; for i:=0 to Count-1 do begin for j:=0 to Items[i].Count-1 do begin arect:=Items[i].GetPointSelRect(j); if PtInRect(arect,aPoint) then begin Items[i].Selected:=True; Items[i].Items[j].Selected:=True; Result:=True; end; end; end; end; procedure TPolyLineObject.MoveSelectedPolyline(dx,dy: Integer); var i: Integer; begin for i := 0 to Count-1 do if Items[i].Selected then Items[i].MovePolyline(dx,dy); end; procedure TPolyLineObject.MoveSelectedPolyPoint(dx,dy: Integer); var i: Integer; begin for i := 0 to Count-1 do Items[i].MoveSelectedPolyPoint(dx,dy); end; function TPolyLine.getItems(index: integer): PGeoPt; begin result:=inherited items[index]; end; procedure TPolyLine.SetItems(index: integer; const Value: PGeopt); begin inherited items[index]:=value; end; function TPolyLineObject.GetItems(index: integer): PPolyline; begin result:= inherited items[index]; end; procedure TPolyLineObject.setitems(index: integer; const Value: PPolyLine); begin inherited items[index]:=value; end; procedure TPolyLineObject.EndAPolyLine; begin if FPointCount>1 then AddPolyline(TempPolyLine); FPointCount:=0; SetLength(TempPolyline,0); FOwner.Invalidate; end; procedure TPolyLineObject.PolyPaint(sender: Pcontrol; DC: HDC); var i: Integer; begin Draw(sender.Canvas); if FPointCount>0 then begin sender.Canvas.MoveTo(TempPolyline[0].X,TempPolyline[0].Y); for i:=1 to High(TempPolyline) do sender.Canvas.LineTo(TempPolyline[i].X,TempPolyline[i].Y) end; end; procedure TPolyLineObject.SetDraggingPoint(const Value: Boolean); begin FDraggingPoint := Value; end; procedure TPolyLineObject.SetDrawMode(const Value: TDrawingMode); begin EndAPolyLine; FDrawMode := Value; if Fdrawmode = dmPolyLine then Fowner.Cursor:=LoadCursor(0,IDC_CROSS) else FOwner.Cursor:=LoadCursor(0, IDC_ARROW); end; procedure TPolyLineObject.SetPointCount(const Value: integer); begin FPointCount := Value; end; end.