Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Drehung eines Körpers um eine beliebige Achse (https://www.delphipraxis.net/133559-drehung-eines-koerpers-um-eine-beliebige-achse.html)

Ben19 4. Mai 2009 23:05


Drehung eines Körpers um eine beliebige Achse
 
Hallo Leute :)
Ich muss für die Schule ein Projekt ausarbeiten und habe mich für ein Programm in Delphi entschieden in dem ich ein Objekt (Quadrat) um eine beliebige Achse drehen lasse. Habe mich auch schon schlau gemacht. Problem ist das die gefunden Formeln mir nicht schlüssig sind. Außerdem weiß ich nicht, da ich ein Anfänger im programieren bin, wie ich das in Delphi umsetze(vorallem die Darstellung von Matrizen ist mir nicht klar). Meine Fragen sind also, ob ihr mir die Drehformel erklären könntet, da ich das Projekt auch vorstellen muss und genau wissen muss wie die Formel zustande kommt und wie ich diese in Delphi umsetze(also vorallem wie ich Matrizen in Delphi einbaue)?

Hier eine Seite mit der Formel: www14.informatik.tu-muenchen.de/lehre/1998WS/proseminar/dlugosch/
Eine andere Möglichkeit der Drehung ist das Objekt in den Ursprung zu transformieren, dort zu drehen und zurück transformieren. Bei der Variante versteh ich allerdings die Rechnung nicht :?

Hoffe ihr könnt mir irgendwie helfen und schon mal vielen Dank im vorraus
Ben19

jfheins 4. Mai 2009 23:19

Re: Drehung eines Körpers um eine beliebige Achse
 
Icvh nehme jetzt mal an, du möchtest das Teil um mehrere Achsten (=3) drehen können und nicht nur um eine.

Dann befindest du dich im 3D-Raum.

Dann machst du folgendes:

1. Das Quadrat vektorbasiert speichern. Also einen Mittelpunkt und von diesem aus Vektoren zu den Eckpunkten. Diese Vektoren sind dreidimensional.

2. Eine Vektor-Matrix-Multiplikations-Funktion schreiben. Eine Matrix hat hier 3x3 also 9 Elemente.

3. 3 Funktionen (eine für jede Achse) schreiben, die dir (unter Angabe eines Drehwinkels) die Matrix zurückgeben.

4. Das ganze verpacken :)

Beachte, dass die Reihenfolge der Drehmatrizen nicht egal ist. Es macht einen Unterschied, ob du erst um X, dann um y und zum Schluss um z drehst, oder andersherum ;)

Vektorn und Matrizen kannst du in Delphi als recors implementieren ;)

R2009 5. Mai 2009 05:10

Re: Drehung eines Körpers um eine beliebige Achse
 
Hi,

ein Vektor ist nichts anderes als ein eindimsionales Array:

Delphi-Quellcode:
   vektor:array[1..n] of double;
wobei n die Dimension deines Vektors ist. ( in deinem Fall 3 )

eine Matrix ist nichts anderes als ein zweidimsionales Array (oder n dimensional brauchst du aber hier nicht):

Delphi-Quellcode:
   matrix:array[1..n,1..m] of double;
wobei n=m eine symetrische Matrix ist (in deinem Fall n=m=3)

Viele Grüsse

Ben19 5. Mai 2009 14:28

Re: Drehung eines Körpers um eine beliebige Achse
 
Erst einmal viel Dank für eure Antworten :-D
Das mit den einbauen von Matrizen in Delphi hab ich begriffen(nochmal vielen Dank :)).
Aber den Weg mit der Drehformel habe ich überhaupt nicht verstanden :? . Was bedeutet Vektorbasiert speichern oder eine Vektor-Matrix-Multiplikations-Funktion schreiben?
Wäre echt nett wenn du mir das nochmal erklären könntest.

Vielen Dank im voraus
Ben19

jfheins 5. Mai 2009 14:57

Re: Drehung eines Körpers um eine beliebige Achse
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab dir da mal ein Beispiel vorgerechnet ...

Um das ganze Quadrat zu verschieben, musst du nach der Drehung einfach einen Vektor hinzuaddieren!

P.S. Falls dir ein Schritt nicht klar ist - frag :)

guidok 5. Mai 2009 15:10

Re: Drehung eines Körpers um eine beliebige Achse
 
Ein Quadrat ist doch nur zweidimensional, oder?

Ein möglicher Körper, der sich im dreidimensionalen Raum aus dem Quadrat ergibt ist z.B. der Quader (wobei hier natürlich der Sonderfall mit der Höhe Null möglich ist).

3_of_8 5. Mai 2009 15:13

Re: Drehung eines Körpers um eine beliebige Achse
 
Es müssen gar nicht drei Rotationsmatrizen sein - eine reicht völlig.

@guidok: Was hat ein Quader mit einem Quadrat zu tun? Ein Quadrat ist zwar kein Körper, aber man kann es trotzdem im dreidimensionalen Raum betrachten.

Alles, was hier zu machen ist, ist, eine entsprechende Rotationsmatrix zu erzeugen (Formel in der Wikipedia) und die mit den Punktvektoren des Quadrats zu multiplizieren.

jfheins 5. Mai 2009 15:18

Re: Drehung eines Körpers um eine beliebige Achse
 
Ja - ein Quadrat ist 2D - aber man kann es ja trotzdem im 3D-Raum einbetten. (Die 4 Eckpunkte müssen natürlich in einer Eebene liegen, sonst ists ja kein Quadrat mehr)

@roter kasten: Ja klar - aber die wird dann schon etwas komplizierter ... und für jemanden der noch nicht damit gearbeitet hat isses vll. einfacher 3 seperate Drehungen zu machen ...

Ben19 5. Mai 2009 16:13

Re: Drehung eines Körpers um eine beliebige Achse
 
Also vielen Dank :-D
Dein Beispiel hat mir sehr geholfen. Habe es jetzt soweit verstanden :-D.
Hätte aber noch eine Frage zum besseren Verständnis, um einen 3D Würfel drehen zulassen muss ich doch die 4 hinteren Punkte v5, v6, v7, v8 auch mit der Drehmatrite multiplizieren oder ?

Vielen Dank im vorraus
Ben19

jfheins 5. Mai 2009 16:16

Re: Drehung eines Körpers um eine beliebige Achse
 
Ja, du musst immer alle Punkte mit der Drehmatrix multiplizieren.

Beachte jedoch, dass immer um den Ursprung gedreht wird - d.h. wenn du die Punkte von dem Quadrat nimmst und noch welche hinzufügst, wird der Würfel nicht um seine Mitte gedreht !

Ben19 5. Mai 2009 16:37

Re: Drehung eines Körpers um eine beliebige Achse
 
Ja ok :)
Nochmals vielen Dank für die schnellen Antworten.
Ich werde das alles einmal ausprobieren und falls dann noch etwas unklar ist bzw Fehler auftreten,
mich hier im Forum melden. Für eine Antwort wäre ich dir sehr dankbar.
Vielen Dank Ben19

jfheins 5. Mai 2009 19:36

Re: Drehung eines Körpers um eine beliebige Achse
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab mir doch noch ein paar Gedanken gemacht und kurzerhand mal was programmiert :mrgreen:

Man kann den Würfel um die verschiedenen Achsten drehen, mittels der Trackbars unten.

Man kann die Projektion auswählen

Man kann dem lustigen Farbenspiel zusehen, weil ich nicht die Muße hatte, die Flächen einzeln zu färben.

Hier ist der Code:
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Test_1
{
    public partial class Form1 : Form
    {
        PointF offset;

        Vector3D[] Punkte;

        Matrix3x3 RotX;
        Matrix3x3 RotY;
        Matrix3x3 RotZ;

        Pen pen;
        Brush brush;

        Projektion p;

        public Form1()
        {
            InitializeComponent();

            offset = new PointF(300, 200);

            Punkte = new Vector3D[8];

            Punkte[0] = new Vector3D(-100, -100, 100);
            Punkte[1] = new Vector3D(100, -100, 100);
            Punkte[2] = new Vector3D(100, 100, 100);
            Punkte[3] = new Vector3D(-100, 100, 100);
            Punkte[4] = new Vector3D(-100, 100, -100);
            Punkte[5] = new Vector3D(100, 100, -100);
            Punkte[6] = new Vector3D(100, -100, -100);
            Punkte[7] = new Vector3D(-100, -100, -100);

            RotX = new Matrix3x3();
            RotY = new Matrix3x3();
            RotZ = new Matrix3x3();

            pen = new Pen(Color.Blue);
            pen.Width = 3;
            brush = new SolidBrush(Color.Red);

            p = Projektion.Zentral;
        }

        public void Draw()
        {
            pictureBox1.Invalidate();
        }

        public Vector3D Multiply(Matrix3x3 A, Vector3D b)
        {
            Vector3D Result;
            Result.X = A[0, 0] * b.X + A[0, 1] * b.Y + A[0, 2] * b.Z;
            Result.Y = A[1, 0] * b.X + A[1, 1] * b.Y + A[1, 2] * b.Z;
            Result.Z = A[2, 0] * b.X + A[2, 1] * b.Y + A[2, 2] * b.Z;
            return Result;
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Vector3D[] Points = (Vector3D[])Punkte.Clone();

            // Rotation
            for (int i = 0; i < Points.Length; i++)
            {
                Points[i] = Multiply(RotX, Points[i]);
            }

            // Rotation
            for (int i = 0; i < Points.Length; i++)
            {
                Points[i] = Multiply(RotY, Points[i]);
            }

            // Rotation
            for (int i = 0; i < Points.Length; i++)
            {
                Points[i] = Multiply(RotZ, Points[i]);
            }


            PointF[] dp = new PointF[Points.Length];

            for (int i = 0; i < Points.Length; i++)
            {
                dp[i] = Points[i].ToPoint(p);
                dp[i].X += offset.X;
                dp[i].Y += offset.Y;

            }

            e.Graphics.FillPolygon(brush, dp);
            e.Graphics.DrawPolygon(pen, dp);
            e.Graphics.DrawLine(pen, dp[0], dp[3]);
            e.Graphics.DrawLine(pen, dp[4], dp[7]);
            e.Graphics.DrawLine(pen, dp[1], dp[6]);
            e.Graphics.DrawLine(pen, dp[2], dp[5]);
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            double alpha = trackBar1.Value * Math.PI / 180;
            RotX[1, 1] = (float)Math.Cos(alpha);
            RotX[1, 2] = -1 * (float)Math.Sin(alpha);
            RotX[2, 1] = (float)Math.Sin(alpha);
            RotX[2, 2] = (float)Math.Cos(alpha);
            Draw();
        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            double alpha = trackBar2.Value * Math.PI / 180;
            RotY[0, 0] = (float)Math.Cos(alpha);
            RotY[0, 2] = -1 * (float)Math.Sin(alpha);
            RotY[2, 0] = (float)Math.Sin(alpha);
            RotY[2, 2] = (float)Math.Cos(alpha);
            Draw();
        }

        private void trackBar3_Scroll(object sender, EventArgs e)
        {
            double alpha = trackBar3.Value * Math.PI / 180;
            RotZ[0, 0] = (float)Math.Cos(alpha);
            RotZ[0, 1] = -1 * (float)Math.Sin(alpha);
            RotZ[1, 0] = (float)Math.Sin(alpha);
            RotZ[1, 1] = (float)Math.Cos(alpha);
            Draw();
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            Vector3D.effect = (float)numericUpDown1.Value / 10000f;
            Draw();
        }

        private void radioButton1_CheckedChanged(object sender, EventArgs e)
        {
            p = radioButton1.Checked ? Projektion.Parallel : Projektion.Zentral;
            Draw();
        }
    }

    public enum Projektion {Parallel, Zentral};

    public struct Vector3D
    {
        public float X, Y, Z;

        public static float effect = 0.002f;

        public Vector3D(float x, float y, float z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        public PointF ToPoint(Projektion art)
        {
            switch (art)
            {
                case Projektion.Parallel:
                    return new PointF(X, Y);
                case Projektion.Zentral:
                    return new PointF(X * (1 - effect * Z), Y * (1 - effect * Z));
                default:
                    throw new Exception();
            }
        }

        public void Add(Vector3D b)
        {
            X += b.X;
            Y += b.Y;
            Z += b.Z;
        }
    }

    public class Matrix3x3
    {
        private float[,] values;

        public Matrix3x3()
        {
            values = new float[3, 3];
            values[0, 0] = 1;
            values[1, 1] = 1;
            values[2, 2] = 1;
        }

        public float this[byte row, byte col]
        {
            get { return values[row, col]; }
            set { values[row, col] = value; }
        }

        public override String ToString()
        {
            String result = "";
            for (int i = 0; i < 3; i++)
            {
                result += "|  ";
                for (int j = 0; j < 3; i++)
                {
                    result += String.Format("%0.00" + Convert.ToChar(8), values[i, j]);
                }
                result += " |\r\n";
            }
            return result;
        }
    }
}
Programm im Anahng ;)

Namenloser 5. Mai 2009 21:37

Re: Drehung eines Körpers um eine beliebige Achse
 
Zitat:

Zitat von jfheins
Ich hab dir da mal ein Beispiel vorgerechnet ...

Danke auch noch mal von mir! Endlich mal eine verständliche Erklärung :thumb:

Ben19 6. Mai 2009 22:09

Re: Drehung eines Körpers um eine beliebige Achse
 
Hey ich bins nochmal :)
Habe dein Beispiel mir noch einmal angeguckt und da sind mir noch zwei Fragen gekommen.
Zunächst gibt es für die Drehmatrizen eine Herleitung? und kann man auch um einen Punkt oder Gerade im Raum drehen? Denn muss man doch von dem jeweiligen Punkt bzw der Gerade die Vektoren zu den Eckpunkten bestimmen und die mit den Drehmatrizen multiplizieren oder?

Vielen Dank im vorraus
Ben19

jfheins 6. Mai 2009 22:38

Re: Drehung eines Körpers um eine beliebige Achse
 
Zitat:

Zitat von Ben19
Hey ich bins nochmal :)
Habe dein Beispiel mir noch einmal angeguckt und da sind mir noch zwei Fragen gekommen.
Zunächst gibt es für die Drehmatrizen eine Herleitung?

Eine Herleitung der Drehmatrizen? naja man kann sie sich herleiten indem man schaut, wohin die Einheitsvektoren gedreht werden sollen (welche neuen Koordinaten die kriegen)
Ansonsten gibts ja auch noch die englische Wikiepdia: http://en.wikipedia.org/wiki/Rotation_matrix Da steht ne ganze Menge (nein, habs mir nicht durchgelesen)
Zitat:

und kann man auch um einen Punkt oder Gerade im Raum drehen? Denn muss man doch von dem jeweiligen Punkt bzw der Gerade die Vektoren zu den Eckpunkten bestimmen und die mit den Drehmatrizen multiplizieren oder?
Also man dreht immer um eine Gerade. Die "Drehgerade" ist genau die, die sich nicht ändert.

In dem Beispiel drehe ich quasi um einen Punkt (weil die Dreh-gerade senkrecht zur Bildebene steht) und zwart um den Mittelpunkt/Ursprung (mit O bezeichnet)

Wenn du das Quadrat nicht um den Ursprung legenb würdest, sondern z.B. die eine Ecke in den Ursprung, würde sich das ganze immernoch um den Ursprung drehen. D.h. der Mittelpunkt des Quadrats bewegt sich ebenfalls. (Ist dann eher ein Mix aus Rotation und Translation)

Aber im Normalfall möchtest du die Rotation von der Translation trennen, also um den Mittelpunkt des Objekts rotieren.

Ben19 12. Mai 2009 13:25

Re: Drehung eines Körpers um eine beliebige Achse
 
Hallo,
ich hätte noch ein Frage. Kann mir jemand bitte erklären wie ich z.B. ein Quadrat in einem 3D-Raum um eine beliebige Gerade(nicht um die Achsen) rotiere?
Hatte mich auch schon informiert, habe aber noch Probleme beim Verständnis.
Würde mich sehr über eine Antwort sehr freuen
Danke im voraus
Ben19

jfheins 12. Mai 2009 17:11

Re: Drehung eines Körpers um eine beliebige Achse
 
Die passende Drehmatrix bekommst du aus Wikiepdia:
http://upload.wikimedia.org/math/e/d...4f396333ac.png

Aus http://de.wikipedia.org/wiki/Drehmat...Raumes_R.C2.B3 unterster Punkt ;)

Ben19 12. Mai 2009 21:04

Re: Drehung eines Körpers um eine beliebige Achse
 
Danke für die schnelle Antwort:)
Die hatte ich auch schon gefunden. Kann mir aber nicht erklären wie die zustande kommt oder wie ich so etwas berechne:S
Wäre echt nett, wenn du mir das noch einmal erklären könntest:)
vielen Dank im vorraus
Ben19

Ben19 30. Mai 2009 00:15

Re: Drehung eines Körpers um eine beliebige Achse
 
Hey Leute
ich bin es wieder :wink: . Beim programieren bin ich auf einen weiteres Problem gestoßen :?
Habe ein Probleme die Rotationsachse um eine beliebige Achse in Delphi einzubinden.
Rotationsachse
http://upload.wikimedia.org/math/e/d...4f396333ac.png
Wie muss ich die Gleichung in Delphi schreiben?
Muss ich wirklich immer den selben Winkel (hier alpha) drehen?
Wäre echt nett wenn ich mir das erklären könntet.
Vielen Dank im voraus
Ben19

Ben19 30. Mai 2009 11:51

Re: Drehung eines Körpers um eine beliebige Achse
 
sry ich meine oben natürlich nicht Rotationsachse sondern Rotationsmatrix :oops:

Khabarakh 30. Mai 2009 14:31

Re: Drehung eines Körpers um eine beliebige Achse
 
Zitat:

Zitat von Ben19
Muss ich wirklich immer den selben Winkel (hier alpha) drehen?

Du kannst natürlich verschieden Drehungen mit verschiedenen Winkeln und Drehachsen durchführen, aber während einer Drehung sollte alpha schon konstant sein :gruebel: ...

Die entstandene Matrix musst du dann mit deinem Vektor multiplizieren, siehe weiter oben im Artikel.

Jakob Ullmann 30. Mai 2009 15:28

Re: Drehung eines Körpers um eine beliebige Achse
 
Wie soll das Quadrat eigentlich 3D dargestellt werden? Parallel- oder Zentralprojeketion?

Ben19 30. Mai 2009 16:22

Re: Drehung eines Körpers um eine beliebige Achse
 
Hey danke für eure antworten :)
Also es ist 3D und soll mit der Parallelprojektion gezeigt werden.
Aber die Drehung funktioniert jetzt schon :thumb:
Problem die Objekte werden immer größer und verschwinden letztendlich. :?
Weiß jmd wodran das liegen kann?
Vielen Dank im voraus
Ben19

Ben19 1. Jun 2009 10:54

Re: Drehung eines Körpers um eine beliebige Achse
 
Hey Delphi-Community
Kann mir wirklich keiner einen Tipp geben was in meinem Programm falsch sein könnte?
Wäre echt nett wenn mir einen Tipp geben könnte.
Vielen Dank im voraus
Ben19

turboPASCAL 1. Jun 2009 11:03

Re: Drehung eines Körpers um eine beliebige Achse
 
Zitat:

Kann mir wirklich keiner einen Tipp geben was in meinem Programm falsch sein könnte?
An der Quadratwurtzel im untrern Quadranten links. :gruebel:

Da wir nicht wissen können was du in deinem Quellcode veranstaltest ist es uns fast nicht möglich zu helfen.

Ben19 1. Jun 2009 11:19

Re: Drehung eines Körpers um eine beliebige Achse
 
Aso ja stimmt :oops:
Also hier ist die procedure die für die Drehung verantwortlich ist

Delphi-Quellcode:
procedure TForm1.bdrehungChange(Sender: TObject);   //Rotation um beliebige Achse
var x,y,z,x1,y1,z1:real;j:integer;bogen:real;
begin
  objekteausblenden;
  bogen:=gradzubogen((Form1.bdrehung.Position-Gegenstaende[Auswahl].drehung[4]));
  for j:=1 to Gegenstaende[Auswahl].Punktzahl do
  begin
    x1:=Gegenstaende[Auswahl].Punktfeld[j][1]+Gegenstaende[Auswahl].ortsvektor[1];
    y1:=Gegenstaende[Auswahl].Punktfeld[j][2]+Gegenstaende[Auswahl].ortsvektor[2];
    z1:=Gegenstaende[Auswahl].Punktfeld[j][3]+Gegenstaende[Auswahl].ortsvektor[3];
    x1:=x1-strtofloat(form1.geradex.text);
    y1:=y1-strtofloat(form1.geradey.text);
    z1:=z1-strtofloat(form1.geradez.text);
    x:=strtofloat(form1.geraderx.text);
    y:=strtofloat(form1.geradery.text);
    z:=strtofloat(form1.geraderz.text);
    Gegenstaende[Auswahl].Punktfeld[j][1]:=(x*x*(1-cos(bogen))+cos(bogen))*x1+(x*y*(1-cos(bogen))-z*sin(bogen))*y1+(x*z*(1-cos(bogen))+y*sin(bogen))*z1+strtofloat(form1.geradex.text)-Gegenstaende[Auswahl].ortsvektor[1];
    Gegenstaende[Auswahl].Punktfeld[j][2]:=(x*y*(1-cos(bogen))+z*sin(bogen))*x1+(y*y*(1-cos(bogen))+cos(bogen))*y1+(y*z*(1-cos(bogen))-x*sin(bogen))*z1+strtofloat(form1.geradey.text)-Gegenstaende[Auswahl].ortsvektor[2];
    Gegenstaende[Auswahl].Punktfeld[j][3]:=(x*z*(1-cos(bogen))-y*sin(bogen))*x1+(y*z*(1-cos(bogen))+x*sin(bogen))*y1+(z*z*(1-cos(bogen))+cos(bogen))*z1+strtofloat(form1.geradez.text)-Gegenstaende[Auswahl].ortsvektor[3];
  end;
  Gegenstaende[Auswahl].drehung[4]:=Form1.bdrehung.Position;
  Form1.bdrehwert.Text:=inttostr(Form1.bdrehung.Position);
  objekteanzeigen();
end;
Vielen Dank im voraus
Ben19

dark_detlef 2. Jun 2009 09:47

Re: Drehung eines Körpers um eine beliebige Achse
 
Hallo,
also ich gehöre auch zu der Gruppe von Ben_19. Wir haben unser Programm eigentlich sogut wie fertig, nur die Drehung um eine beliebige Achse fehlt noch. Leider können wir unseren Fehler nicht identifizieren :(
Zur Erklärung des Quelltextes von Ben_19:
Gegenstaende[Auswahl].ortsvektor:
Hierbei handelt es sich um einen Vektor, der vom Ursprung zum Objekt hinzeigt. Von diesem Punkt widerrum aus gibt es zu jeder Objektecke einen neuen Vektor, deshalb addieren wir diesen erst um den absoluten Punkt zu erhalten und später subtrahieren wir ihn wieder, weil in unserer Zeichenoperation dieser automatisch addiert wird.
form1.geradex.text:
Hier steht der Ortsvektor der Geraden drin, den wir zunächst von jedem Objektpunkt subtrahieren, um die Gerade in den Ursprung zu verlegen, später wird dieser wieder addiert.
form1.geraderx.text:
Dieser ist der Richtungsvektor der Geraden. Diesen setzen wir später in die Gleichung ein und möchten um diesen drehen.

Der Fehler der nun autritt sieht folgender Maßen aus:

Der Objekt wird zwar gedreht, scheinbar auch um die Achse, aber der Z-Wert spielt verrückt(Wird einfach zu groß mit der Zeit). Lassen wir diesen aus dem Spiel(Z=0) dann funktioniert die Drehung...
Hatten früher schon einmal so ein ähnliches Problem, da lags daran, dass wir mit bereits veränderten Werten gerechnet haben.

Bitte helft uns ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz