Nah, om de boel maar een beetje kracht bij te zetten (en omdat ik toch geen oog dichtdoe).
Ik de boel nog eens getekend op de canvas van de form. Hoe hard ik ook kijk, ik kan er geen bloemkool van maken
Dus voor de zekerheid maar een snapshot van de form toegevoegd zoals het er bij mij uitziet (gaarne op originele formaat bekijken, anders zou daar misschien verwarring over kunnen ontstaan).
De gegevens die ik gebruikt heb komen van dit wiki artikel vandaan, daar stond een mooi plaatje met alle afmetingen (ik heb de ballen verstand van voetbal nl).
En dit is de magische brij aan spagetthi:
Code:
unit Unit1;
(*
5 augustus 2012
Het tekenen van een voetbalveld op een form-canvas.
Dit naar aanleiding van een vraag waarom de cirkels er zo blokkerig uitzien.
Deze unit is gebaseerd op een standaard TForm.
Geen enkele property is visueel aangepast en hebben dus hun default
waarden. Enige uitzondering hierop zijn standaard events, mar deze zijn
zichtbaar aanwezig in deze unit.
Properties die wel dienen te worden aangepast worden programmatisch aangepast
zodat zichtbaar is wat er nu precies met de properties can de verschillende
componenten gebeurt.
Het voetbalveld is niet geheel compleet. De volgende zaken ontbreken:
- de corner-hoek-geval-cirkels
- Alles wat verder vergeten is
*)
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs;
type
{ TForm1 }
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.lfm}
type
// Een definitie voor een Arc object, zie Lazarus Help -> TCanvas.arc();
TArc = record
Left, Top, Bottom, Right: integer;
sx, sy: integer;
ex, ey: integer;
end;
// De benodigdheden voor 1 helft van een voetbalveld
TVoetbalVeldHelft = record
StrafschopGebied : TRect; // coordinaten van het Strafschopgebied
DoelGebied : TRect; // coordinaten van het doelgebied
DoelCirkel : TArc; // coordinaten van de doelcirkel
StrafschopStip : TRect; // coordinaten van de strafschopstip
end;
// De benodigdheden voor 1 compleet voetbalveld
TVoetbalVeld = record
Veld : TRect; // coordinaten van het voetbalveld
LH : TVoetbalVeldHelft; // coordinaten van de linker speelhelft
RH : TVoetbalveldHelft; // coordinaten van de rechter speelhelft
Middenlijn : TRect; // coordinaten van de middellijn
MiddenCirkel : TRect; // coordinaten van de middencirkel
MiddenStip : TRect; // coordinaten van de middenstip
end;
const
{
De afmetingen van een voetbalveld geplaatst in constanten.
Afmetingen zijn gebaseerd op het wikipedia artikel genaamd Voetbalveld
waarbij een SVG plaatje staat met alle afmetingen.
De hier gekozen waarden is een willekeurige (niet op voetbalbondregels)
selectie van afmetingen welke het beste uitkwamen.
De afmetingen zijn gegeven in meters en worden door de routines geschaald
als het voetbalveld daadwerkelijk op het scherm getekend wordt.
}
Veld_Lengte = 100.00; // Lengte van een voetbalveld
Veld_Breedte = 50.00; // Breedte van een voetbalveld
Middencirkel = 9.15; // Radius van de middencirkel
StrafSchopGebied_Breedte = 40.30; // Breedte van het strafschopgebied
StrafSchopGebied_Lengte = 16.50; // Lengte van het strafschopgebied
DoelGebied_Breedte = StrafSchopGebied_Breedte - 11.00 * 2;
DoelGebied_Lengte = 5.50;
DoelCirkel = 9.15; // radius van de doelcirkel
StrafSchopAfstand = 11.00; // Afstand van de strafschopstip
VirtualBorderSize = 100;
var
// Men definiere een voetbalveld
EenVoetBalVeld: TVoetbalVeld;
{
Om het voetbalveld te tekenen wordt de volgende strategie toegepast:
- Het TForm mag niet kleiner worden dan een vooraf bepaalde hoogte
en breedte. Dit om rariteiten te voorkomen tijdens het tekenen.
- Voor het tekenen van het voetbalveld wordt er gebruik gemaakt van een
kleine virtuele border rondom het voetbalveld, zodat het getekende beter
overkomt.
- Op het moment dat het TForm vergroot wordt, wordt bepaald welke afmeting
van het TForm (lengte of breedte) bepalend is voor het geschaald kunnen
tekenen van het voetbalveld. Immers (als alles in verhouding moet worden
getekend) zal de (binnenste) breedte van het TForm 2x zo groot moeten
zijn als de (binnenste) hoogte van datzelfde formulier (en vice versa).
- Als bekend is welke afmeting van het TForm bepalend is dan wordt hierop
de schalingsfactor bepaald.
- Alle afmetingen/coordinaten kunnen daarna worden berekend op basis van de
(voorgedefineerde) constante afmetingen van een voetbalveld en de
schalingsfactor.
- Als basis voor het berekenen van de coordinaten wordt uitgegaan van een
enkel punt (genaamd middenMidden/MM) en dit punt zal dan ook in het
midden van het TForm gepositioneerd zijn.
- Als alle benodigde coordinaten zijn berekend kunnen we het veld
daadwerkelijk gaan tekenen.
}
{
De functie voor het bepalen van de schalingsfactor.
}
function BepaalSchaalFactor(TekenGebiedBreedte, TekenGebiedHoogte: integer): single;
begin
(*
- Omdat we de minimale constraints van het tekengebied hebben gezet
zal deze nooit kleiner zijn dan Breedte x Hoogte (= 100x50, zie formcreate).
De hier gekregen lengte en breedte waarden worden dus bepaald door de
aanroeper middels het vergroten/verkleinen van het TForm.
- De uiteindelijke (opgeschaalde) waarde van de lengte (in pixels) van het
voetbalveld wordt bepaald door de ClientWidth property van het TForm
- De uiteindelijke (opgeschaalde) waarde van de Breedte (in pixels) van het
voetbalveld wordt bepaald door de ClientHeight property van het TForm
- De schaalfactor is afhankelijk van ofwel de lengte danwel de breedte die
aam deze routine is meegegeven.
Het voetbalveld moet immers wel (in verhouding) op het TForm kunnen passen,
zodat het nodig is te bepalen welke afmeting verantwoordelijk kan worden
gehouden voor het zo groots mogelijk kunnen tekenen van het voetbalveld
(met inachtnemning van de opschaling).
- Om uit te maken of de lengte of de breedte verantwoordelijk is voor de
schalingsfactor passen we een eenvoudige strategie toe (in pseudocode):
Als Lengte < (Breedte * 2) dan schaalfactor afhankelijk van lengte
Of ook wel:
Als Breedte < (Lengte / 2) dan schaalfactor afhankelijk van breedte
Het aan den lezer zelf te achterhalen uit welke toverhoed deze magische
factor 2 ineens vandaan is getoverd ;-)
DUS:
*)
if TekenGebiedBreedte < (TekenGebiedHoogte * 2) then
begin
// Schaalfactor wordt bepaald door de Lengte
Result := TekenGebiedBreedte / Veld_Lengte;
end
else
begin
// Schaalfactor wordt bepaald door de breedte
Result := TekenGebiedHoogte / Veld_Breedte;
end;
end;
{
Het herschalen van alle coordinaten op basis van de nieuwe (meegegeven) dimensies
}
procedure HerSchaalVeldCoordinaten(var Veld: TVoetbalVeld; TekenGebiedBreedte, TekenGebiedHoogte: integer);
var
MM : TPoint; // Het MiddenMidden van het tekengebied
Schalingsfactor : Single; // Een factor om mee te kunnen schalen
begin
// Eerst dient de schalingsfactor bepaald te worden
// Opm: Er wordt een border van de dimensies afgetrokken
SchalingsFactor := BepaalSchaalFactor(TekenGebiedBreedte - VirtualBorderSize,
TekenGebiedHoogte - VirtualBorderSize);
// bepaal het MiddenMidden van het tekengebied
MM.x := TekenGebiedBreedte div 2;
MM.y := TekenGebiedHoogte div 2;
// Opm: Berekenen de belangrijkste coordinaten vanuit het middenpunt MM
{- het veld Zelf -}
Veld.Veld.Top := MM.y - round(Veld_Breedte / 2 * SchalingsFactor);
Veld.Veld.Left := MM.x - round(Veld_Lengte / 2 * SchalingsFactor);
Veld.Veld.Bottom := MM.y + round(Veld_Breedte / 2 * SchalingsFactor);
Veld.Veld.Right := MM.x + round(Veld_Lengte / 2 * SchalingsFactor);
{- de middenlijn -}
Veld.Middenlijn.Top := Veld.veld.Top;
Veld.Middenlijn.Left := MM.x;
Veld.Middenlijn.Bottom := Veld.Veld.Bottom;
Veld.Middenlijn.Right := MM.x;
{- de middencirkel -}
Veld.MiddenCirkel.Top := MM.y - Round(MiddenCirkel * SchalingsFactor);
Veld.MiddenCirkel.Left := MM.x - Round(MiddenCirkel * SchalingsFactor);
Veld.MiddenCirkel.Bottom := MM.y + Round(MiddenCirkel * SchalingsFactor);
Veld.MiddenCirkel.Right := MM.x + Round(MiddenCirkel * SchalingsFactor);
{- de middenstip -}
Veld.MiddenStip.Top := MM.y - 3;
Veld.MiddenStip.Left := MM.x - 3;
Veld.MiddenStip.Bottom := MM.y + 3;
Veld.MiddenStip.Right := MM.x + 3;
{- de strafschopgebieden -}
Veld.LH.StrafschopGebied.Top := MM.y - round(StrafschopGebied_Breedte / 2 * SchalingsFactor);
Veld.LH.StrafschopGebied.Left := Veld.Veld.Left;
Veld.LH.StrafschopGebied.Bottom := MM.y + round(StrafschopGebied_Breedte / 2 * SchalingsFactor);
Veld.LH.StrafschopGebied.Right := Veld.Veld.Left + round(StrafschopGebied_Lengte * SchalingsFactor);
Veld.RH.StrafschopGebied.Top := MM.y - round(StrafschopGebied_Breedte / 2 * SchalingsFactor);
Veld.RH.StrafschopGebied.Left := Veld.Veld.Right - round(StrafschopGebied_Lengte * SchalingsFactor);
Veld.RH.StrafschopGebied.Bottom := MM.y + round(StrafschopGebied_Breedte / 2 * SchalingsFactor);
Veld.RH.StrafschopGebied.Right := Veld.Veld.Right;
{- de strafschopstippen -}
Veld.LH.StrafschopStip.Top := MM.y - 3;
Veld.LH.StrafschopStip.Left := Veld.Veld.Left + Round(StrafSchopAfstand * SchalingsFactor) - 3;
Veld.LH.StrafschopStip.Bottom := MM.y + 3;
Veld.LH.StrafschopStip.Right := Veld.Veld.Left + Round(StrafSchopAfstand * SchalingsFactor) + 3;
Veld.RH.StrafschopStip.Top := MM.y - 3;
Veld.RH.StrafschopStip.Left := Veld.Veld.Right - Round(StrafSchopAfstand * SchalingsFactor) - 3;
Veld.RH.StrafschopStip.Bottom := MM.y + 3;
Veld.RH.StrafschopStip.Right := Veld.Veld.Right - Round(StrafSchopAfstand * SchalingsFactor) + 3;
{- de doelgebieden -}
Veld.LH.DoelGebied.Top := MM.y - round(DoelGebied_Breedte / 2 * SchalingsFactor);
Veld.LH.DoelGebied.Left := Veld.Veld.Left;
Veld.LH.DoelGebied.Bottom := MM.y + round(DoelGebied_Breedte / 2 * SchalingsFactor);
Veld.LH.DoelGebied.Right := Veld.Veld.Left + round(DoelGebied_Lengte * SchalingsFactor);
Veld.RH.DoelGebied.Top := MM.y - round(DoelGebied_Breedte / 2 * SchalingsFactor);
Veld.RH.DoelGebied.Left := Veld.Veld.Right - round(DoelGebied_Lengte * SchalingsFactor);
Veld.RH.DoelGebied.Bottom := MM.y + round(DoelGebied_Breedte / 2 * SchalingsFactor);
Veld.RH.DoelGebied.Right := Veld.Veld.Right;
{- de doelcirkels -}
Veld.LH.DoelCirkel.Top := MM.y - Round(DoelCirkel * SchalingsFactor);
Veld.LH.DoelCirkel.Left := Veld.Veld.Left + Round((StrafschopAfstand - Doelcirkel) * Schalingsfactor);
Veld.LH.DoelCirkel.Bottom := MM.y + Round(DoelCirkel * SchalingsFactor);
Veld.LH.DoelCirkel.Right := Veld.Veld.Left + Round((StrafschopAfstand + Doelcirkel) * Schalingsfactor);
// omdat cirkela nu net ffkes de andere kant opdraaien dienen het start en eindpunt
// verwisseld te worden op de linkerhelft
Veld.LH.DoelCirkel.ex := Veld.LH.StrafschopGebied.Right;
Veld.LH.DoelCirkel.ey := MM.y - round(7.312 * schalingsfactor); // 7.312 is magisch wiskundige toverwaarde
Veld.LH.DoelCirkel.sx := Veld.LH.StrafschopGebied.Right;
Veld.LH.DoelCirkel.sy := MM.y + round(7.312 * schalingsfactor); // 7.312 is magisch wiskundige toverwaarde
Veld.RH.DoelCirkel.Top := MM.y - Round(DoelCirkel * SchalingsFactor);
Veld.RH.DoelCirkel.Left := Veld.Veld.Right - Round((StrafschopAfstand - Doelcirkel) * Schalingsfactor);
Veld.RH.DoelCirkel.Bottom := MM.y + Round(DoelCirkel * SchalingsFactor);
Veld.RH.DoelCirkel.Right := Veld.Veld.Right - Round((StrafschopAfstand + Doelcirkel) * Schalingsfactor);
Veld.RH.DoelCirkel.sx := Veld.RH.StrafschopGebied.Left;
Veld.RH.DoelCirkel.sy := MM.y - round(7.312 * schalingsfactor); // 7.312 is magisch wiskundige toverwaarde
Veld.RH.DoelCirkel.ex := Veld.RH.StrafschopGebied.Left;
Veld.RH.DoelCirkel.ey := MM.y + round(7.312 * schalingsfactor); // 7.312 is magisch wiskundige toverwaarde
// Het lijken net echte vector graphics, niet ? ;-)
end;
{
Teken een voetbalveld op een canvas
}
procedure TekenVoetbalVeldOpCanvas(Veld: TVoetBalVeld; C: TCanvas);
begin
// c.LockCanvas;
// c.AntialiasingMode:= amOn;
// maak het tekenveld schoon met de kleur groen.
c.Brush.Color := clGreen;
c.Clear;
// Een willekeurige breedte van de tekenpen
c.pen.Width := 1;
// kleurinstelling van de pen
// TIP: Als je de 'witte'lijnen niet helemaal wit maakt maar een zachtere
// kleur groen dan steekt dat ook niet zo af en voorkomt dit opvallende
// blokkerigheid.
c.pen.color := $0088FF88;
// Hier gaat het daadwerkelijk tekenen van start
c.Rectangle(veld.Veld); // teken veld;
c.Ellipse(Veld.MiddenCirkel); // teken middencirkel
c.Line(Veld.Middenlijn); // teken middenlijn;
c.brush.color := $0088FF88;
c.Ellipse(Veld.MiddenStip); // teken middenstip;
c.Brush.Color := clGreen;
c.rectangle(Veld.LH.StrafschopGebied); // teken strafschopgebied op linkerhelft
c.rectangle(Veld.RH.StrafschopGebied); // teken strafschopgebied op rechterhelft
c.brush.color := $0088FF88;
c.Ellipse(Veld.LH.StrafschopStip); // teken strafschopstip op linkerhelft
c.Ellipse(Veld.RH.StrafschopStip); // teken strafschopstip op rechterhelft
c.Brush.Color := clGreen;
c.Rectangle(Veld.LH.DoelGebied); // teken doelgebied op linkerhelft
c.Rectangle(Veld.RH.DoelGebied); // teken doelgebied op rechterhelf
c.Arc
(
Veld.LH.DoelCirkel.Left,
Veld.LH.DoelCirkel.Top,
Veld.LH.DoelCirkel.Right,
Veld.LH.DoelCirkel.Bottom,
Veld.LH.DoelCirkel.sx,
Veld.LH.DoelCirkel.sy,
Veld.LH.DoelCirkel.ex,
Veld.LH.DoelCirkel.ey
); // teken doelcirkel op linkerhelft
c.Arc
(
Veld.RH.DoelCirkel.Left,
Veld.RH.DoelCirkel.Top,
Veld.RH.DoelCirkel.Right,
Veld.RH.DoelCirkel.Bottom,
Veld.RH.DoelCirkel.sx,
Veld.RH.DoelCirkel.sy,
Veld.RH.DoelCirkel.ex,
Veld.RH.DoelCirkel.ey
); // teken doelcirkel op rechterhelft
// c.UnlockCanvas;
end;
{ TForm1 }
procedure TForm1.FormCreate(Sender: TObject);
begin
// Zeker maken dat we niet op een al te klein werkgedeelte moeten gaan tekenen
Constraints.MinHeight := Round(Veld_Breedte + VirtualBorderSize + Self.BorderWidth);
Constraints.MinWidth := Round(Veld_Lengte + VirtualBorderSize + Self.BorderWidth);
end;
procedure TForm1.FormPaint(Sender: TObject);
begin
TekenVoetbalVeldOpCanvas(EenVoetbalVeld, Canvas);
end;
procedure TForm1.FormResize(Sender: TObject);
begin
HerSchaalVeldCoordinaten(EenVoetbalVeld, ClientWidth, ClientHeight);
end;
end.
Ziet iemand nu nog steeds bloemkolen ?
groetjes,
Bookmarks