1. more trig (and basic vectors) 2. inheritance (chapter 9) lecture 10 (last one!)
TRANSCRIPT
1. MORE TRIG (AND BASIC VECTORS)2. INHERITANCE (CHAPTER 9)
Lecture 10 (Last one!)
Part I: More Trig
Recall: Angles (orientation and rotation) Polar vs. Cartesian Coordinates (and offsets) Polar => Cartesian conversion for “asteroids-style”
movement (vs. normal “pacman” style movement)What we still need
Cartesian => Polar conversion (and applications) Generalize the idea of a vector (and look at a few
properties) Define acceleration and velocity and their
applications.
Cartesian => Polar coordinatesMotivation:
Control orientation with the mouse.
Previously we knew H and θ; We wanted to calculate Δx and Δy Δx is the amount to change our x-position. Δy is the amount to change our y-position.
Now, however, want to calculate θ (from Δx and Δy)
X
rotSurf = pygame.transform.rotate( origSurf, degrees)
Cartesian => Polar, cont.
θ
A
O
H X
We know initially:• The position of the plane (x,
y)• The position of the mouse
(mx, my) We want to calculate:
• The angle θ
First step: We can calculate A and O below using our givens:
• A = mx – x• O = my - y• Note: it does make a
difference which order you do the sub.• Think of both as
directions• negative = left.• positive = right.• Similar for O
Second step: (we won't actually need it here) We can calculate H as well:
Note: for step 3, we could've also calculated theta as: or
Third step: look at the trig identities:
In each, we now know two of the three variables. The only unknown is now θ. We can use the inverse trig functions to solve for θ.
So…
Example Cartesian => Polar (here using arcsin) Givens:
Plane at (50, 500) Target at (200, 150)
Step1: Calculate offsets towards target Note how we take destination minus
source Step2: Calculate hypotenuse
length Step3: Calculate theta
θ =
200-50 = +150
150-500= -350
H =
(50,500)
(200,150)
Cartesian => Polar, Problem with asin (and acos)
θ
200-50 = +150
150-500= -350
H1 =
(50,500)
(200,150)
θ=𝑠𝑖𝑛− 1(350381
)≈67°
φ
(-100)-50 = -150
150-500= -350
H2 =
(50,500)
(-100,150)
φ=𝑠𝑖𝑛−1(350381
)≈67°
WRONG! ψ should be ≈ 113 degreesAsin ignores the sign of the horizontal direction.
Solution:
δ
If we're in quadrant II or III, we need the complement of ψ (180 – ψ) .
Cartesian=>Polar, problem with atan.Atan has problems too…
Problem1: If Δx is 0…
(when?) we'll have a zero-division theta is undefined.
Solution: If Δx = 0 and Δy > 0, θ = 90 degrees If Δx = 0 and Δy < 0, θ = -90 degrees
Problem2: Say Δx = 150 and Δy = -350 (as on the prev. slide) θ = degrees …correct. But… Consider the target point (-100, 850)
Δx = -150 and Δy = 350 degrees WRONG (should be 67+180 degrees) Solution: look at the sign of Δx and Δy to determine the quadrant, handle it
appropriately. Or…
Cartesian => Polar
Because this is such a common problem, math libraries (like python's math module) include this:
This includes logic to handle the quadrants and "poles" correctly. Internally, it just uses a "regular" inverse trig function
+ some extra logic.
Conversions finished
Polar => Cartesian We know distance and θ We want Δx and Δy
Cartesian => Polar We know Δx and Δy We want distance and θ
• Note: The red negative sign is to compensate for pygame's inverted y-axis.
Example
[Making object point towards a point]
Part III. Inheritance Overview
Basically, basing a new (derived) class from an existing one
(base) . You "inherit" all methods (and attributes) from the base
class. You then:
Add new methods Replace existing methods
Although you can still call the base class's version
Abstract example
class Base(object):def __init__(self, x):
self.num = xdef foo(self, string):
return string * self.num
class Derived(Base):def bar(self):
return self.num ** 2def foo(self, string): # REPLACES "inherited" foo
return string * self.bar()
A = Derived(4) # Note, we're using Base's __init__ method.print(A.foo("ABC")) # Calling the replaced methodprint(A.bar()) # Call a new method.
One problem: If we want to add new attributes to Derived…
Calling (hidden) base class methods.
class A(object):def f(self):
print("A.f")
class B(A):def f(self):
print("B.f")
obj = B()obj.f() # Print's B.fB.f(obj) # Same as above
It appears as if we've replaced A's version of f, but it's actually just hidden.
Calling (hidden) base class methods, cont.
class A(object):def f(self):
print("A.f")
class B(A):def f(self):
A.f(self) # Calls the "hidden" f.print("B.f")
obj = B()obj.f() # Print's 'A.f' then 'B.f'
When you call base class methods like this, it is the only time self isn't passed automatically.
Adding attributes to a new class
class Base(object):def __init__(self, x):
self.num = xdef foo(self, string):
return string * self.num
class Derived(Base):def __init__(self, x, y):
Base.__init__(self, x)self.num2 = y
def bar(self):return self.num ** 2 + self.num2 ** 2
A = Derived(4, 6) # Calling the new __init__ method.print(A.foo("ABC")) # Calling the inherited methodprint(A.bar()) # Call a new method.
isinstance function
Used to determine if an object is of a given type
isinstance(5, int) # Trueisinstance("5", int) # Falseisinstance("5", str) # Trueclass Foo(object):
…x = Foo()isinstance(x, Foo) # True
isinstance and inheritance
A derived class object is considered: an instance of the derived class (no surprise) an instance of the base class
class A(object):…
class B(A):…
x = A()y = B()isinstance(x, A) # Trueisinstance(x, B) # Falseisinstance(y, B) # Trueisinstance(y, A) # True
Example
[Make the spaceship into a base class and add enemies that fire on the player]