|
http://rubinghsoftware.de/projects/dako_3d/
|
Projekt | |
Entwicklung 3D-Kern für 3D CAD-Programme | |
Für | |
DAKO GmbH, Jena (als Angestellter) | |
Datum | |
Sep 2008 - Jun 2010 | |
Platforms | |
C++, OpenGL, MS Visual C++, Windows PC | |
Für diesen Auftrag habe ich einen neuen 3D-Kern entworfen und implementiert, für Einsatz in Programmen für Computer Aided Design (CAD), in 3D, von mechanischen Bauteilen.
Die wichtigste Funktion dieses 3D-Kerns ist, eine CSG-Operation zu berechnen (CSG = Constructive Solid Geometry), wobei sowohl die zwei input-Körper als auch der output-Körper repräsentiert werden durch ihre Oberflächensegmente und Kanten; und wobei der output-Körper als input für weitere CSG-Operationen verwendet werden kann.
Der 3D-Kern wurde strikt isoliert von OpenGL und von jeder visuellen Darstellung.
Das Wort "3D-Kern" bezeichnet die Software-Funktionalität (software library) die zuständig ist für die mathematischen Operationen die intern in dem 3D CAD Anwendungsprogramm benötigt werden.
Die wichtigste Funktion des von mir entwickelten 3D-Kerns ist, eine CSG-Operation auszuführen (zu berechnen). In dem CAD Anwendungsprogramm wird, wie auch in vielen anderen 3D CAD Programmen, so vorgegangen dass der CAD-Zeichner ein komplexes Bauteil schrittweise zusammenstellt über Constructive Solid Geometry (CSG), das heißt durch das Schneiden, Subtrahieren, und Zusammenfügen von einfachen 3D-Körpern. Jede einzelne CSG-Operation ist also eine Operation auf 2 Körper, die als Ergebnis ein neues (und komplexeres) Körper produziert; und ist eine der klassischen Mengenoperationen: Schnittmenge (intersection), Differenz (difference), oder Vereinigung (union)[1].
Im 3D CAD Programm des Auftraggebers war ursprünglich das vom Zeichner konstruierte 3D Bauteil nur verfügbar als "Rezept" der sukzessive ausgeführten CSG-Operationen. Ziel meiner Aufgabe war, das konstruierte Bauteil zusätzlich verfügbar zu machen als boundary representation, das heißt als mathematische Spezifikation der Oberfläche des fertig konstruierten 3D Körpers. Diese boundary representation war notwendig um es dem CAD-Anwendungsprogramm zu ermöglichen, die konstruierten Bauteile abzuspeichern in der Form einiger zusätzlichen vielbenutzten CAD-Dateiformate.
![]() |
(Abb. 1)
A simple case to show the basics of how an individual CSG operation is performed on
the boundary representation level. The two input solids to the CSG operation
are the two cubes A (green) and B (blue).
Shown in orange are the curve segments where the surfaces of the two solids intersect. The orange curve segments cut up a number of the surface segments of the two solids into parts. The surface of the output solid of the CSG operation is composed of: (1) a subset of exactly these cut-up surface parts, plus (2) a subset of those input surface segments that were not cut up by the orange curve segments. Furthermore, for each of the four possible CSG operations (A-B, B-A, A∩B, A∪B), the set of edges of the output solid always contains all of the orange curve segments. |
Die boundary representation eines 3D Körpers besteht aus einer Menge von Oberflächensegmenten (surface segments), Kanten (edges), und Eckpunkten (vertices), wobei die Oberflächensegmente eine einfache mathematische Beschreibung haben. (Für diese Aufgabe waren nur 4 Typen von Oberflächensegmenten benötigt: Ebene, Zylinder, Kegel, und Torus.)
Mit Verwendung meines neuen 3D-Kerns wird jede CSG-Operation ausgeführt (errechnet) auf
der boundary representation Ebene.
Eine der (einfacheren) Teilaufgaben ist selbstverständlich gewesen das Entwerfen der
Datenstrukturen benötigt für die boundary representation eines 3D-Körpers.
Die wichtigsten Datenstrukturen, bereitgestellt von der 3D-Kern library an die Anwendungssoftware,
und intern verwendet innerhalb des 3D-Kerns, sind gewesen:
Die dem CAD-Zeichner verfügbaren atomischen Körper haben sich in dieser Aufgabe beschränkt
auf Körper die erzeugbar sind durch Extrusion oder Rotation einer ebenen Kontur bestehend aus
Geradensegmenten und Kreissegmenten. Daher waren für diese Aufgabe nur 4 Typen von
Oberflächensegmenten benötigt: Ebene (Extrusion von Gerade), Zylinder (Extrusion von Kreis),
Kegel (Rotation von Ebene), und Torus (Rotation von Kreis). Durch eine CSG-Operation können
nur neue Typen von Kanten (Schnittkurven) entstehen, aber nicht neue Typen von Oberflächen.
Die wichtigste Teilaufgabe beim errechnen einer CSG-Operation auf der boundary representation
Ebene ist, die Schnittkurven zu berechnen von zwei Oberflächensegmenten.
Die Schnittkurven wurden im 3D-Kern analytisch berechnet wo möglich, und numerisch
für komplexere Fälle. Der numerische Berechnungsweg ist für alle Oberflächentypen
verwendbar. Für die einfacheren Fälle wurde der analytische Berechnungsweg aber
bevorzugt wegen seiner unschlagbar schnellen Laufzeit.
Schnittkurven berechnen — Analytisch
Wenn die zwei sich schneidenden Oberflächensegmenten von einfacher Art sind (z.B. Ebene + Ebene,
oder Ebene + Zylinder), dann ist es einfach, analytisch die Parametergleichung für die
Schnittkurven zu finden.
Dieser analytische Lösungsweg ist aber unmöglich wenn die sich schneidenden Oberflächen
beide von einem komplexen Typ sind. In diesem Fall wird nämlich der Polynom-Grad der
zu lösenden Gleichung für Parameter β zu hoch dafür, im Allgemeinfall noch analytisch
lösbar zu sein. (Zum Beispiel: Schneiden von zwei Torus-Oberflächen ergibt im Allgemeinfall
eine Gleichung 8. Grades in den Parametern α und β.)
Schnittkurven berechnen — Numerisch
Ein numerisch gefundenes Schnittkurvensegment wird intern repräsentiert durch eine
geordnete Liste von Punkten auf dem Kurvensegment
(Stützpunkten).[2]
Das erste Teilproblem wird gelöst dadurch dass auf eines der sich schneidenden
Oberflächensegmente eine Menge grid curves mit α=konstant (rot), und separat auch
eine Menge grid curves mit β=konstant (blau), geschnitten werden mit dem anderen
Oberflächensegment, wie beschrieben beim Diagramm oben.
Hiermit ist est sichergestellt dass wir genug Punkte finden die zu den Schnittkurven der zwei
Oberflächensegmente gehören (je feiner das α,β-Grid, desto genauer die numerischen
Schnittkurven).
Die Zuordnung der so gefundenen Punkte zu zusammenhangenden Kurvensegmenten ist ein separates
Problem (wofür das bekannte mathematische Standardrepertoire keine Bausteine anbietet).
Für dieses neue Problem habe ich einen (nicht-iterativen) Algorithmus entwickelt basiert auf
der Vorgehensweise grob beschrieben bei folgendem Diagramm.
Schnittkurven berechnen — Endpunkte
Beide Berechnungswege (analytisch und numerisch) ergeben zunächst die Schnittkurven in der
Form von Kurvensegmenten die nur begrenzt werden durch die Grenzen der Oberflächenparameter
α,β eines der zwei geschnittenen Oberflächen (gelbes Kurvensegment AE in
Abb. 3).
Zwei durch digitale Berechnung entstandene Kurven in 3D schneiden sich natürlich generell nie
100% exakt. Die Software findet daher genaugenommen nicht Schnittpunkte, sondern findet
die Punkte auf den zwei Kurven die die kleinste Distanz zwischen sich haben.
Zum Finden dieser Punkte wurde die geometrische Idee hinter der Newton-Raphson Methode benutzt,
wie beschrieben beim folgenden Diagramm.
Zwei beliebige Kurvensegmente können natürlich im Allgemeinen mehr als nur 1 Schnittpunkt
haben.
Zum Finden aller Schnittpunkte (aller Stellen wo die zwei Kurven sich nah annähern) wurde
Rekursion eingesetzt, nämlich nach Finden eines Schnittpunktes werden die Kurven dort zerteilt,
wonach dann die separaten Teile miteinander geschnitten werden (und so weiter bis keine weiteren
Schnittpunkte gefunden).
Als Teil des Projektes wurde einen Tessellator entwickelt der einsetzbar ist für gebogene
(d.h. nicht-ebene) Oberflächen.
Dies war notwendig weil, werkwürdigerweise, keiner der eingebauten Tessellatoren in den
verfügbaren OpenGL libraries in der Lage war, nicht-ebene Oberflächen zu tessellieren.
Wie üblich in nicht-trivialen Softwareprojekten, wurde auch im 3D-Kern eine Funktionalität
hinzugefügt zum regression testing, plus eine Menge Testfälle. Für den 3D-Kern war
dieses regression testing absolut unerlässlich, nicht nur wegen der Komplexität der Software,
aber auch wegen der großen Menge der mathematischen Spezialfälle.
In allen Softwareentwicklungs-Projekten versuche ich, neben Dokumentation über die in
der Software benutzen Algorithmen, im Sourceverzeichnis immer eine Textfile aufzunehmen die
eine kurze Übersicht gibt über die Sourcefiles. Die
entsprechende für dieses Projekt abgelieferte Textfile ist hier.
Dank gebührt dem technischen Leiter der DAKO CAD-Softwareabteilung, Volker Scheffer, für die
gute Zusammenarbeit; dem damaligen Mitarbeiter Alex Lärz für den nützlichen fachlichen
Gedankenaustausch; und der DAKO-Geschäftsführung, für die Vergabe dieser interessanten
Aufgabe!
Ein Arbeitszeugnis des Auftraggebers kann ich Ihnen auf Anfrage gern senden.
Aufgabe meines 3D-Kerns ist also, gegeben eine auszuführende CSG-Operation
(intersection/
(Abb. 2)
As is usual in 3D computer graphics, each surface segment in my 3D kernel is represented
as a parameterized surface (x,y,z) = P(α,β), bounded by closed loops
of curve segments. Similarly, each curve segment (edge) is represented as a
parameterized curve (x,y,z) = P(α), bounded by two vertices.
Each curve segment handled by the 3D kernel is always part of at least one surface
segment (ConS, Curve on Surface), namely it is usually a curve on two different surface
segments simultaneously.
"Schnittkurven zu berechnen" bedeutet, genauer formuliert, dass die Kurven-Segmente gefunden
werden wo die Oberflächensegmente der beiden input-Körper der CSG-Operation einander
schneiden. Diese Schnittkurvensegmente (orange im Diagramm unten) bilden Kanten
(edges) im output-Körper der CSG-Operation.
Die Schnittkurvensegmente müssen —wie auch jede andere Kante in der boundary representation
eines Körpers— als parameterisierte Kurven repräsentiert werden. Ziel der
Schnittkurvenfindung ist also, Parametergleichungen zu finden für die Schnittkurvensegmente.
(Abb. 3)
Overview of the basic geometrical situation when intersecting two surface segments.
Each of the two intersected surface segments is bounded by a contour (blue, green).
The intersection of the two surface segments is the bounded curve segment CD
(orange).
This is a segment of the (unbounded) intersection curve between the two (unbounded)
surfaces, namely it is the segment of the (unbounded) intersection curve that falls
within the contours of both of the surface segments.
(Nämlich: Substutution der Parametergleichung für eine der Oberflächen (x,y,z) =
P(α,β) in die implizite Gleichung der anderen Oberfläche f(x,y,z) = 0,
dann Lösung so dass eine Formel entsteht für Parameter β als Funktion von α, und
schließlich Zurücksubstitution dieser Formel für β in die Parametergleichung für die erste
Oberfläche, ergibt die Parametergleichung für die Schnittkurve (x,y,z) = P(α).)
(Abb. 4)
The basic layout of a relatively complex case of intersection curves computed analytically:
the intersection curves between two cylindrical surfaces.
The upper sketch shows the axes of the two cylinders. The lower sketch shows
the intersection curve segments, and the parameter by which each of these
intersection curve segments is parameterized.
Die zwei Teilprobleme beim numerisch Finden der Schnittkurven zweier Oberflächensegmente sind:
Finden einer Menge von Punkten die zu den Schnittkurven gehören, und Zuordnung (Sammeln)
dieser Punkte zu zusammenhangenden Kurvensegmenten.
(Abb. 5)
Das Finden einer Menge von Punkten auf den Schnittkurven wird wie folgt gemacht.
Es wird wieder die Parametergleichung (x,y,z) = P1(α,β) benutzt
für eine der Oberflächen (Oberfläche 1), und die implizite Gleichung f2(x,y,z)
= 0 für die andere Oberfläche (Oberfläche 2).
Die Grundidee des Lösungsweges ist, uns über Oberflächensegment 1 zu bewegen entlang
einer Kurve (grid curve) mit α = konstant und mit β = variabel (rot).
Entlang einer solchen grid curve betrachten wir die Funktion f2(x,y,z) =
f2(P1(β)) = f2(β). Die Nullstellen
dieser Funktion sind die Punkte wo diese grid curve die Oberfläche 2 schneidet. Damit
ist dieses Teilproblem reduziert zum Finden der Nullstellen einer Funktion von einer
Variablen, ein bekanntes Problem in der numerischen Mathematik. Im 3D-Kern werden
die Nullstellen dieser einvariablingen Funktion gefunden durch zunächst eine grobe
Ortung der Nullstellen über einen intelligent gesteuerten Einsatz der bisection
method, gefolgt für jede einzelne Nullstelle durch eine Verfeinerung über
einige Newton-Raphson-Schritte.
Selbstverständich kann das gleiche gemacht werden für grid curves auf Oberflächensegment
1 mit α = variabel und mit β = konstant (blau), d.h. in dem Fall Finden wir die
Nullstellen der Funktion f2(x,y,z) = f2(P1(α))
= f2(α).
(Abb. 6)
Der Algorithmus sammelt zunächst die gesamte Menge 3D-Punkte auf den Nullstellen
der grid curves mit α=konstant zusammen zu einer Menge vorerst noch
separater Teilkurven (Teile der zu findenden gesamten Schnittkurven), die
"constant-α-Teilkurven" (rot). Danach werden analog die Menge
3D-Punkte auf den Nullstellen der grid curves mit β=konstant zusammengesammelt zu
einer Menge von "constant-β-Teilkurven" (blau).
Durch Hinzufügen von zusätzlichen grid curves (die regulär numerisch verarbeitet werden)
auf die Endpunkte dieser Teilkurven wird sichergestellt dass diese Teilkurven zwischen
einander auf eindeutige weise überlappen.
Letzteres erlaubt es dann schließlich, die Teilkurven zusammenzunehmen zu kompletten
Kurvensegmenten.
Als letzter Schritt ist er daher immer notwendig, diese noch zu langen gelben
Kurvensegmente zu schneiden mit den Konturen der beiden Oberflächensegmente
(blau und grün in Abb. 3), damit die Endpunkte
gefunden werden der gesuchten Schnittkurvensegmente (orangenes Kurvensegment CD in
Abb. 3).
(Abb. 7)
Iterative algorithm to find the points on two 3D curves that are nearest to each
other. Note that the diagram depicts a situation in 3D, not in 2D.
A0 and B0 are initial points on curves A and B, respectively.
Take the lines tangent to the curves at these points, and compute the points
on these two tangent lines that are nearest to each
other; call these points PA (on the tangent line through A0),
and PB (on the tangent line through B0).
Invert PA to curve A, i.e. compute the point on curve A nearest to point
PA; call this point A1. Similarly, invert PB
to curve B, yielding point B1.
A1 and B1 are nearer to the closest points on the two curves than
A0 and B0 were, and replace these initial points A0
and B0.
Repeat the same step until sufficiently accurate convergence is reached for the
two points A0 and B0.
Dass dieses Verfahren alle Schnittpunkte findet konnte sichergestellt werden dadurch dass für
komplexe Kurvensegmente mehr als nur ein Paar von strategisch positionierten initiellen Punkten
A0,B0 ausprobiert wird (der Komplexitätsgrad der zwei geschnittenen
Kurvensegmente ist bekannt).
(Abb. 8)
The tessellation of a non-planar surface segment (here: a cylinder surface segment).
Typical 3D graphical display libraries, such as OpenGL, are designed and optimized for
drawing planar 3D surface elements bounded by contours composed of a few straight-line
segments. Therefore, to display a surface segment that is curved and/or that has
a complex boundary, it must be represented, during drawing, as a set of small, planar
surface elements (its tessellation).
Note that the tessellation of a surface segment is strictly only a part of the visual
representation process; and has nothing to do with the boundary representation of the
solid (as a set of surface segments, edges, and vertices) that is operated upon during
the computation of a CSG operation.
Diese Spezialfälle treten auf wenn zwei Oberflächensegmente teilweise zusammenfallen, oder
wenn zwei Oberflächensegmente einander schneiden nicht in einer Kurve sondern in einem Punkt
(z.B.: Ebene wird nur in einem Punkt berührt von Torus-Oberfäche, oder Ebene wird berührt
von nur einem Punkt in der Grenzkurve eines Oberflächensegments). Solches Zusammenfallen
passiert in der Praxis oft, weil beim 3D CAD Zeichnen oft absichtlich so konstruiert wird.
Das Zusammenfallen wurde überall im 3D-Kern überprüft und detektiert aufgrund eines vorgegebenen
Distanz-Schwellenwertes (Konstruktionsgenauigkeit).
(Abb. 9)
A small extract from the input file to the regression tester, specifying the test cases to
be executed. This extract contains only three of the 135 test cases used in total.
For each test case, a file is loaded containing the specification of a number of solids
(WF), and then a number of 3D kernel operations are executed (CS).
After this, the number of surface segments in the result solid (IS) is compared with
the expected number of surface segments (NM).
The number of surface segments in the result solid is extremely sensitive to any type of
error in the CSG operation, and this test criterion was not only simple to specify but
also proved to be very reliable for catching errors.
Die Textfile gibt eine Auflistung aller Sourcefiles, beschreibt kurz den Zweck jeder Sourcefile,
und legt vor allem dar, wie die Zusammenhänge (dependencies) zwischen den Sourcefiles
strukturiert sind. Zweck dieser Sourcen-Übersicht ist, den nächsten Programmierern
die nach mir den Code anfassen, den Zugang zu den Sourcen zu erleichtern.
Anmerkungen
[1]
Mengenoperationen.
Eine CSG-Operation ist eine Mengenoperation (Schnittmenge, Differenz, Vereinigung)
wenn jeder 3D-Körper betrachtet wird als die Menge aller Punkte eingeschlossen durch
die Oberfläche des Körpers. Diese Betrachtungsweise ist die, die benutzt wird
von dem CAD-Zeichner während der Konstruktion des Bauteils.
Jedoch, der Computer kann natürlich keine unendlichen Datenmengen verarbeiten, wie die
Menge aller Punkte innerhalb eines 3D-Körpers. Im Computer sind wir gezwungen, zu
rechnen mit endlichen Datenmengen — nämlich in diesem Fall den boundary representations
basiert auf endlichen Mengen von Stützpunkten.
Was hier passiert ist also dass die abstrakte Mengenoperation auf die Mengen der von der
Körperoberflächen eingeschlossenen Punkte "simuliert" wird dadurch dass das Ergebnis
errechnet wird auf der Ebene der boundary representations.
Die CSG-Operation wie im Computer ausgeführt, d.h. wie berechnet auf der Ebene der
boundary representations, ist keine Mengenoperation, sondern ist eine wesentlich
andere (und komplexere) Art von Operation.
[2]
Ein numerisch gefundenes Schnittkurvensegment wird intern repräsentiert durch
eine geordnete Liste von Punkten auf dem Kurvensegment (Stützpunkten).
Genauer: Ein numerisch gefundenes Schnittkurvensegment wird repräsentiert in der Form
einer geordneten Liste von diskreten Werten des Kurvenparameters α, wobei das
Listenelement jedes α-Wertes auch den dazugehördenden 3D-Punkt auf der Kurve
enthält.
Gegeben diese Repräsentation wird dann ein Punkt auf dem Kurvensegment mit beliebigem
gegebenen Parameterwert α jederzeit on-the-fly berechnet durch Interpolation aus
den 3D-Punkten in den Listenelementen für die benachbarten α-Werte (nämlich
das Kurvensegment wird dargestellt als piecewise quadradic Bezier curve).
Die Funktion die diese Interpolationsberechnung ausführt, implementiert
die Parametergleichung (x,y,z) = P(α) des numerisch gefundenen
Schnittkurvensegmentes.