Παρακάτω φτιάχνουμε διάφορες μικρές συναρτήσεις για πράξεις ανάμεσα σε διανύσματα, συναρτήσεις που χρησιμοποιούμε μετά στο σχεδιασμό διαφόρων σχημάτων.
Τα διανύσματα για μας είναι, κατ' αρχήν, ζεύγη αριθμών (λίστες μήκους 2) όσον αφορά διανύσματα στις 2 διαστάσεις ή τριάδες αριθμών (λίστες μήκους 3) όσον αφορά διανύσματα στις 3 διαστάσεις. Μιλάμε δηλ. για λίστες της μορφής [x, y]
ή [x, y, z]
. Θα καταβάλλουμε προσπάθεια οι συναρτήσεις μας να δουλεύουν και για τις δύο περιπτώσεις (και ακόμη και για διανύσματα σε μεγαλύτερη διάσταση) εκτός φυσικά όπου αυτό δεν έχει νόημα (π.χ. το εξωτερικό γινόμενο διανυσμάτων ορίζεται μόνο για 3-διάστατα διανύσματα και η περιστροφή διανύσματος γύρω από την αρχή των αξόνων κατά γωνία θ ορίζεται μόνο για 2-διάστατα διανύσματα).
Όλες οι συναρτήσεις παρακάτω που το όνομά τους αρχίζει με V
είναι συναρτήσεις για πράξεις σε διανύσματα. Στα σχόλια που φαίνονται παρακάτω περιγράφονται λεπτομερώς.
Έπειτα δίνουμε 3 διαφορετικές μεθόδους υπολογισμού του εμβαδού ενός τριγώνου, όπου οι κορυφές του τριγώνου είναι τα διανύσματα v, w, z
. Στην πρώτη συνάρτηση TriangleArea
χρησιμοποιούμε τον τύπο βάση επί ύψος διά 2, στη δεύτερη συνάρτηση TriangleAreaNew
χρησιμοποιούμε τον τύπο του Ήρωνα και στην τρίτη συνάρτηση TriangleAreaVeryNew
χρησιμοποιούμε τον τύπο με το εξωτερικό γινόμενο διανυσμάτων (δείτε τύπο (10) εδώ. Επειδή υλοποιούμε και τις τρεις αυτές συναρτήσεις αμιγώς με τις παραπάνω συναρτήσεις που φτιάξαμε, οι οποίες δουλεύουν και για 2-διάστατα και για 3-διάστατα διανύσματα, έχουμε το πολύ επιθυμητό γεγονός ότι και οι τρεις αυτές συναρτήσεις δουλεύουν ακόμη και στις 3 διαστάσεις (3 μη συνευθειακά σημεία στο χώρο πάντα ορίζουν ένα επίπεδο και ένα τρίγωνο). Στο τέλος του προγράμματος δοκιμάζουμε και τις 3 συναρτήσεις σε ένα τρίγωνο και παρατηρούμε ότι παίρνουμε το ίδιο αποτέλεσμα.
import math
import matplotlib.pyplot as plt
def plot_polygon(L): # Η συνάρτηση αυτή έχει γραφεί σε προηγούμενη διάλεξη.
# Εδώ έχουμε μια απλουστευμένη μορφή της.
"""
L είναι μια λίστα ζευγών (λίστες μήκους δύο, [x, y]) και ζωγραφίζουμε
το κλειστό πολύγωνο που ορίζουν οι κορυφές αυτές.
"""
x = [pair[0] for pair in L] + [ L[0][0] ]
y = [pair[1] for pair in L] + [ L[0][1] ]
plt.plot(x, y)
def Vadd(v, w):
"""
Επιστρέφει το διάνυσμα v+w
"""
ret = []
for i,x in enumerate(v):
ret.append(x+w[i])
return ret
def Vneg(v):
"""
Επιστρέφει το αντίθετο διάνυσμα, -v
"""
ret = []
for x in v:
ret.append(-x)
return ret
def Vrotate(theta, v):
"""
Επιστρέφει το v περιστραμμένο κατά theta γύρω από την αρχή των αξόνων, (0,0).
Εδώ το v πρέπει να είναι διδιάστατο διάνυσμα.
"""
[x, y] = v # παίρνουμε τις συντεταγμένες του v και πολλαπλασιάζουμε με τον πίνακα περιστροφής
return [ math.cos(theta)*x-math.sin(theta)*y, math.sin(theta)*x + math.cos(theta)*y ]
def Vmul(t, v):
"""
Επιστρέφει το διάνυσμα v πολλαπλασιασμένο με τον αριθμό t
"""
ret = []
for x in v:
ret.append(t*x)
return ret
def Vinner(v, w):
"""
Επιστρέφει το εσωτερικό γινόμενο των v, w
"""
s = 0.
for i,x in enumerate(v):
s += x*w[i]
return s
def Vcross(v, w):
"""
Επιστρέφει το εξωτερικό γινόμενο των 3-διάστατων διανυσμάτων v, w.
Αν τα v, w είναι 2-διάστατα τους προσθέτει ένα μηδενικό στο τέλος.
"""
vv = v[:]; ww = w[:] # δημιουργεί αντίγραφα των v, w
if len(vv)==2:
vv.append(0.)
if len(ww)==2:
ww.append(0.)
return [ vv[1]*ww[2]-vv[2]*ww[1],
-( vv[0]*ww[2]-vv[2]*ww[0]),
vv[0]*ww[1]-vv[1]*ww[0] ]
def TriangleArea(v, w, z):
"""
Επιστρέφει το εμβαδό του τριγώνου με κορυφές τα διανύσματα v, w, z.
Χρησιμοποιεί τον τύπο βάση x ύψος / 2, και υπολογίζει τα δύο αυτά ύψη με γεωμετρικές πράξεις
χρησιμοποιώντας τις παραπάνω πράξεις που υλοποιήσαμε ως συναρτήσεις.
"""
vw = Vadd(w, Vneg(v)) # vw = w -v
lvw = math.sqrt(Vinner(vw, vw)) # μήκος του vw.
u = Vmul(1./lvw, vw) # μοναδιαίο διάνυσμα παράλληλο με το vw
vz = Vadd(z, Vneg(v)) # vz = z -v
x = Vadd(v, Vmul( Vinner(vz, u), u )) # το x είναι η ορθή προβολή του z στην πλευρά vw
xz = Vadd(z, Vneg(x)) # xz είναι το διάνυσμα από την κορυφή z στο ίχνος του ύψους της, x
lxz = math.sqrt(Vinner(xz,xz)) # το μήκος της xz είναι το ύψος που αντιστοιχεί στην πλευρά vw
return lvw*lxz*0.5
def TriangleAreaNew(v, w, z):
"""
Επιστρέφει το εμβαδό του τριγώνου με κορυφές τα διανύσματα v, w, z
με τον τύπο του Ήρωνα
"""
vw = Vadd(w, Vneg(v)) # το διάνυσμα vw = w - v
lvw = math.sqrt(Vinner(vw, vw)) # μήκος του vw
wz = Vadd(z, Vneg(w)) # το διάνυσμα wz = z - w
lwz = math.sqrt(Vinner(wz, wz)) # μήκος του wz
zv = Vadd(v, Vneg(z)) # το διάνυσμα zv = v - z
lzv = math.sqrt(Vinner(zv, zv)) # μήκος του zv
s = (lvw+lwz+lzv)/2. # ημιπερίμετρος του τριγώνου
return math.sqrt(s*(s-lvw)*(s-lwz)*(s-lzv)) #ο τύπος του Ήρωνα
def TriangleAreaVeryNew(v, w, z):
"""
Επιστρέφει το εμβαδό του τριγώνου με κορυφές τα διανύσματα v, w, z
με εξωτερικό γινόμενο
"""
vw = Vadd(w, Vneg(v))
vz = Vadd(z, Vneg(v))
cross = Vcross(vz, vw) # cross είναι το εξωτερικό γινόμενο των vz, vw
l = math.sqrt(Vinner(cross, cross)) # το μήκος του cross
return 0.5*l
def SquareOnSide(v, w):
"""
Ζωγραφίζει τετράγωνο με πλευρά vw, δεξιά όπως κοιτάμε από v στο w.
Τα v, w πρέπει να είναι διδιάστατα διανύσματα.
"""
vw = Vadd(w, Vneg(v))
vx = Vrotate(-math.pi/2., vw)
x = Vadd(vx, v)
y = Vadd(x, vw)
plot_polygon([ v, w, y, x])
# Παρακάτω δοκιμάζουμε και τις τρεις μεθόδους υπολογισμού εμβαδού που γράψαμε στο τρίγωνο με τις κορυφές v, w, z
# που φαίνονται. Παίρνουμε το ίδιο αποτέλεσμα.
v = [2,1]
w = [3,2]
z = [-1,1]
print "Area is", TriangleArea(v, w, z)
print "Area is", TriangleAreaNew(v, w, z)
print "Area is", TriangleAreaVeryNew(v, w, z)
# Ζωγραφίζουμε το ορθογώνιο τρίγωνο A, B, C και ένα τετράγωνο σε κάθε πλευρά του,
# ένα σχήμα που βλέπουμε πολύ συχνά να συνοδεύει το Πυθαγόρειο θεώρημα.
A = [0, 0]
B = [1, 0]
C = [0, 3]
plot_polygon([A, B, C])
SquareOnSide(A, B)
SquareOnSide(B, C)
SquareOnSide(C, A)
plt.axes().set_aspect('equal')
plt.show()