Zentralprojektion
Eine Zentralprojektion wird festgelegt durch einen dreidimensionalen Augpunkt S und eine Ebene E. Die Projektion bildet einen dreidimensionalen Punkt P auf einen Punkt T(P) in der Ebene E so ab, dass S, T(P) und P auf einer Geraden liegen.
Wenn die Ebene E durch zwei orthogonale Einheitsvektoren u und v und einen Punkt w festgelegt wird, führt die obige Definition auf
T(P) = x u + y v + w und
T(P) = S + a (P - S),
wobei P, S, u, v und w gegeben sind und man T(P), x, y und a sucht. Das führt auf ein Gleichungssystem:
u1 x + v1 y + (S1 - P1) a = S1 - w1
u2 x + v2 y + (S2 - P2) a = S2 - w2
u3 x + v3 y + (S3 - P3) a = S3 - w3
Da u und v orthogonale Einheitsvektoren sind, kann man die Lösungen des Gleichungssystems in x und y zu einem Punkt (x y) kombinieren und diesen als Ergebnis der Projektion von P ins Zweidimensionale auffassen.
Die Realisierung verwendet die Funktion create-projection-matrix, um aus den Vektoren u und v und den Punkten s und p die Matrix des linearen Gleichungssystems aufzustellen.
(defproc create-projection-matrix (u v s p)
(new matrix 3 3
(list
(list (first u) (first v) (- (first s) (first p)))
(list (second u) (second v) (- (second s) (second p)))
(list (third u) (third v) (- (third s) (third p))))))
Der Spaltenvektor des linearen Gleichungssystems wird von der Funktion create-projection-column-vector in Abhängigkeit von w und s berechnet.
(defproc create-projection-column-vector (w s)
(new column-vector 3
(list
(- (first s) (first w))
(- (second s) (second w))
(- (third s) (third w)))))
Die Funktion project-point projiziert einen Punkt indem, wie oben beschrieben, das lineare Gleichungssystem gelöst wird (siehe Lineare Gleichungssysteme lösen). Die Lösung hat drei Elemente (x, y und a), von denen man das letzte nicht verwendet.
(defproc project-point (u v w s point)
(map-with
first
(butlast
(.
(solve-linear-equations
(create-projection-matrix u v s point)
(create-projection-column-vector w s))
elements))))
Die Funktion project-polylines wendet project-point auf die Punkte in einer Liste von Listen an.
(defproc project-polylines (u v w s polylines)
(map-with
(lambda (polyline)
(map-with
(curry (project-point u v w s _))
polyline))
polylines))
Die Funktion get-bounds bestimmt den rechteckigen Bereich, in dem sich die Punkte in einer Liste von Listen befinden.
(defproc get-bounds (polylines)
(let
((min-x nil) (min-y nil) (max-x nil) (max-y nil))
(dolist (polyline polylines (list min-x min-y (- max-x min-x) (- max-y min-y)))
(dolist (point polyline)
(when (or (null? min-x) (< (first point) min-x))
(setq min-x (first point)))
(when (or (null? min-y) (< (second point) min-y))
(setq min-y (second point)))
(when (or (null? max-x) (> (first point) max-x))
(setq max-x (first point)))
(when (or (null? max-y) (> (second point) max-y))
(setq max-y (second point)))))))
Die Funktion adjust-polylines verschiebt die Punkte aus einer Liste von Listen. Die Verschiebung wird anhand der Resultate von get-bounds so gewählt, dass keine Koordinate einen negativen Wert hat.
(defproc adjust-polylines (polylines min-x min-y)
(map-with
(lambda (polyline)
(map-with
(lambda (point)
(list
(- (first point) min-x)
(- (second point) min-y)))
polyline))
polylines))
Schließlich benutzt die Funktion draw-projected-polylines das Primitiv grid, um aus einer Liste von Listen von dreidimensionalen Punkten polylines-3d und den Parametern u, v, w und s eine Zeichnung zu erzeugen.
(defproc draw-projected-polylines (u v w s polylines-3d)
(let
((polylines-2d (project-polylines u v w s polylines-3d)))
(let
((bounds (get-bounds polylines-2d)))
(grid
(third bounds)
(fourth bounds)
(adjust-polylines polylines-2d (first bounds) (second bounds))))))
Wenn die Ebene E durch zwei orthogonale Einheitsvektoren u und v und einen Punkt w festgelegt wird, führt die obige Definition auf
T(P) = x u + y v + w und
T(P) = S + a (P - S),
wobei P, S, u, v und w gegeben sind und man T(P), x, y und a sucht. Das führt auf ein Gleichungssystem:
u1 x + v1 y + (S1 - P1) a = S1 - w1
u2 x + v2 y + (S2 - P2) a = S2 - w2
u3 x + v3 y + (S3 - P3) a = S3 - w3
Da u und v orthogonale Einheitsvektoren sind, kann man die Lösungen des Gleichungssystems in x und y zu einem Punkt (x y) kombinieren und diesen als Ergebnis der Projektion von P ins Zweidimensionale auffassen.
Die Realisierung verwendet die Funktion create-projection-matrix, um aus den Vektoren u und v und den Punkten s und p die Matrix des linearen Gleichungssystems aufzustellen.
(defproc create-projection-matrix (u v s p)
(new matrix 3 3
(list
(list (first u) (first v) (- (first s) (first p)))
(list (second u) (second v) (- (second s) (second p)))
(list (third u) (third v) (- (third s) (third p))))))
Der Spaltenvektor des linearen Gleichungssystems wird von der Funktion create-projection-column-vector in Abhängigkeit von w und s berechnet.
(defproc create-projection-column-vector (w s)
(new column-vector 3
(list
(- (first s) (first w))
(- (second s) (second w))
(- (third s) (third w)))))
Die Funktion project-point projiziert einen Punkt indem, wie oben beschrieben, das lineare Gleichungssystem gelöst wird (siehe Lineare Gleichungssysteme lösen). Die Lösung hat drei Elemente (x, y und a), von denen man das letzte nicht verwendet.
(defproc project-point (u v w s point)
(map-with
first
(butlast
(.
(solve-linear-equations
(create-projection-matrix u v s point)
(create-projection-column-vector w s))
elements))))
Die Funktion project-polylines wendet project-point auf die Punkte in einer Liste von Listen an.
(defproc project-polylines (u v w s polylines)
(map-with
(lambda (polyline)
(map-with
(curry (project-point u v w s _))
polyline))
polylines))
Die Funktion get-bounds bestimmt den rechteckigen Bereich, in dem sich die Punkte in einer Liste von Listen befinden.
(defproc get-bounds (polylines)
(let
((min-x nil) (min-y nil) (max-x nil) (max-y nil))
(dolist (polyline polylines (list min-x min-y (- max-x min-x) (- max-y min-y)))
(dolist (point polyline)
(when (or (null? min-x) (< (first point) min-x))
(setq min-x (first point)))
(when (or (null? min-y) (< (second point) min-y))
(setq min-y (second point)))
(when (or (null? max-x) (> (first point) max-x))
(setq max-x (first point)))
(when (or (null? max-y) (> (second point) max-y))
(setq max-y (second point)))))))
Die Funktion adjust-polylines verschiebt die Punkte aus einer Liste von Listen. Die Verschiebung wird anhand der Resultate von get-bounds so gewählt, dass keine Koordinate einen negativen Wert hat.
(defproc adjust-polylines (polylines min-x min-y)
(map-with
(lambda (polyline)
(map-with
(lambda (point)
(list
(- (first point) min-x)
(- (second point) min-y)))
polyline))
polylines))
Schließlich benutzt die Funktion draw-projected-polylines das Primitiv grid, um aus einer Liste von Listen von dreidimensionalen Punkten polylines-3d und den Parametern u, v, w und s eine Zeichnung zu erzeugen.
(defproc draw-projected-polylines (u v w s polylines-3d)
(let
((polylines-2d (project-polylines u v w s polylines-3d)))
(let
((bounds (get-bounds polylines-2d)))
(grid
(third bounds)
(fourth bounds)
(adjust-polylines polylines-2d (first bounds) (second bounds))))))
Projektion des Linienzugs mit den Ecken (0 0 0), (1 0 0), (1 1 0), (0 1 0), (0 0 0), (0 0 1), (1 0 1), (1 1 1), (0 1 1) und (0 1/5 1) mit u = (1 0 0), v = (0 1 0) und S = (-4 2 -20)
perspective-projection.sheet.xml