changing class variables python - python

I am writing a script to read DXF files and return length and area of the shapes so that I can automatically calculate the price of laser cut parts.
Arc should take arc name, center point, radius, starting angle, ending angle from the dxf file.
Arc should calculate the starting and ending point of the arc.
The problem is, that the angles are arbitrary so the starting and ending point are arbitrary,
And such it is hard for me to string arcs together to form a full figure,
I need a mechanism to switch the starting and ending point if I notice that it’s backwards.
I tried to write a function in arc class, to switch the starting and ending points
But it isn’t working,
I am not so strong with OOP, please help
attached code
class arc:
def __init__(self, name, center_point, radius, angles):
self.name = name
self.center_point = center_point
self.radius = radius
self.starting_angle = angles[0]
self.ending_angle = angles[1]
starting_angle = angles[0]
ending_angle = angles[1]
self.starting_point = center_point[0]+radius * math.cos((starting_angle)*((math.pi)/(180))),center_point[1]+radius * math.sin((starting_angle)*((math.pi)/(180)))
self.ending_point = center_point[0]+radius * math.cos((ending_angle)*((math.pi)/(180))),center_point[1]+radius * math.sin((ending_angle)*((math.pi)/(180)))
starting_point =center_point[0]+radius * math.cos((starting_angle)*((math.pi)/(180))),center_point[1]+radius * math.sin((starting_angle)*((math.pi)/(180)))
ending_point = center_point[0]+radius * math.cos((ending_angle)*((math.pi)/(180))),center_point[1]+radius * math.sin((ending_angle)*((math.pi)/(180)))
self.length = math.sqrt((starting_point[0]-ending_point[0])**2+(starting_point[1]-ending_point[1])**2)
I desire a function called switch
this is how it should work:
arc1.starting_point = (0,0)
arc1.ending_point = (1,1)
print(arc1.starting_point, arc1.ending_point)
#Desired Output:
((0,0),(1,1))
arc1.switch()
print(arc1.starting_point, arc1.ending_point)
#Desired Output:
((1,1),(0,0))

You can swap the values of two variables with simultaneous assignment:
>>> x = 0
>>> y = 1
>>> x, y = y, x
>>> x
1
>>> y
0
so
def switch(self):
self.starting_point, self.ending_point = self.ending_point, self.starting_point
should do it.

Related

How to check if two polygons from geopositions touch or overlap on python?

I would like to check if two polygons built from coordinates touch or overlap. The following example should touch (they came from a simulation), however, the code doesn't detect they touch, hence, crash. Any clue?
import geopy
import geopy.distance
import math
from shapely import geometry
def bearing_calc(a_lat, a_lon, b_lat, b_lon): # a previous position b current position
# print(a)
# a.type
rlat1 = math.radians(a_lat)
# print(a_lat)
rlon1 = math.radians(a_lon)
rlat2 = math.radians(b_lat)
rlon2 = math.radians(b_lon)
dlon = math.radians(b_lon - a_lon)
b = math.atan2(math.sin(dlon) * math.cos(rlat2),
math.cos(rlat1) * math.sin(rlat2) - math.sin(rlat1) * math.cos(rlat2) * math.cos(
dlon)) # bearing calc
bd = math.degrees(b)
br, bn = divmod(bd + 360, 360) # the bearing remainder and final bearing
return bn
# To get a rotated rectangle at a bearing, you need to get the points of the the recatangle at that bearing
def get_rotated_points(coordinates, bearing, width, length):
start = geopy.Point(coordinates)
width = width / 1000
length = length / 1000
rectlength = geopy.distance.distance(kilometers=length)
rectwidth = geopy.distance.distance(kilometers=width)
halfwidth = geopy.distance.distance(kilometers=width / 2)
halflength = geopy.distance.distance(kilometers=length / 2)
pointAB = halflength.destination(point=start, bearing=bearing)
pointA = halfwidth.destination(point=pointAB, bearing=0 - bearing)
pointB = rectwidth.destination(point=pointA, bearing=180 - bearing)
pointC = rectlength.destination(point=pointB, bearing=bearing - 180)
pointD = rectwidth.destination(point=pointC, bearing=0 - bearing)
points = []
for point in [pointA, pointB, pointC, pointD]:
coords = (point.latitude, point.longitude)
points.append(coords)
return points
v1_id = 176
v1_coord_5 = [41.39129840757358, 2.1658667401858738]
v1_coord_4 = [41.391335226146495, 2.1658363842310404]
v1_length = 5 #meters
v1_width = 1.8 #meters
v1_bearing = bearing_calc(v1_coord_4[0], v1_coord_4[1],
v1_coord_5[0], v1_coord_5[1])
v1_points = get_rotated_points(tuple(v1_coord_5), v1_bearing,
v1_width, v1_length)
polygon1 = geometry.Polygon(v1_points)
v2_id = 210
v2_coord_5 = [41.39134056977012, 2.1658847515529804]
v2_coord_4 = [41.39127485461452, 2.165851515431892]
v2_length = 5 #meters
v2_width = 1.8 #meters
v2_bearing = bearing_calc(v2_coord_4[0], v2_coord_4[1],
v2_coord_5[0], v2_coord_5[1])
v2_points = get_rotated_points(tuple(v2_coord_5), v2_bearing,
v2_width, v2_length)
polygon2 = geometry.Polygon(v2_points)
if polygon1.intersection(polygon2).area > 0.0:
print("COLISION")
else:
print("NO COLISION")
I've also tried the function touches
polygon1.touches(polygon2)
Unfortunately, also, false. I don't know if there is a function to check if the two borders touch even in one points, minimally.
If I plot the two polygons, they do not collide, as shown above, however, they should. Is it because the code is not considering the earth surface curvature? How can I adapt the code to consider that factor.

Generating random dots within an outer circle that have a certain distance

I'm currently really stuck with some of my code and I can't seem to find the issue. Here is what I am trying to do:
I have a big outer circle in which I want to display smaller dots. These dots should be randomly distributed but should not overlap, thus they should have a minimum distance to each other.
What I have tried is to first randomly generate a point, check wether it is in the outer circle and if it is, append it to the final list of dot positions. Then another point is created, checked if in circle and then it should be checked if the dot has a minimum distance to the other dot(s) in the final list.
However, I seem to have some issues with my code as it will not run through whenever I set the required distances higher than 1. I have changed multiple things, but I cannot make it work.
Does anyone have an idea about what the problem might be?
Here's what I have been trying:
import random
import numpy as np
import math
#Variables
radiusOC = 57
size_obj = 7
required_dist = 5
no_stimuli = 3
def CreatePos(radiusOC, size_obj, required_dist, no_stimuli):
final_list = []
def GenRandPos(radiusOC,size_obj):
"""
Takes the radius of the outer circle and generates random dots within this radius. Then checks if the the dots are located
within the outer circle.
"""
while True:
xPos = random.randint(-radiusOC,radiusOC)
yPos = random.randint(-radiusOC,radiusOC)
# check if in Circle
on_circle = (xPos- 0)**2 + (yPos-0)**2
if (radiusOC-size_obj)**2 >= on_circle:
print("Still in circle",on_circle, xPos, yPos )
position = [xPos, yPos]
break
else:
print("Not in circle",on_circle, xPos, yPos )
continue
return position
def CheckSurrounding(position, final_list, required_dist):
"""
Takes dot positions that are in the visual field, the list of positions, and the distances dots are required to have from each other.
It is checked if there are dots close by or not.
"""
X1 = position[0]
Y1 = position[1]
dist_list = []
for elem in final_list:
for i in elem:
X2 = elem[0]
Y2 = elem[1]
dist = math.sqrt((X1-X2)**2 + (Y1-Y2)**2)
dist_list.append(dist)
if all(dist_list) >= required_dist:
return position
else:
return None
# append the first dot to the list
position = GenRandPos(radiusOC, size_obj)
final_list.append(position)
# now append the rest of the dots if they have a certain distance to each other
while len(final_list) < no_stimuli:
position = GenRandPos(radiusOC, size_obj)
if CheckSurrounding(position, final_list, required_dist) != None:
position = CheckSurrounding(position, final_list, required_dist)
final_list.append(position)
else:
continue
return final_list
´´´
In the line
if all(dist_list) >= required_dist:
all(dist_list) will be either True or False, which is numerically equivalent to either 1 or 0. If required_dist is greater than 1 the inequality will never be satisfied. I think that you intended this to be
if all(dist_list >= required_dist):
but this will not work since you cannot compare a list dist_list to a number required_dist. To fix it, convert dist_list to a numpy array:
if np.all(np.array(dist_list) >= required_dist):
By the way, the random points you are selecting will always have integer coordinates since you are using random.randint(), I am not sure if this is intentional.
The whole code can be made more efficient by using numpy arrays. For example:
import numpy as np
def CreatePos(radiusOC, size_obj, required_dist, no_stimuli):
final_list = []
def GenRandPos(radiusOC, size_obj):
"""
Takes the radius of the outer circle and generates
random dots within this radius. Then checks if the dots are
located within the outer circle.
"""
while True:
position = (2 * np.random.random(2) - 1) * radiusOC
# check if in Circle
if (radiusOC - size_obj)**2 >= (position**2).sum():
return position
def CheckSurrounding(position, final_list, required_dist):
"""
Takes dot positions that are in the visual field,
the list of positions, and the distances dots are
required to have from each other.
It is checked if there are dots close by or not.
"""
final_arr = np.array(final_list)
dist = ((np.array(final_list) - position)**2).sum(axis=1)
if np.all(np.array(dist) >= required_dist**2):
return position
# append the first dot to the list
position = GenRandPos(radiusOC, size_obj)
final_list.append(position)
# now append the rest of the dots if they have a certain distance to each other
while len(final_list) < no_stimuli:
position = GenRandPos(radiusOC, size_obj)
if CheckSurrounding(position, final_list, required_dist) is not None:
final_list.append(position)
return final_list
Note that this returns a list of points with coordinates given by floats, not integers.
Sample usage:
#Variables
radiusOC = 57
size_obj = 7
required_dist = 3
no_stimuli = 400
final_list = np.array(CreatePos(radiusOC, size_obj, required_dist, no_stimuli))
Plot the resulting points:
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111)
ax.set_aspect("equal")
plt.scatter(f[:, 0], f[:, 1])
plt.show()
This gives:
I would add a condition in the final while loop so it can break if a new point cannot be found after some number of attempts. Otherwise, it may end up running indefinitely.
You need to figure out a way to estimate the max number of points given the distance. This can be extrapolated from the circle packing problem. https://planetcalc.com/7473/ I will comment if I have an easy check to do.
import numpy as np
from matplotlib import pyplot as plt
from scipy.spatial.distance import cdist
def random_in_circle(num_points=1000, R=1, min_dist=0.2):
assert min_dist < R, "Min distance between points must be smaller than the radius of outer circle"
assert R / (2 ** (num_points - 1)) < min_dist, "Min dist is too large"
points = []
while len(points) < num_points:
a = np.random.rand() * 2 * np.pi # random angle
r = R * np.random.rand() # random radius
point = r * np.array([np.cos(a), np.sin(a)])
if len(points) == 0:
points.append(point)
elif np.all(cdist([point], points) > min_dist):
points.append(point)
return np.vstack(points)
points = random_in_circle(num_points=1000, min_dist=0.01)
plt.scatter(points[:, 0], points[:, 1])
plt.show()

Python: Intersection of spheres

I am extremely new to programming but I decided to take on an interesting project as I recently learnt how to represent a sphere in parametric form. When intersecting three spheres, there are two points of intersections that are distinct unless they only overlap at a singular point.
Parametric representation of a sphere:
The code I have is modified from the answer from Python/matplotlib : plotting a 3d cube, a sphere and a vector?, adding the ability to dictate the x, y and z origin and the radius of the sphere. Many similar questions were written in C++, Java, and C#, which I cannot understand at all (I barely know what I am doing so go easy on me).
My Code:
import numpy as np
def make_sphere_x(x, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
x += radius * np.cos(u) * np.sin(v)
return x
def make_sphere_y(y, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
y += radius * np.sin(u) * np.sin(v)
return y
def make_sphere_z(z, radius):
u, v = np.mgrid[0:2 * np.pi:5000j, 0:np.pi:2500j]
z += radius * np.cos(v)
return z
#x values
sphere_1_x = make_sphere_x(0, 2)
sphere_2_x = make_sphere_x(1, 3)
sphere_3_x = make_sphere_x(-1, 4)
#y values
sphere_1_y = make_sphere_y(0, 2)
sphere_2_y = make_sphere_y(1, 3)
sphere_3_y = make_sphere_y(0, 4)
#z values
sphere_1_z = make_sphere_z(0, 2)
sphere_2_z = make_sphere_z(1, 3)
sphere_3_z = make_sphere_z(-2, 4)
#intercept of x-values
intercept_x = list(filter(lambda x: x in sphere_1_x, sphere_2_x))
intercept_x = list(filter(lambda x: x in intercept_x, sphere_3_x))
print(intercept_x)
Problems:
Clearly there must be a better way of finding the intercepts. Right now, the code generates points at equal intervals, with the number of intervals I specify under the imaginary number in np.mgrid. If this is increased, the chances of an intersection should increase (I think) but when I try to increase it to 10000j or above, it just spits a memory error.
There are obvious gaps in the array and this method would most likely be erroneous even if I have access to a super computer and can crank up the value to an obscene value. Right now the code results in a null set.
The code is extremely inefficient, not that this is a priority but people like things in threes right?
Feel free to flame me for rookie mistakes in coding or asking questions on Stack Overflow. Your help is greatly valued.
Using scipy.optimize.fsolve you can find the root of a given function, given an initial guess that is somewhere in the range of your solution. I used this approach to solve your problem and it seems to work for me. The only downside is that it only provides you one intersection. To find the second one you would have to tinker with the initial conditions until fsolve finds the second root.
First we define our spheres by defining (arbitrary) radii and centers for each sphere:
a1 = np.array([0,0,0])
r1 = .4
a2 = np.array([.3,0,0])
r2 = .5
a3 = np.array([0,.3,0])
r3 = .5
We then define how to transform back into cartesian coordinates, given angles u,v
def position(a,r,u,v):
return a + r*np.array([np.cos(u)*np.sin(v),np.sin(u)*np.sin(v),np.cos(v)])
Now we think about what equation we need to find the root of. For any intersection point, it holds that for perfect u1,v1,u2,v2,u3,v3 the positions position(a1,r1,u1,v1) = position(a2,r2,u2,v2) = position(a3,r3,u3,v3) are equal. We thus find three equations which must be zeros, namely the differences of two position vectors. In fact, as every vector has 3 components, we have 9 equations which is more than enough to determine our 6 variables.
We find the function to minimize as:
def f(args):
u1,v1,u2,v2,u3,v3,_,_,_ = args
pos1 = position(a1,r1,u1,v1)
pos2 = position(a2,r2,u2,v2)
pos3 = position(a3,r3,u3,v3)
return np.array([pos1 - pos2, pos1 - pos3, pos2 - pos3]).flatten()
fsolve needs the same amount of input and output arguments. As we have 9 equations but only 6 variables I simply used 3 dummy variables so the dimensions match. Flattening the array in the last line is necessary as fsolve only accepts 1D-Arrays.
Now the intersection can be found using fsolve and a (pretty random) guess:
guess = np.array([np.pi/4,np.pi/4,np.pi/4,np.pi/4,np.pi/4,np.pi/4,0,0,0])
x0 = fsolve(f,guess)
u1,v1,u2,v2,u3,v3,_,_,_ = x0
You can check that the result is correct by plugging the angles you received into the position function.
The problem would be better tackled using trigonometry.
Reducing the problem into 2D circles, we could do:
import math
import numpy
class Circle():
def __init__(self, cx, cy, r):
"""initialise Circle and set main properties"""
self.centre = numpy.array([cx, cy])
self.radius = r
def find_intercept(self, c2):
"""find the intercepts between the current Circle and a second c2"""
#Find the distance between the circles
s = c2.centre - self.centre
self.dx, self.dy = s
self.d = math.sqrt(numpy.sum(s**2))
#Test if there is an overlap. Note: this won't detect if one circle completly surrounds the other.
if self.d > (self.radius + c2.radius):
print("no interaction")
else:
#trigonometry
self.theta = math.atan2(self.dy,self.dx)
#cosine rule
self.cosA = (c2.radius**2 - self.radius**2 + self.d**2)/(2*c2.radius*self.d)
self.A = math.acos(self.cosA)
self.Ia = c2.centre - [math.cos(self.A+self.theta)*c2.radius, math.sin(self.A+self.theta)*c2.radius]
self.Ib = c2.centre - [math.cos(self.A-self.theta)*c2.radius,-math.sin(self.A-self.theta)*c2.radius]
print("Interaction points are : ", self.Ia, " and: ", self.Ib)
#define two arbitrary circles
c1 = Circle(2,5,5)
c2 = Circle(1,6,4)
#find the intercepts
c1.find_intercept(c2)
#test results by reversing the operation
c2.find_intercept(c1)

duplicate objects with trig function translations in Maya + Python

I'm trying to create a hexagonal array of spheres around a center sphere in the XY plane using a python loop function (couldn't figure out how to do it using duplicate special). It should end up looking something like this:
0 0
0 0 0
0 0
Here's my code. I' getting a syntax error
# Error: line 1: invalid syntax #
when I call it, though I'm pretty sure there's nothing wrong with line one.
import maya.cmds as cmds
class Sphere(radius, tx=0, ty=0, tz=0, sx=0, sy=0, sz=0):
self.diameter = 2*radius
def createSphere(radius, tx, ty):
newSphere = Sphere(radius=radius, tx=tx, ty=ty)
return newSphere
def duplicateSphere(wholeSphere):
for i in range(6, 1, -1):
createSphere(tx=wholeSphere.diameter*math.cos(2*math.pi/i), ty=wholeSphere.diameter*math.sin(2*math.pi/i))
# create spheres with projections onto x and y axes as translation params
duplicateSphere(createSphere(1.03))
Any ideas as to what's going on?
Ok, first off to answer your question, the SyntaxError is caused by improper class instantiation. class declaration must be separated from the constructor method in python, like so:
class Sphere(object):
def __init__(self, radius, tx=0, ty=0, tz=0, sx=0, sy=0, sz=0):
self.diameter = 2 * radius
Tip: In the maya script editor panel, if you enable History->Show Stack Trace it will give you a better idea of where the actual error is occurring.
However, there are a couple other issues at play. For one, you are never storing the parameters you pass into the sphere class (except radius, which you are storing implicitly by storing the diameter). You probably wanted:
class Sphere(object):
def __init__(self, radius, tx=0, ty=0, tz=0, sx=1, sy=1, sz=1):
self.radius = radius
self.tx = tx
self.ty = ty
self.tz = tz
self.sx = sx
self.sy = sy
self.sz = sz
self.diameter = 2 * radius
I changed the scale defaults to 1 instead of 0 so that the default sphere is not invisibly small.
Also, as theodox pointed out, you have a TypeError in your createSphere method, which takes 3 params (none of them are keyword arguments currently and therefore not optional) and you are only passing in 1.
However, the main issue is that currently you are not actually creating any spheres in maya. If the intention is that your sphere object is an object-oriented wrapper around maya.cmds, you will need it to call cmds.sphere somewhere, and you will probably want to cmds.move() and cmds.scale() it to by your t* and s* values. If you do all this in the constructor, you could actually then avoid setting instance variables for all the sphere properties if you wanted.
This would look something like this:
cmds.sphere(radius=self.radius)
cmds.move(self.tx,self.ty,self.tz)
cmds.scale(self.sx,self.sy,self.sz)
Finally, I think your trig is a bit off (you want each iteration to vary by exactly 60° or π/3 radians). To get the proper angles, I think you want something more along the lines of:
import math
for i in range(6,0,-1):
angle = math.pi * i / 3.0
distance = wholeSphere.diameter
tx = distance * math.cos(angle)
ty = distance * math.sin(angle)
# ...
As a last note, consider looking at an object-oriented solution like pymel to avoid needing to reinvent the wheel in terms of wrapping maya.cmds commands in objects.
Anyways, applying all these corrections produces something like:
import maya.cmds as cmds
import math
class Sphere(object):
def __init__(self, radius, tx=0, ty=0, tz=0, sx=1, sy=1, sz=1):
self.diameter = 2*radius
self.radius = radius
self.tx = tx
self.ty = ty
self.tz = tz
self.sx = sx
self.sy = sy
self.sz = sz
cmds.sphere(radius=self.radius)
cmds.move(self.tx,self.ty,self.tz)
cmds.scale(self.sx,self.sy,self.sz)
def createSphere(radius, tx=0, ty=0):
newSphere = Sphere(radius, tx=tx, ty=ty)
return newSphere
def duplicateSphere(wholeSphere):
for i in range(6, 0, -1):
angle = math.pi * i / 3.0
distance = wholeSphere.diameter
tx = distance * math.cos(angle)
ty = distance * math.sin(angle)
createSphere(wholeSphere.radius, tx=tx, ty=ty)
duplicateSphere(createSphere(1.03))

Python - get the ratios between degrees and meters, depending on coordinates on a (google) map

I am trying to find spots that block a circle (a,b,R) on a map, the problem is that coordinates used on the map are translated to different metric values as longitude changes.
i know it has something to do with radians.
this is the code with the missing part:
class Circle(object):
def __init__(self,x,y,R):
self.x = x #Degrees
self.y = y #Degrees
self.R = R #meters
def getRatio(self,x,y):
#This is where the magic happens...
return latRatio, LatRatio
def getBlockingSquareCords(self):
latRation, lonRatio = getLonRatio(x,y)
latD = self.R/latRatio
lonD = self.R/lonRatio
x1 = self.x+latD
y1 = self.y+lonD
x2 = self.x-latD
y2 = self.y-lonD
return (x1,y1,x2,y2)
this following question has some hints to the answer, but i couldn't figure it out -Calculate distance between two latitude-longitude points? (Haversine formula)
It does depend only on latitude (because the circles are getting smaller when you move towards poles).
Latitude: 1 deg = 110.54 km
Longitude: 1 deg = 111.320*cos(latitude) km

Categories

Resources