I'm trying to solve the tasks (text below), but I have a problem with the second point, i.e. method in it that displays the length of the segment and the positions of the start and end points.-
I don't really know how to write it, Could someone look at the code and give some hints?
Define a Point class with x, y fields and a method displaying the
point's position (eg "point (2,3)").
Then create a class Segment that will inherit from the class point.
Create a method in it that displays the length of the segment and the
positions of the start and end points.
Then define the Triangle class which will contain 3 Points,
automatically determined 3 Sections (walls) of them and included a
method for displaying the surface area of the perimeter.
code:
from math import sqrt, hypot
class Point:
def __init__(self, x_init, y_init):
self.x = x_init
self.y = y_init
def __str__(self):
return "Point(%s,%s)"%(self.x, self.y)
class Segment(Point):
def distance(self): **!-probably a badly written method**
return ((self.x ** 2) + (self.y ** 2)) ** 0.5
def position(self, p): **!-probably a badly written method**
dx = self.x - p.X
dy = self.y - p.Y
return hypot(dx, dy)
class Triangle(Point):
def __init__(self, x, y, z):
Point.__init__(self, x, y)
self.z = z
def __str__(self):
return "Point(x %s ,y %s, z %s )" % (self.x, self.y, self.z)
def __area__(a, b, c):
s = (a + b + c) / 2
return (s * (s - a) * (s - b) * (s - c)) ** 0.5
def __perimeter__(a, b, c):
s = (a + b + c)
return s
Task 2 is just wrong. Segment needs to contain 2 points, so it can't inherit from Point. It should be:
class Segment:
def __init__(self, start, end):
self.start = start
self.end = end
def distance(self):
dist = ((self.start.x - self.end.x) ** 2 + (self.start.y - self.end.y) ** 2) ** 0.5
print(f"distance from {self.start} to {self.end} is {dist}"
Related
I am attempting to create a program which simulates particles colliding in a 2D box, but each particle is labeled with a random 5 character string name and each collision is tracked in a list along each particle. So after the simulation, I would like a list from each particle listing which particles it has hit. I have forked this great simulation https://github.com/xnx/collision and added the name and history attributes to the particle class. However, whenever I attempt to access .name or .history, my kernel dies. The output says:
Kernel died, restarting
Restarting kernel...
The failure happens in the handle_collisions function (line 197), or whenever I try to access the history or the name, so there must be something wrong in my implementation of the name and history attributes. I have also tried to instantiate name and history in the init_particles function instead of place_particles but that had the same results. I'm not exactly sure how to correctly implement them. Thanks for your help.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib import animation
from itertools import combinations
import random
import string
class Particle:
"""A class representing a two-dimensional particle."""
def __init__(self, x, y, vx, vy, name, history, radius=0.01, styles=None):
"""Initialize the particle's position, velocity, name, history and radius.
Any key-value pairs passed in the styles dictionary will be passed
as arguments to Matplotlib's Circle patch constructor.
"""
self.r = np.array((x, y))
self.v = np.array((vx, vy))
self.radius = radius
self.mass = self.radius**2
self.styles = styles
if not self.styles:
# Default circle styles
self.styles = {'edgecolor': 'b', 'fill': False}
# For convenience, map the components of the particle's position and
# velocity vector onto the attributes x, y, vx and vy.
#property
def x(self):
return self.r[0]
#x.setter
def x(self, value):
self.r[0] = value
#property
def y(self):
return self.r[1]
#y.setter
def y(self, value):
self.r[1] = value
#property
def vx(self):
return self.v[0]
#vx.setter
def vx(self, value):
self.v[0] = value
#property
def vy(self):
return self.v[1]
#vy.setter
def vy(self, value):
self.v[1] = value
#property
def history(self):
return self.history
#history.setter
def history(self,value):
self.history=value
#property
def name(self):
return self.name
#name.setter
def name(self,value):
self.name=value
def overlaps(self, other):
"""Does the circle of this Particle overlap that of other?"""
return np.hypot(*(self.r - other.r)) < self.radius + other.radius
def draw(self, ax):
"""Add this Particle's Circle patch to the Matplotlib Axes ax."""
circle = Circle(xy=self.r, radius=self.radius, **self.styles)
ax.add_patch(circle)
return circle
def advance(self, dt):
"""Advance the Particle's position forward in time by dt."""
self.r += self.v * dt
class Simulation:
"""A class for a simple hard-circle molecular dynamics simulation.
The simulation is carried out on a square domain: 0 <= x < 1, 0 <= y < 1.
"""
ParticleClass = Particle
def __init__(self, n, radius=0.01, styles=None):
"""Initialize the simulation with n Particles with radii radius.
Each particle is initialized with a 10 letter string name and an empty history.
radius can be a single value or a sequence with n values.
Any key-value pairs passed in the styles dictionary will be passed
as arguments to Matplotlib's Circle patch constructor when drawing
the Particles.
"""
self.init_particles(n, radius, styles)
self.dt = 0.01
def place_particle(self, rad, styles):
# Choose x, y so that the Particle is entirely inside the
# domain of the simulation.
x, y = rad + (1 - 2*rad) * np.random.random(2)
# Choose a random velocity (within some reasonable range of
# values) for the Particle.
vr = 0.1 * np.sqrt(np.random.random()) + 0.05
vphi = 2*np.pi * np.random.random()
vx, vy = vr * np.cos(vphi), vr * np.sin(vphi)
name = self.assignname
history = []
particle = self.ParticleClass(x, y, vx, vy, name, history, rad, styles)
# Check that the Particle doesn't overlap one that's already
# been placed.
for p2 in self.particles:
if p2.overlaps(particle):
break
else:
self.particles.append(particle)
return True
return False
def assignname(self):
letters = string.ascii_lowercase
name=''.join(random.choice(letters) for i in range(5))
return name
def init_particles(self, n, radius, styles=None):
"""Initialize the n Particles of the simulation.
Positions and velocities are chosen randomly; radius can be a single
value or a sequence with n values.
"""
try:
iterator = iter(radius)
assert n == len(radius)
except TypeError:
# r isn't iterable: turn it into a generator that returns the
# same value n times.
def r_gen(n, radius):
for i in range(n):
yield radius
radius = r_gen(n, radius)
self.n = n
self.particles = []
for i, rad in enumerate(radius):
# Try to find a random initial position for this particle.
while not self.place_particle(rad, styles):
pass
def change_velocities(self, p1, p2):
"""
Particles p1 and p2 have collided elastically: update their
velocities.
"""
m1, m2 = p1.mass, p2.mass
M = m1 + m2
r1, r2 = p1.r, p2.r
d = np.linalg.norm(r1 - r2)**2
v1, v2 = p1.v, p2.v
u1 = v1 - 2*m2 / M * np.dot(v1-v2, r1-r2) / d * (r1 - r2)
u2 = v2 - 2*m1 / M * np.dot(v2-v1, r2-r1) / d * (r2 - r1)
p1.v = u1
p2.v = u2
def handle_collisions(self):
"""Detect and handle any collisions between the Particles.
When two Particles collide, they do so elastically: their velocities
change such that both energy and momentum are conserved.
"""
# We're going to need a sequence of all of the pairs of particles when
# we are detecting collisions. combinations generates pairs of indexes
# into the self.particles list of Particles on the fly.
#particles share history when they collide
pairs = combinations(range(self.n), 2)
for i,j in pairs:
if self.particles[i].overlaps(self.particles[j]):
self.change_velocities(self.particles[i], self.particles[j])
#FAILS HERE
#self.particles[i].history.append(self.particles[j].name)
#self.particles[j].history.append(self.particles[i].name)
def handle_boundary_collisions(self, p):
"""Bounce the particles off the walls elastically."""
if p.x - p.radius < 0:
p.x = p.radius
p.vx = -p.vx
if p.x + p.radius > 1:
p.x = 1-p.radius
p.vx = -p.vx
if p.y - p.radius < 0:
p.y = p.radius
p.vy = -p.vy
if p.y + p.radius > 1:
p.y = 1-p.radius
p.vy = -p.vy
def apply_forces(self):
"""Override this method to accelerate the particles."""
pass
def advance_animation(self):
"""Advance the animation by dt, returning the updated Circles list."""
for i, p in enumerate(self.particles):
p.advance(self.dt)
self.handle_boundary_collisions(p)
self.circles[i].center = p.r
self.handle_collisions()
self.apply_forces()
return self.circles
def advance(self):
"""Advance the animation by dt."""
for i, p in enumerate(self.particles):
p.advance(self.dt)
self.handle_boundary_collisions(p)
self.handle_collisions()
self.apply_forces()
def init(self):
"""Initialize the Matplotlib animation."""
self.circles = []
for particle in self.particles:
self.circles.append(particle.draw(self.ax))
return self.circles
def animate(self, i):
"""The function passed to Matplotlib's FuncAnimation routine."""
self.advance_animation()
return self.circles
def setup_animation(self):
self.fig, self.ax = plt.subplots()
for s in ['top','bottom','left','right']:
self.ax.spines[s].set_linewidth(2)
self.ax.set_aspect('equal', 'box')
self.ax.set_xlim(0, 1)
self.ax.set_ylim(0, 1)
self.ax.xaxis.set_ticks([])
self.ax.yaxis.set_ticks([])
def save_or_show_animation(self, anim, save, filename='collision.mp4'):
if save:
Writer = animation.writers['ffmpeg']
writer = Writer(fps=10, bitrate=1800)
anim.save(filename, writer=writer)
else:
plt.show()
def do_animation(self, save=False, interval=1, filename='collision.mp4'):
"""Set up and carry out the animation of the molecular dynamics.
To save the animation as a MP4 movie, set save=True.
"""
self.setup_animation()
anim = animation.FuncAnimation(self.fig, self.animate,
init_func=self.init, frames=800, interval=interval, blit=True)
self.save_or_show_animation(anim, save, filename)
if __name__ == '__main__':
nparticles = 20
radii = .02
styles = {'edgecolor': 'C0', 'linewidth': 2, 'fill': None}
sim = Simulation(nparticles, radii, styles)
sim.do_animation(save=False)
I see two immediate problems in your code.
First: you did add history as a Particle.__init__ parameter but you never initialize the property itself.
Add something like this:
def __init__(self, x, y, vx, vy, name, history, radius=0.01, styles=None):
self._history = history
And bigger problem: you have an infinite recursion in your #property definition:
#property
def history(self):
return self.history
#history.setter
def history(self,value):
self.history=value
So in your getter you called history you call itself return self.history which will loop itself until program will crash.
Rename internal property to _history:
#property
def history(self):
return self._history
#history.setter
def history(self,value):
self._history=value
The questions asks to "Write a method add_point that adds the position of the Point object given as an argument to the position of self". So far my code is this:
import math
epsilon = 1e-5
class Point(object):
"""A 2D point in the cartesian plane"""
def __init__(self, x, y):
"""
Construct a point object given the x and y coordinates
Parameters:
x (float): x coordinate in the 2D cartesian plane
y (float): y coordinate in the 2D cartesian plane
"""
self._x = x
self._y = y
def __repr__(self):
return 'Point({}, {})'.format(self._x, self._y)
def dist_to_point(self, other):
changex = self._x - other._x
changey = self._y - other._y
return math.sqrt(changex**2 + changey**2)
def is_near(self, other):
changex = self._x - other._x
changey = self._y - other._y
distance = math.sqrt(changex**2 + changey**2)
if distance < epsilon:
return True
def add_point(self, other):
new_x = self._x + other._x
new_y = self._y + other._y
new_point = new_x, new_y
return new_point
However, I got this error message:
Input: pt1 = Point(1, 2)
--------- Test 10 ---------
Expected Output: pt2 = Point(3, 4)
Test Result: 'Point(1, 2)' != 'Point(4, 6)'
- Point(1, 2)
? ^ ^
+ Point(4, 6)
? ^ ^
So I'm wondering what is the problem with my code?
Your solution returns a new tuple without modifying the attributes of the current object at all.
Instead, you need to actually change the object's attributes as per the instructions and don't need to return anything (ie, this is an "in-place" operation).
def add_point(self, other):
self._x += other._x
self._y += other._y
import math
class Vector:
def __init__(self,x,y):
self.x= x
self.y =y
def add(self,other):
new_x = self.x + other.x
new_y = self.y + other.y
return Vector(new_x,new_y)
def subtract(self,other):
new_x = self.x - other.x
new_y = self.y - other.y
return Vector(new_x,new_y)
def scale(self,factor):
new_x = self.x * factor
new_y = self.y * factor
return Vector(new_x,new_y)
def length(self,other):
r_squared = self.x ** 2 + self.y **2
return Vector(r_squared)
I've been trying to test this code that I was given, how am I able to test this using some numbers so that I am able to learn to understand what each function in this code actually does. I am able to see what it does from looking at the code but I also want to reassure that what I am predicting it to do is actually what it does.
Thank you in advance!
Add a checker for your code at the very end of yor file:
if __name__=="__main__":
vec1 = Vector(0, 0)
vec2 = Vector(2,2)
vec3 = vec1.add(vec2)
print(vec1, vec2, vec3)
#add other tests
You could add an override to a built in function in the Vector class for printing instances in a human readable way.
def __repr__(self):
return 'Vector: ({}, {})'.format(self.x, self.y)
Then you might want to fix the length function. It should only return a number and not another Vector. Additionally, it should return the square root of the sum. For example the vector (3, 4) should have a length of 5, not 25. Also, the length method does not need vector supplied as a param.
Once these are fixed up you can add this to the bottom of the file and run the script in the terminal like so: python vec.py
if __name__ == '__main__':
v1 = Vector(0,0)
v2 = Vector(3,4)
print('v1', v1)
print('v2', v2)
print('v1 + v2', v1.add(v2))
print('v2.length', v2.length())
G'day! When I know the slope and y-intercept of a line, I need to calculate an x-value that is 1 unit out from the line.
For example, if pointA = (4,5), and I set a line going from it with 0 slope (and therefore 5 as the y-intercept), then the x value I want would be 5. If the slope were undefined (vertical), then the x value would be 4. And so on.
So far, I calculate x as x = m(point[0]+1)-b. This doesn't work so well for vertical lines, however.
This and this are similar, but I can't read C# for the first, and on the second one, I don't need to eliminate any possible points (yet).
This is kind of hitting a nail with a sledge hammer, but if you're going to be running into geometry problems often, I'd either write or find a Point/Vector class like
import math
class Vector():
def __init__(self, x=0.0, y=0.0, z=0.0):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
self.x += other.x
self.y += other.y
self.z += other.z
return self
def __sub__(self, other):
self.x -= other.x
self.y -= other.y
self.z -= other.z
return self
def dot(self, other):
return self.x*other.x + self.y*other.y + self.z*other.z
def cross(self, other):
tempX = self.y*other.z - self.z*other.y
tempY = self.z*other.x - solf.x*other.z
tempZ = self.x*other.y - self.y*other.x
return Vector(tempX, tempY, tempZ)
def dist(self, other):
return math.sqrt((self.x-other.x)**2 + (self.y-other.y)**2 + (self.z-other.z)**2)
def unitVector(self):
mag = self.dist(Vector())
if mag != 0.0:
return Vector(self.x * 1.0/mag, self.y * 1.0/mag, self.z * 1.0/mag)
else:
return Vector()
def __repr__(self):
return str([self.x, self.y, self.z])
Then you can do all kinds of stuff like find the vector by subtracting two points
>>> a = Vector(4,5,0)
>>> b = Vector(5,6,0)
>>> b - a
[1, 1, 0]
Or adding an arbitrary unit vector to a point to find a new point (which is the answer to your original question)
>>> a = Vector(4,5,0)
>>> direction = Vector(10, 1, 0).unitVector()
>>> a + direction
[4.995037190209989, 5.099503719020999, 0.0]
You can add more utilities, like allowing Vector/Scalar operations for scaling, etc.
class Point:
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x = initX
self.y = initY
def getX(self):
return self.x
def getY(self):
return self.y
def distanceFromOrigin(self):
return ((self.x ** 2) + (self.y ** 2))** 0.5
def __str__(self):
return "x=" + str(self.x) + ", y=" + str(self.y)
def get_line_to(self, target):
mx = (-target.x + self.x )
my = (-target.y + self.y)
grad=my/mx
c=-(grad*(self.x))+self.y
return grad
def halfway(self, target):
"""calculating midpoint"""
mx = (self.x + target.x) / 2
my = (self.y + target.y) / 2
return Point(mx, my)
def cencd(p1,p2,p3):
"""calculating the center of a circle"""
ma=(p2.getY-p1.getY)/(p2.getX-p1.getX)
mb=(p3.getY-p2.getY)/(p3.getX-p2.getX)
hw=p1.halfway(p2)
x=(ma*mb*(p1.getY-p3.getY)+mb*(p1.getX+p2.getX)-ma*(p2.getX+p3.getX))/2*(mb-ma)
ya=-(1/ma)*((x-hw.getX)+hw.getY)
return x,ya
"""defining the points for p1,p2 and p3"""
p = Point(5,5)
q = Point(6,-2)
r=Point(2,-4)
print(cencd(p,q,r))
I get this error message:SyntaxError: duplicate argument 'p1' in function definition on
Traceback (most recent call last):
File "python", line 45, in
File "python", line 34, in cencd
TypeError: unsupported operand type(s) for -: 'method' and 'method'
please assist.
"""working solution """"
ma=(p2.y-p1.y)/(p2.x-p1.x)
mb=(p3.y-p2.y)/(p3.x-p2.x)
hw=p1.halfway(p2)
x1=(ma*mb*(p1.y-p3.y)+mb*(p1.x+p2.x)-ma*(p2.x+p3.x))/(2*(mb-ma))
ya=-(1/ma)*((x1-hw.x))+hw.y
You don't need getters or setters in python nor is it pythonic to use them, you should access the attributes directly:
def cencd(p1, p2, p3):
"""calculating the center of a circle"""
ma = (p2.y - p1.y) / (p2.x - p1.x)
mb = (p3.y - p2.y) / (p3.x - p2.x)
hw = p1.halfway(p2)
x = (ma * mb * (p1.y - p3.y) + mb * (p1.x + p2.x) - ma * (p2.x + p3.x)) / 2 * (mb - ma)
ya = -(1 / ma) * ((x - hw.x) + hw.y)
return x, ya
Both getX and getY are methods in your code, not attributes. So you will need to call them using getX() and getY().
So ma=(p2.getY-p1.getY)/(p2.getX-p1.getX) becomes:
ma = (p2.getY()-p1.getY())/(p2.getX()-p1.getX())
And so on, the other code changes.
Otherwise, you can also define your methods as #property:
class Point:
...
...
#property
def getX(self):
return self.x
#property
def getY(self):
return self.y
...
And now you can access these as p1.getX and p2.getY and so on.
Note that the above #property decorator turns the method into a getter, which makes sense to use only with private variables (variables defined to start with _).
As such, since both x and y are normal attributes of your class, you can access them directly without using and property decorators or using getter methods, like p1.x and p2.y, as #Padraic points in his post.
As Padraic Cunningham said, we don't need getters or setters in Python, but as mu said we can make getters if we want, but normally they are used to get "fake" attributes that are actually computed from true attributes. For example, in the code below I've added a fake norm attribute to your Point class.
I've also added a few more double-underscore methods (aka dunder methods or magic methods) to your class. These methods are discussed in the official Python docs.
One of the most common dunder methods is __repr__() which should return a string that corresponds to how you create an instance of the class. This is especially handy when you're using a class in the interactive interpreter. FWIW, if a class doesn't define a __str__() method its __repr__() method will be used if you attempt to turn a class instance into a string. If a __repr__() method hasn't been defined a default one will be used.
The other dunder methods I've added make it easier to perform arithmetic operations on points; this can make code easier to write and to read. I think you'll agree that it makes the cencd() function a little clearer. (I'm not sure exactly what that function's supposed to do; I assume you've got the algebra correct :) ).
This code was tested on Python 2.6.6; it should run ok on Python 3 without modification.
#!/usr/bin/env python
''' Point class demo
From http://stackoverflow.com/q/28602056/4014959
Written by koseph, Padraic Cunningham, and PM 2Ring
2015.02.19
'''
from __future__ import print_function
from __future__ import division
class Point(object):
def __init__(self, initX, initY):
""" Create a new point at the given coordinates. """
self.x, self.y = initX, initY
#property
def norm(self):
return self.x ** 2 + self.y ** 2
def distance_from_origin(self):
return self.norm ** 0.5
def __repr__(self):
return 'Point({self.x}, {self.y})'.format(self=self)
def __str__(self):
return 'x={self.x}, y={self.y}'.format(self=self)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __mul__(self, scale):
return Point(self.x * scale, self.y * scale)
__rmul__ = __mul__
def __neg__(self):
return -1 * self
def __sub__(self, other):
return self + -other
def weighted_mean(self, other, weight):
cweight = 1.0 - weight
x = cweight * self.x + weight * other.x
y = cweight * self.y + weight * other.y
return Point(x, y)
def halfway(self, other):
return self.weighted_mean(other, 0.5)
def cencd(p1, p2, p3):
""" Calculate the center of a circle """
a = p2 - p1
b = p3 - p2
ma = a.y / a.x
mb = b.y / b.x
hw = p1.halfway(p2)
x = ma * mb * (p1 - p3).y + mb * (p1 + p2).x - ma * (p2 + p3).x
x /= 2.0 * (mb - ma)
y = -(x - hw.x + hw.y) / ma
return Point(x, y)
p1 = Point(3, 4)
print(p1)
print('p1 is {0!r}, its norm is {1}'.format(p1, p1.norm))
print('and its distance from the origin is', p1.distance_from_origin(), end='\n\n')
p2 = Point(7, 2)
print('p2 is', repr(p2), end='\n\n')
print('p1 + p2 is', repr(p1 + p2))
print('p1 * 0.1 is', repr(p1 * 0.1))
print('p2 - p1 is', repr(p2 - p1), end='\n\n')
p3 = 4 * p1
print('p3 is', repr(p3), end='\n\n')
print('Weighted means from p1 to p3')
for i in range(5):
weight = i / 4.0
print('{0} {1:4.2f} {2!r}'.format(i, weight, p1.weighted_mean(p3, weight)))
print()
print('center of a circle for p1, p2, & p3:', repr(cencd(p1, p2, p3)))
output
x=3, y=4
p1 is Point(3, 4), its norm is 25
and its distance from the origin is 5.0
p2 is Point(7, 2)
p1 + p2 is Point(10, 6)
p1 * 0.1 is Point(0.3, 0.4)
p2 - p1 is Point(4, -2)
p3 is Point(12, 16)
Weighted means from p1 to p3
0 0.00 Point(3.0, 4.0)
1 0.25 Point(5.25, 7.0)
2 0.50 Point(7.5, 10.0)
3 0.75 Point(9.75, 13.0)
4 1.00 Point(12.0, 16.0)
center of a circle for p1, p2, & p3: Point(8.22727272727, 12.4545454545)