In the Python implementation of the Xiaolin Wu's line algorithm, a function is defined while being nested in another function. Part of the code:
def draw_line(img, p1, p2, color):
"""Draws an anti-aliased line in img from p1 to p2 with the given color."""
x1, y1 = p1
x2, y2 = p2
dx, dy = x2-x1, y2-y1
steep = abs(dx) < abs(dy)
p = lambda px, py: ((px,py), (py,px))[steep]
if steep:
x1, y1, x2, y2, dx, dy = y1, x1, y2, x2, dy, dx
if x2 < x1:
x1, x2, y1, y2 = x2, x1, y2, y1
grad = dy/dx
intery = y1 + _rfpart(x1) * grad
def draw_endpoint(pt):
x, y = pt
xend = round(x)
yend = y + grad * (xend - x)
xgap = _rfpart(x + 0.5)
px, py = int(xend), int(yend)
putpixel(img, p(px, py), color, _rfpart(yend) * xgap)
putpixel(img, p(px, py+1), color, _fpart(yend) * xgap)
return px
How does this work? Why didn't the programmer just define the code somewhere else and then call it in this function? If this function where to be called in a loop to draw multiple lines, wouldn't it be more efficient to just, as I said, define the nested function somewhere else?
try this:
class Humans():
def health(self, weight, height, age):
self.weight = weight
self.height = height
self.age = age
def health_health(self):
print(f'''This human is {self.age} years old; weighs {self.weight},
and has a height of {self.height}''')
Harar = Humans()
Harar.health(1,2,3)
Harar.health_health()
The reason why is because you are calling the function from the class definition, as opposed to the initialized object. You can do two things to fix this:
Call the function from the object that has been initialized:
class Foo:
def bar(x):
print(x)
a = Foo()
a.bar(10)
Or, define the function as a staticmethod:
class Foo:
#staticmethod
def bar(x):
print(x)
Foo.bar(10)
Both will output:
10
Related
I am trying to make a fill between two datasets of different length using Matplotlib in Python.
Datasets are as follows
x1 = [0.00137221, 0.01372213, 0.02607204, 0.03910806, 0.05351629,
0.07066895, 0.08713551, 0.10634648, 0.12761578, 0.14888508,
0.17221269, 0.19691252, 0.2271012 , 0.25797599, 0.28747856,
0.31766724, 0.34373928, 0.36569468, 0.38559177]
y1 = [1.03307393, 1.04661479, 1.05875486, 1.07182879, 1.08723735,
1.10544747, 1.11945525, 1.13299611, 1.14607004, 1.15540856,
1.15680934, 1.15680934, 1.15354086, 1.14513619, 1.13346303,
1.12085603, 1.10964981, 1.09891051, 1.08677043]
x2 = [0.00960549, 0.03773585, 0.06929674, 0.11595197, 0.15574614,
0.18113208, 0.20994854, 0.2380789 , 0.27101201]
y2 = [1.00645914, 1.02233463, 1.03821012, 1.05315175, 1.05688716,
1.05595331, 1.04894942, 1.04054475, 1.01579767]
I followed the procedure suggested here:
fill between two lines lacking common x values
xfill = np.linspace(0,0.4,10)
y1fill = np.interp(xfill, x1,y1)
y2fill = np.interp(xfill, x2,y2)
plt.plot(x1,y1)
plt.plot(x2,y2)
plt.fill_between(xfill, y1fill,y2fill,color = "lightgray")
When I apply the suggested code, I am getting wrong fill_between:
Using pgfplots in LaTeX I am getting somewhat that I want
You can add an extra start/end point with the terminal values of the outside line:
x_start = min(x1[0], x2[0])
x_end = max(x1[-1], x2[-1])
y_start = y1[0] if x_start == x1[0] else y2[0]
y_end = y1[-1] if x_end == x1[-1] else y2[-1]
xfill = np.linspace(x_start, x_end, 100)
y1fill = np.interp(xfill, np.r_[x_start, x1, x_end], np.r_[y_start, y1, y_end])
y2fill = np.interp(xfill, np.r_[x_start, x2, x_end], np.r_[y_start, y2, y_end])
plt.plot(x1,y1)
plt.plot(x2,y2)
plt.fill_between(xfill, y1fill, y2fill, color="lightgray")
Output:
As a function:
def fill_between_ends(x1, x2, y1, y2, **kwargs):
x_start = min(x1[0], x2[0])
x_end = max(x1[-1], x2[-1])
y_start = y1[0] if x_start == x1[0] else y2[0]
y_end = y1[-1] if x_end == x1[-1] else y2[-1]
xfill = np.linspace(x_start, x_end,100)
y1fill = np.interp(xfill, np.r_[x_start, x1, x_end], np.r_[y_start, y1, y_end])
y2fill = np.interp(xfill, np.r_[x_start, x2, x_end], np.r_[y_start, y2, y_end])
plt.fill_between(xfill, y1fill, y2fill, **kwargs)
plt.plot(x1,y1)
plt.plot(x2,y2)
fill_between_ends(x1, x2, y1, y2, color="lightgray")
I have this coupled mass system code that runs good and prints results. But I have trouble plotting the graphs for positions and velocities since I am unable to extract values from arrays. I would appreciate some help!
import numpy as np
%matplotlib inline
import matplotlib.pyplot as pl
from scipy.integrate import odeint
def vectorfield(w, t, p):
x1, y1, x2, y2 = w
m1, m2, k1, k2, kc = p
# Create f = (x1',y1',x2',y2'):
f = [y1, (-x1*(k1+kc) + x2*kc)/m1, y2, (x1*kc - x2*(k2+kc)) / m2]
return f
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.0
# Spring constants
k1 = 4.0
k2 = 1.0
kc = 0.1
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = -2.0
y1 = 5.0
x2 = 2.0
y2 = 10.0
# ODE solver parameters
abserr = 1.0e-8
relerr = 1.0e-6
stoptime = 100.0
numpoints = 250
t = [stoptime * float(i) / (numpoints - 1) for i in range(numpoints)]
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, kc]
w0 = [x1, y1, x2, y2]
# Call the ODE solver.
wsol = odeint(vectorfield, w0, t, args=(p,), atol=abserr, rtol=relerr)
# Print solution
for t1, w1 in zip(t, wsol):
AZ = [t1, w1[0], w1[1], w1[2], w1[3]]
print(AZ)
I have tried searching the web but wasnt unable to find a fitting solution to plot this. I tried
with open('coupled_masses.dat', 'w') as f:
for t1, w1 in zip(t, wsol):
print(f, t1, w1[0], w1[1], w1[2], w1[3])
import matplotlib.pyplot as plt;
from matplotlib.font_manager import FontProperties;
# get saved values from saved file
t, x1, y1, x2, y2 = np.loadtxt('coupled_masses.dat', unpack=True);
but it doesnt work
Is this what you want? Using list comprehension here and then convert to numpy array.
from scipy.integrate import odeint
def vectorfield(w, t, p):
x1, y1, x2, y2 = w
m1, m2, k1, k2, kc = p
# Create f = (x1',y1',x2',y2'):
f = [y1, (-x1*(k1+kc) + x2*kc)/m1, y2, (x1*kc - x2*(k2+kc)) / m2]
return f
# Parameter values
# Masses:
m1 = 1.0
m2 = 1.0
# Spring constants
k1 = 4.0
k2 = 1.0
kc = 0.1
# Initial conditions
# x1 and x2 are the initial displacements; y1 and y2 are the initial velocities
x1 = -2.0
y1 = 5.0
x2 = 2.0
y2 = 10.0
# ODE solver parameters
abserr = 1.0e-8
relerr = 1.0e-6
stoptime = 100.0
numpoints = 250
t = [stoptime * float(i) / (numpoints - 1) for i in range(numpoints)]
# Pack up the parameters and initial conditions:
p = [m1, m2, k1, k2, kc]
w0 = [x1, y1, x2, y2]
# Call the ODE solver.
wsol = odeint(vectorfield, w0, t, args=(p,), atol=abserr, rtol=relerr)
# Print solution
data = np.array([[t1, w1[0], w1[1], w1[2], w1[3]] for t1, w1 in zip(t, wsol)])
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7.2, 7.2/2))
ax1.plot(data[:, 0], data[:, 1])
ax2.plot(data[:, 0], data[:, 3])
UNEXPOSED = '~'
POKEMON = "☺"
FLAG = "♥"
EXPOSED = "0"
possible_adjacent_amount = ["0", "1", "2" "3", "4", "5", "6", "7", "8"]
def draw_board(self, board):
self.delete()
index = 0
print(board)
for row in range(self._grid_size):
for column in range(self._grid_size):
x1 = column*self._cell_size
y1 = row * self._cell_size
x2 = x1 + self._cell_size
y2 = y1 + self._cell_size
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
#bounding_box = ((int (x1), int (y1)), (int (x2), int (y2)))
centre_pixel = (x1 + (x2 - x1) // 2, y1 + (y2 - y1) // 2)
self.pos_to_pixel[(row, column)] = centre_pixel
#tp = self.create_text((100, 100), text='1')
print(board[index])
if board[index] ==UNEXPOSED:
self.create_rectangle (x1, y1, x2, y2, fill="dark green", tags="rect")
if board[index] in possible_adjacent_amount :
# TODO why doesn't this work...?
self.create_text(self.position_to_pixel ((row, column)), text = board[index])
self.create_rectangle (x1, y1, x2, y2, fill="light green", tags="rect")
if board[index] == FLAG:
self.create_rectangle (x1, y1, x2, y2, fill="red", tags="rect")
if board[index] == POKEMON:
self.create_text ((centre_pixel), text=board[index])
self.create_rectangle (x1, y1, x2, y2, fill="yellow", tags="rect")
index += 1
The board input will be something like 0~~~1~~12~~3
The idea is that it will take the board index and then create text at the center pixel of the board index that will be the numbers which in this case would be 0, 1, 1, 2, 3
You if you want the numbers to be drawn on the rectangles you need to create the text after you create the rectangles otherwise you will create text behind the rectangles.
I have 3 points p1(x1, y1), p2(x2, y2) and p3(x3, y3). I am trying to calculate angle (in anti-clockwise direction) between these 3 points. I am using following dot product method as provided in multiple blogs and SE sites (like this).
def angle_between(p1, p2, p3):
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
v21 = (x1 - x2, y1 - y2)
v23 = (x3 - x2, y3 - y2)
dot = v21[0] * v23[0] + v21[1] * v23[1]
det = v21[0] * v23[1] - v21[1] * v23[0]
theta = np.rad2deg(np.arctan2(det, dot))
print(theta)
It is giving me correct angle for any points which are not on the straight line. For example
p1 = (0, 0)
p2 = (1, 0)
p3 = (1, 1)
angle_between(p1, p2, p3) # Prints -90
angle_between(p3, p2, p1) # Prints +90
However, if points are on the straight line, it is giving me same answer
p1 = (0, 0)
p2 = (1, 0)
p3 = (2, 0)
angle_between(p1, p2, p3) # Prints +180
angle_between(p3, p2, p1) # Prints +180
Here I was expecting (p3, p2, p1) to give -180. What am I missing here? If the method I am using is not correct, can someone help me point towards the correct method?
I have tried to use direct cosine law (as given here) but it only provides me angle without any sense of direction of the angle.
Check out this solution. It always provides positive angles, measured in anti-clockwise direction:
from math import atan2, degrees
def angle_between(p1, p2, p3):
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
deg1 = (360 + degrees(atan2(x1 - x2, y1 - y2))) % 360
deg2 = (360 + degrees(atan2(x3 - x2, y3 - y2))) % 360
return deg2 - deg1 if deg1 <= deg2 else 360 - (deg1 - deg2)
Is there a way to make a function that just adds motion to an object like the motion_add function from Game Maker?
Like: motion_add(speed1, direction1, speed2, direction2)
... where speed1 and direction1 is the current values of the object and
speed2 and direction2 is the additive.
I've figured it out myself.
def CoordsToDir(x1, y1, x2, y2):
return atan2(y2 - y1, x2 - x1)*180/pi
def DirToCoords(direction, length):
return [cos((direction) * pi / 180) * length, sin((direction) * pi / 180) * length]
def motion_add(speed1, direction1, speed2, direction2):
[x1, y1] = DirToCoords(direction1, speed1)
[x2, y2] = map(sum, zip([x1, y1], DirToCoords(direction2, speed2)))
speed = hypot(x2, y2)
direction = CoordsToDir(0, 0, x2, y2)
return [speed, direction]