Forming a polygon with classes - python

So, my problem is: I am trying to create a program which would create a polygon that has atleast 3 points(that are composed of coordinates x and y) or angles. I would like that, if there are less than 3 points or angles submitted, the program returns an error saying there are insufficient number of points. I need to create this with classes.
I have created this so far: `
class Polygon:
number_points = 0
number_angles = 0
def __init__(self, coordinate_x, coordinate_y, angles):
s = []
self.coordinate_x = coordinate_x
self.coordinate_y = coordinate_y
self.angles = angles
self.s = s.append([coordinate_x, coordinate_y])
Polygon.number_points = Polygon.number_points + 1
Nkotnik.number_angles = Polygon.number_angles + 1
# Here i would like the program to check if there are enough points
# and angles to form a polygon and to check if all coordinates are
# numbers. If this requirement is not met, the program prints an
# error message.
def creation(self):
if not isinstance(coordinate_x, (int,float)):
#raise Exception("That is not a number")
if Polygon.number_points <= 3:
`
The idea that I had is that i store the coordinates in a list and then when the user enters enough points, a polygon can be formed.
I am not a native speaker, so if I need to clear things a bit further feel free to ask :) thank you for any possible answers :)

I see an error here:
Polygon.number_points = Polygon.number_points + 1
Nkotnik.number_angles = Polygon.number_angles + 1
Nkotnik should be Polygon. Also, to make it shorter, you could do Polygon.number_points += 1 and same for number_angles.
So now, the creation of the program:
def creation(self):
This is bad design. The function should take the number of points and the number of angles as parameters. So, do this:
def creation(self, points, angles):
But creation is basically initialization, so you should integrate it into your __init__.
Also, your __init__ is strange. number_points and number_angles should be defined in the __init__, not the object body, because those variables are different for different Polygon objects. So after modification, your code looks like this:
class Polygon:
def __init__(self, coord_list, angles):
if len(coord_list) // 2 < 3:
raise Exception("Side count must be 3 or more.")
s = []
self.number_points = 0
self.number_angles = 0
self.coordinates_x = coord_list[::2]
self.coordinates_y = coord_list[1::2]
self.angles = angles
self.s = s.append([coordinate_x, coordinate_y])
self.number_points += len(coord_list // 2)
self.number_angles += len(angles)
num_sides = int(input('Number of sides: ')) #raw_input if you're using Python 2
points = []
angles = []
for i in range(num_sides):
points.append(int(input('X value of point: ')))
points.append(int(input('Y value of point: ')))
for i in range(num_sides):
angles.append(int(input('Angle value: ')))
polygon_object = Polygon(points, angles)
And you're done!

You can do the check at creation time in the class, like this, also you need more that just a angle to define a point
import collections
PointCartesian = collections.namedtuple("PointCartesian","coordinate_x coordinate_y")
PointPolar = collections.namedtuple("PointPolar","magnitude angle")
#this is a easy way to make a class for points, that I recommend have
#a class too
class Polygon(object):
def __init__(self,*argv,**kargv):
points = list()
for elem in argv:
if isinstance(elem,(PointCartesian,PointPolar ) ):
points.append(elem)
else:
raise ValueError("Element "+str(elem)+" of wrong type")
if len(points) <3:
raise ValueError("Insufficient data")
self.points = points
and in other place you have the routine that ask the user for the data, you can check every input or leave it to the class.
to call it do something like this
Polygon(PointCartesian(1,2),PointCartesian(4,7),PointPolar(5,28.2))
Polygon(*list_of_points)

Related

Python - generating weight map in C4D

Hello Python and Stack community in general.
First, let me say I'm a 3d guy and not much of a code guy. So thanks for the understanding...
The exact problem:
I have to generate a gradient map based on a distance from points to multiple objects
Here is a simple preview of what my problem looks like
Preview_C4D
My base code is the following:
import c4d
#Welcome to the world of Python
warray = [0.0]
def main():
global warray
wtag = op[c4d.ID_USERDATA,1] #drag vertex map from user data panel
obj = wtag.GetObject() #the object of the vertex map
pts = obj.GetAllPoints()
cnt = len(pts)
null = op.GetObject()
nullpos = null.GetMg().off #vector magnitude from matrix
minDistance = op[c4d.ID_USERDATA,4] #drag slider map from user data panel
maxDistance = op[c4d.ID_USERDATA,5] #drag slider map from user data panel
if len(warray) != cnt:
diff = cnt - len(warray)
warray.extend([0.0]*diff)
for x in xrange(cnt): #remapping in the range 0-1
point = pts[x]
distance = (nullpos - point).GetLength()
warray[x] = c4d.utils.RangeMap(distance,minDistance,maxDistance,1,0,False)
if warray[x] > 1:
warray[x] = 1.0
elif warray[x] < 0:
warray[x] = 0.0
wtag.SetAllHighlevelData(warray) #bake the new vertex map
Now lets say I have a list with multiple objects:
parent = doc.SearchObject('parent')
list1 = parent.GetChildren() # list of cubes under parent
count = len(list1)
for a in range(list):
obj = list1[a]
distance = ?
So I'm stuck here, because I can't figure it out how to merge
the new values with the old.
In few words I need a loop that evaluates the values for the points
for each object in the list and then adds them toghether.
I just can't dial it up right.
So I'll be very grateful if anybody can help.
Regards

backtracking not trying all possibilities

so I've got a list of questions as a dictionary, e.g
{"Question1": 3, "Question2": 5 ... }
That means the "Question1" has 3 points, the second one has 5, etc.
I'm trying to create all subset of question that have between a certain number of questions and points.
I've tried something like
questions = {"Q1":1, "Q2":2, "Q3": 1, "Q4" : 3, "Q5" : 1, "Q6" : 2}
u = 3 #
v = 5 # between u and v questions
x = 5 #
y = 10 #between x and y points
solution = []
n = 0
def main(n_):
global n
n = n_
global solution
solution = []
finalSolution = []
for x in questions.keys():
solution.append("_")
finalSolution.extend(Backtracking(0))
return finalSolution
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
return finalSolution
def reject(k):
if solution[k] in solution: #if the question already exists
return True
if k > v: #too many questions
return True
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for x in solution:
if x in questions.keys():
points = points + questions[x]
if points in range (x, y+1) and k in range (u, v+1):
return True
return False
print(main(len(questions.keys())))
but it's not trying all possibilities, only putting all the questions on the first index..
I have no idea what I'm doing wrong.
There are three problems with your code.
The first issue is that the first check in your reject function is always True. You can fix that in a variety of ways (you commented that you're now using solution.count(solution[k]) != 1).
The second issue is that your accept function uses the variable name x for what it intends to be two different things (a question from solution in the for loop and the global x that is the minimum number of points). That doesn't work, and you'll get a TypeError when trying to pass it to range. A simple fix is to rename the loop variable (I suggest q since it's a key into questions). Checking if a value is in a range is also a bit awkward. It's usually much nicer to use chained comparisons: if x <= points <= y and u <= k <= v
The third issue is that you're not backtracking at all. The backtracking step needs to reset the global solution list to the same state it had before Backtracking was called. You can do this at the end of the function, just before you return, using solution[k] = "_" (you commented that you've added this line, but I think you put it in the wrong place).
Anyway, here's a fixed version of your functions:
def Backtracking(k):
finalSolution = []
for c in questions.keys():
solution[k] = c
print ("candidate: ", solution)
if not reject(k):
print ("not rejected: ", solution)
if accept(k):
finalSolution.append(list(solution))
else:
finalSolution.extend(Backtracking(k+1))
solution[k] = "_" # backtracking step here!
return finalSolution
def reject(k):
if solution.count(solution[k]) != 1: # fix this condition
return True
if k > v:
return True
points = 0
for q in solution:
if q in questions:
points = points + questions[q]
if points > y: #too many points
return True
return False
def accept(k):
points = 0
for q in solution: # change this loop variable (also done above, for symmetry)
if q in questions:
points = points + questions[q]
if x <= points <= y and u <= k <= v: # chained comparisons are much nicer than range
return True
return False
There are still things that could probably be improved in there. I think having solution be a fixed-size global list with dummy values is especially unpythonic (a dynamically growing list that you pass as an argument would be much more natural). I'd also suggest using sum to add up the points rather than using an explicit loop of your own.

K-center algorithm

I am currently using python to solving k-center algorithm.
When I run my codes its runtime exceeds the limit time(provided by my teacher),I don't quite know the way to improve my code so it can pass the limited runtime.
My code is below:
import math
# 1.Import group
# 2.Find the most farthest point in this group.
# 3.reassign the rest points between two center points
# 4.Find the most farthest point from its center point, and make it the newest center point
# 5.reassign points among all center points
# 6.Repeat 4 and 5 step untill the answer fits the condition
class point():
def __init__(self,x,y,num,group=[]):
self.x = x
self.y = y
self.id = num
self.group = []
def range_cus(one,two):
return math.sqrt(math.pow((one.x-two.x),2)+math.pow((one.y-two.y),2))
def reassign(all_points,all_answer):
for i in range(len(all_answer)):
all_answer[i].group = []
for i in range(len(all_points)):
if all_points[i] not in all_answer:
min_length = 0
for j in range(len(all_answer)):
current_length = range_cus(all_answer[j],all_points[i])
if min_length == 0:
min_length = current_length
current_group = all_answer[j]
elif current_length < min_length:
min_length = current_length
current_group = all_answer[j]
current_group.group.append(all_points[i])
def search(all_answer,seek_points_number):
if seek_points_number == 0:
return 0
answer_range = 0
for j in range(len(all_answer)):
for i in range(len(all_answer[j].group)):
if range_cus(all_answer[j],all_answer[j].group[i])>answer_range:
answer_range = range_cus(all_answer[j].group[i],all_answer[j])
answer_obj = all_answer[j].group[i]
seek_points_number -= 1
final_answer.append(answer_obj)
reassign(group,final_answer)
search(final_answer,seek_points_number)
info = raw_input().split(',')
info = [int(i) for i in info]
group = []
final_answer = []
for i in range(info[0]):
x = raw_input().split(',')
group.append(point(float(x[0]),float(x[1]),i+1))
final_answer.append(group[info[2]-1])
group[info[2]-1].group = [point for point in group if point not in final_answer]
search(final_answer,info[1]-1)
print ",".join([str(answer.id) for answer in final_answer])
Please help me examine where should the function be revised to save some runtime.
Example input:
10,3,10 #The first number denotes the sets of data.The second denotes the number of answer I want to return.The third denotes the first center point's id.
21.00,38.00
26.00,28.00
45.00,62.00
31.00,51.00
39.00,44.00
42.00,39.00
21.00,27.00
28.00,29.00
31.00,60.00
27.00,54.00
Example output
10,7,6
You can save at least some time by simply rewriting the range_cus function. As you call this function inside a nested loop, it should to be a good point of attack. Try replacing it with
def range_cus(one,two):
return sqrt((one.x - two.x)**2 + (one.y - two.y)**2)
and remember to do from math import sqrt at the top of your program. In this version, you get rid of a lot of lookups on the math object (math.)

python - divide world into bins

I am working on trying to put moving balls into appropriate bins. I like to think I'm on the right track but I've been stuck for awhile now.
I left code out that didn't seem relevant to my question but if those who answer need further details I can provide them. Basically, I have a world of 200 moving balls. They have an X and Y coordinate. I want to divide the world into square bins of width 256 and place the balls in the appropriate bin.
My approach to this was to put them into a dictionary. It looked like this:
dict_of_balls = {}
for i in range(len(balls)):
xb = int(balls[i].x/256)
yb = int(balls[i].y/256)
and I wanted to make the keys a tuple of the (xb, yb) pairs and then place the appropriate balls in that bin but I don't think you can use tuples as keys...
The code is below:
import math
import random
import time
import sys
ball_min_radius = 16.0 #world coordinates
ball_max_radius = 128.0 #world coordniates
number_balls = 200
class Ball:
"""
Implements a point/ball
"""
def __init__(self):
self.x = random.uniform(world_min_x,world_max_x)
self.y = random.uniform(world_min_y,world_max_y)
self.radius = int(random.uniform(ball_min_radius,ball_max_radius))
def __lt__(self, other):
return self.id < other.id
def main():
world_min_x = -200.0*number_balls**.5 # minimum x in world coordinates
world_max_x = +200.0*number_balls**.5 # maximum x in world coordinates
world_min_y = -200.0*number_balls**.5 # minimum y in world coordinates
world_max_y = +200.0*number_balls**.5 # maximum y in world coordinates
balls = [Ball() for i in range(number_balls)]
so does anyone have any ideas for how to divide the world into bins based on the given world coordinates? I am unsure of which data structure to use since I can't use tuples for keys. Thanks in advance for any feedback.
Why do you want a dictionary? Here's how you would do this, but keep in mind you will only get one ball per bin because you are specifically casting their key to be (int, int) and keys are unique.
If you use a collection, you can also sort (in my example I sort by the region identifiers):
I am not sure what you are doing that for, but you can do it:
import math
import random
import time
import sys
ball_min_radius = 16.0 #world coordinates
ball_max_radius = 128.0 #world coordniates
number_balls = 200
world_min_x = -200.0*number_balls**.5 # minimum x in world coordinates
world_max_x = +200.0*number_balls**.5 # maximum x in world coordinates
world_min_y = -200.0*number_balls**.5 # minimum y in world coordinates
world_max_y = +200.0*number_balls**.5 # maximum y in world coordinates
class Ball:
"""
Implements a point/ball
"""
def __init__(self):
self.x = random.uniform(world_min_x,world_max_x)
self.y = random.uniform(world_min_y,world_max_y)
self.radius = int(random.uniform(ball_min_radius,ball_max_radius))
def __lt__(self, other):
return self.id < other.id
def __str__(self):
return 'x={x} y={y} r={r}'.format(x=self.x, y=self.y, r=self.radius)
def main():
balls = [Ball() for i in range(number_balls)]
dict_of_balls = {}
ball_collection = []
for b in balls:
xb = int(b.x/256)
yb = int(b.y/256)
key = (xb, yb)
dict_of_balls[key] = b
ball_collection.append((key, b))
print 'length of dictionary:{}'.format(len(dict_of_balls.keys()))
print 'length of collection:{}'.format(len(ball_collection))
Notice that the dictionary has fewer items than the collection.
You can also print each item this way pretty trivially:
for b in ball_collection:
print 'ball region: {r} with coords: {c}'.format(r=b[0], c=b[1])
Or, sort them if you want:
print 'Collections also let you sort the collection by region(s)...'
sorted_list = sorted(ball_collection, key= lambda x: (x[0][0], x[0][1]))
for b in sorted_list:
print 'ball region: {r} with coords: {c}'.format(r=b[0], c=b[1])
You can also pretty simply get balls in a specific region too:
print '... or get only ones in a specific region'
subset = [b for b in ball_collection if b[0][0] == 1]
for b in subset:
print 'ball region: {r} with coords: {c}'.format(r=b[0], c=b[1])
main()
A collection seems to do what you are actually wanting.
You can use tuple for keys in a dictionary, since tuple is immutable. The only data type you can't use for a dictionary key is a list [] or set {}
**a = {(1,2):'example1', (2,3):'example2'}
>>> a[(1,2)]
'example1'**
So I believe this should make it much easier to solve your problem.

convert list of arrays in python, to tree in grasshopper

I'm a beginner in Python and have a question about converting data structure, for using it in Grasshopper.
As an output from my python code, I have a grid of cubes (GUID's), layered up in what I call 'generations'. Besides that, it outputs a grid of data, which contains the information about what color each cube should get.
For example: for j=5 in i=3, in generation=5, I have a cube. In the other list, for j=5 in i=3 , in generation=5, I have 'green' as a string. In grasshopper, I want to link this 'green' value to a swatch, and then color the right cube with it.
The problem is that Python outputs a three-dimensional array, while Grasshopper works in trees. So, I have to convert my outputs to a tree structure in which the first level is 'generations', the second level is 'i' and the third is 'j'.
A friend sent me this piece of code, so I guess that is how to begin:
import clr
clr.AddReference("Grasshopper")
from Grasshopper.Kernel.Data import GH_Path
from Grasshopper import DataTree
I hope you guys can help!
Tessa
This is my mainfunction:
def Main():
intLength = input1
intWidth = input2
intGen = input3
arrValues = randomizeArray01(intLength,intWidth)
arrDensity = densityfunction(arrValues)
arrMeshes = render(arrValues,-1)
for k in range(intGen):
arrValues = applyGOL(arrValues,arrDensity)
arrDensity = densityfunction(arrValues)
genC = colorObject(arrValues)
colorList.append(genC)
genR = render(arrValues,k)
renderList.append(genR)
In which this is the renderfunction:
def render(arrValues, z):
rs.EnableRedraw(False)
arrMeshes = []
for i in range(len(arrValues)):
arrRow = []
for j in range(len(arrValues[i])):
box = addMeshBox([(i-0.5),(len(arrValues[i])-j-0.5),z-0.5], [(i+0.5),(len(arrValues[i])-j+0.5),z+0.5])
arrRow.append(box)
arrMeshes.append(arrRow)
rs.EnableRedraw(True)
return arrMeshes
And this is the colorfunction:
def colorObject(arrValues):
arrColor = []
for i in range(len(arrValues)):
rowColor= []
for j in range(len(arrValues[i])):
if arrValues[i][j] == 0:
color = green
rowColor.append(color)
elif arrValues[i][j] ==1:
color = residential
rowColor.append(color)
elif arrValues[i][j] ==100:
color = retail
rowColor.append(color)
elif arrValues[i][j] ==1000:
color = road
rowColor.append(color)
arrColor.append(rowColor)
return arrColor
And in the end, this is what I output to Grasshopper:
a = renderList
b = colorList
In grasshopper, this gives me a list of 'Iron.Python.Runtime.List'.
I don't have a working version of grasshopper to hand, but my code for doing this is:
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
from clr import AddReference as addr
addr("Grasshopper")
from System import Object
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
def raggedListToDataTree(raggedList):
rl = raggedList
result = DataTree[object]()
for i in range(len(rl)):
temp = []
for j in range(len(rl[i])):
temp.append(rl[i][j])
#print i, " - ",temp
path = GH_Path(i)
result.AddRange(temp, path)
return result
There is a gist of this here that also has a function that turns trees into lists.
There's still quite a lot wrong with this, no recursion, no error checking, no branch magic, but it does the job in most cases. I'd love to see it improved!
In your case you can just pipe the output that would otherwise give you a runtime list into the raggedListToDataTree function.

Categories

Resources