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
Related
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.)
This code suppose to reduce the distance of initial tour: distan(initial_tour) < distan(best) . Can you help me plz? I 've been trying all day now. Do I need to change my swapping method?
Something goes wrong and the simulated annealing does'not work:
def prob(currentDistance,neighbourDistance,temp):
if neighbourDistance < currentDistance:
return 1.0
else:
return math.exp( (currentDistance - neighbourDistance) / temp)
def distan(solution):
#gives the distance of solution
listax, listay = [], []
for i in range(len(solution)):
listax.append(solution[i].x)
listay.append(solution[i].y)
dists = np.linalg.norm(np.vstack([np.diff(np.array(listax)), np.diff(np.array(listay))]), axis=0)
cumsum_dist = np.cumsum(dists)
return cumsum_dist[-1]
#simulated annealing
temp = 1000000
#creating initial tour
shuffle(greedys)
initial_tour=greedys
print (distan(initial_tour))
current_best = initial_tour
best = current_best
while(temp >1 ):
#create new neighbour tour
new_solution= current_best
#Get a random positions in the neighbour tour
tourPos1=random.randrange(0, len(dfar))
tourPos2=random.randrange(0, len(dfar))
tourCity1=new_solution[tourPos1]
tourCity2=new_solution[tourPos2]
#swapping
new_solution[tourPos1]=tourCity2
new_solution[tourPos2]=tourCity1
#get distance of both current_best and its neighbour
currentDistance = distan(current_best)
neighbourDistance = distan(new_solution)
# decide if we should accept the neighbour
# random.random() returns a number in [0,1)
if prob(currentDistance,neighbourDistance,temp) > random.random():
current_best = new_solution
# keep track of the best solution found
if distan(current_best) < distan(best):
best = current_best
#Cool system
temp = temp*0.99995
print(distan(best))
Your problem is in the first line of your while loop, where you write
new_solution= current_best
What this does is puts a reference to the current_best list into new_solution. This means that when you change new_solution, you're actually changing current_best as well, which was not your intention.
The problem could be solved by replacing the problematic line with one that copies the list into a new list, like so:
new_solution = list(current_best)
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)
I'm trying to compare the locations of vertices on one mesh to another and generate a list of paired vertices, (the ultimate purpose is to pair up vertices on a neck geo with the top verts of a body geo.)
The way I'm 'pairing' them is to just compare the distances between all vertices in both meshes and then match up the closest ones to eachother by ordering them in separate lists, (neck_geo_verts[0] is paired with body_geo_verts[0].)
I want to use OpenMaya as I've heard it considerably faster than cmds.xform.
Here's my code so far getting the verts, although it's using cmds and not the Maya API. I am having a really tough time finding what I need from the Maya documentation.
# The user selects an edge on both the bottom of the neck and top of the body, then this code gets all the vertices in an edge border on both of those geos and populates two lists with the vertices
import maya.cmds as mc
import maya.api.OpenMaya as om
import re
mc.unloadPlugin('testingPlugin.py')
mc.loadPlugin('testingPlugin.py')
def main():
geoOneVerts = []
geoTwoVerts = []
edges = cmds.ls(selection=True, sn=True)
geoOneEdgeNum = re.search(r"\[([0-9_]+)\]", edges[0])
geoTwoEdgeNum = re.search(r"\[([0-9_]+)\]", edges[1])
cmds.polySelect(add=True, edgeBorder=int(geoOneEdgeNum.group(1)))
geoOneEdgeBorder = cmds.ls(selection=True, sn=True)
geoOneEdgeVerts = cmds.polyInfo(edgeToVertex=True)
for vertex in geoOneEdgeVerts:
vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
geoOneVerts.append(vertexPairNums.group(1))
geoOneVerts.append(vertexPairNums.group(2))
cmds.polySelect(replace=True, edgeBorder=int(geoTwoEdgeNum.group(1)))
geoTwoEdgeBorder = cmds.ls(selection=True, sn=True)
geoTwoEdgeVerts = cmds.polyInfo(edgeToVertex=True)
for vertex in geoTwoEdgeVerts:
vertexPairNums = re.search(r":\s*([0-9_]+)\s*([0-9_]+)", vertex)
geoTwoVerts.append(vertexPairNums.group(1))
geoTwoVerts.append(vertexPairNums.group(2))
geoOneVerts = list(set(geoOneVerts))
geoTwoVerts = list(set(geoTwoVerts))
# How do I use OpenMaya to compare the distance from the verts in both lists?
main()
EDIT: This code gives me two lists filled with the DAG names of vertices on two meshes. I'm unsure how to get the positions of those vertices to compare the distance between the vertices in both lists and I'm also unsure if I should be using maya.cmds for this as opposed to maya.api.OpenMaya considering the amount of vertices I'm going to be operating on.
EDIT2: Thanks to Theodox and hundreds of searches for the help. I ended up making a version that worked using boundary vertices and one that assumed paired vertices on both meshes would be in identical global space. Both of which I chose to use the Maya API and forewent Maya Commands completely for performance reasons.
Vesion1 (Using Boundary Verts):
import maya.OpenMaya as om
def main():
geo1Verts = om.MFloatPointArray()
geo2Verts = om.MFloatPointArray()
selectionList = om.MSelectionList()
om.MGlobal.getActiveSelectionList(selectionList)
geo1SeamVerts = getSeamVertsOn(selectionList, 1)
geo2SeamVerts = getSeamVertsOn(selectionList, 2)
pairedVertsDict = pairSeamVerts(geo1SeamVerts, geo2SeamVerts)
def getSeamVertsOn(objectList, objectNumber):
count = 0
indexPointDict = {}
selectedObject = om.MObject()
iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
while not iter.isDone():
count += 1
connectedVerts = om.MIntArray()
if (count != objectNumber):
iter.next()
else:
iter.getDependNode(selectedObject)
vertexIter = om.MItMeshVertex(selectedObject)
while not vertexIter.isDone():
if (vertexIter.onBoundary()):
vertex = om.MPoint()
vertex = vertexIter.position()
indexPointDict[int(vertexIter.index())] = vertex
vertexIter.next()
return indexPointDict
def pairSeamVerts (dictSeamVerts1, dictSeamVerts2):
pairedVerts = {}
if (len(dictSeamVerts1) >= len(dictSeamVerts2)):
for vert1 in dictSeamVerts1:
distance = 0
closestDistance = 1000000
vertPair = 0
for vert2 in dictSeamVerts2:
distance = dictSeamVerts1[vert1].distanceTo(dictSeamVerts2[vert2])
if (distance < closestDistance):
closestDistance = distance
vertPair = vert2
pairedVerts[vert1] = vertPair
return (pairedVerts)
else:
for vert1 in dictSeamVerts2:
distance = 0
closestDistance = 1000000
vertPair = 0
for vert2 in dictSeamVerts1:
distance = dictSeamVerts2[vert1].distanceTo(dictSeamVerts1[vert2])
if (distance < closestDistance):
closestDistance = distance
vertPair = vert2
pairedVerts[vert1] = vertPair
return (pairedVerts)
main()
Version2 (Assuming Paired Vertices Would Share a Global Space):
import maya.OpenMaya as om
def main():
selectionList = om.MSelectionList()
om.MGlobal.getActiveSelectionList(selectionList)
meshOneVerts = getVertPositions(selectionList, 1)
meshTwoVerts = getVertPositions(selectionList, 2)
meshOneHashedPoints = hashPoints(meshOneVerts)
meshTwoHashedPoints = hashPoints(meshTwoVerts)
matchingVertList = set(meshOneHashedPoints).intersection(meshTwoHashedPoints)
pairedVertList = getPairIndices(meshOneHashedPoints, meshTwoHashedPoints, matchingVertList)
def getVertPositions(objectList, objectNumber):
count = 0
pointList = []
iter = om.MItSelectionList(objectList, om.MFn.kGeometric)
while not iter.isDone():
count = count + 1
if (count != objectNumber):
iter.next()
dagPath = om.MDagPath()
iter.getDagPath(dagPath)
mesh = om.MFnMesh(dagPath)
meshPoints = om.MPointArray()
mesh.getPoints(meshPoints, om.MSpace.kWorld)
for point in range(meshPoints.length()):
pointList.append([meshPoints[point][0], meshPoints[point][1], meshPoints[point][2]])
return pointList
def hashPoints(pointList):
_clamp = lambda p: hash(int(p * 10000) / 10000.00)
hashedPointList = []
for point in pointList:
hashedPointList.append(hash(tuple(map(_clamp, point))))
return (hashedPointList)
def getPairIndices(hashListOne, hashListTwo, matchingHashList):
pairedVertIndices = []
vertOneIndexList = []
vertTwoIndexList = []
for hash in matchingHashList:
vertListOne = []
vertListTwo = []
for hashOne in range(len(hashListOne)):
if (hashListOne[hashOne] == hash):
vertListOne.append(hashOne)
for hashTwo in range(len(hashListTwo)):
if (hashListTwo[hashTwo] == hash):
vertListTwo.append(hashTwo)
pairedVertIndices.append([vertListOne, vertListTwo])
return pairedVertIndices
main()
API is significantly faster for the distance comparison method, but in this case I think the real killer is likely to be the algorithm. Comparing every vert to ever other is a lot of math.
Probably the easiest thing to do is to come up with a way to hash the vertices instead: turn each xyz point into a single value that can be compared with others without doing the distances: two verts with the same hash would necessarily be in the same position. You can tweak the hash algorithm to quantize the vert positions a bit to account for floating point error at the same time.
Here's a way to hash a point (down to 4 significant digits, which you can tweak by changing the constant in _clamp) :
def point_hash(point):
'''
hash a tuple, probably a cmds vertex pos
'''
_clamp = lambda p: hash(int(p * 10000) / 10000.00)
return hash(tuple(map(_clamp, point)))
As long as both sets of verts are hashed in the same space (presumably world space) identical hashes will mean matched verts. All you'd have to do is to loop through each mesh, creating a dictionary which keyed the vertex hash to the vertex index. Here's a way to do it in cmds:
def vert_dict(obj):
'''
returns a dictionary of hash: index pairs representing the hashed verts of <obj>
'''
results = dict()
verts = cmds.xform(obj + ".vtx[*]", q=True, t=True, ws=True)
total = len(verts)/ 3
for v in range(total):
idx = v * 3
hsh = point_hash (verts[idx: idx + 3])
results[hsh] = v
return results
You can find the intersecting verts - the ones present in both meshes -- by intersecting the keys from both dictionaries. Then convert the matching verts in both meshes back to vertex indices by looking up in the two dictionaries.
Unless the meshes are really heavy, this should be doable without the API since all the work is in the hash function which has no API analog.
The only likely issue would be making sure that the verts were in the same space. You would have to fall back on a distance based strategy if you can't get the verts into the same space for some reason.
If you want to get a more useable result from the op's version 2 script (instead of returning nested and combined lists), you could do something like the following:
indices = lambda itr, val: (i for i, v in enumerate(itr) if v==val) #Get the index of each element of a list matching the given value.
matching = set(hashA).intersection(hashB)
return [i for h in matching
for i in zip(indices(hashA, h), indices(hashB, h))]
which will return a list of two element tuples representing the matched vertex pairs:
[(119, 69), (106, 56), (82, 32), (92, 42), ...
Also, you can use om.MSpace.kObject to compare the two mesh objects in local space depending on your specific needs.
this probably leads to scipy/numpy, but right now I'm happy with any functionality as I couldn't find anything in those packages. I have a matrix that contains data for a multi-variate distribution (let's say, 2, for the fun of it). Is there any function to compute (higher) moments of that? All I could find was numpy.mean() and numpy.cov() :o
Thanks :)
/edit:
So some more detail: I have multivariate data, that is, a matrix where rows display variables and columns observations. Now I would like to have a simple way of computing the joint moments of that data, as defined in http://en.wikipedia.org/wiki/Central_moment#Multivariate_moments .
I'm pretty new to python/scipy so I'm not sure I'd be the best person to code this one up, especially for the n-variables case (note that the wikipedia definition is for n=2), and I kind of expected there to be some out-of-the-box thing to use as I thought this would be a standard problem.
/edit2:
Just for the future, in case someone wants to do something similar, the following code (which is still under review) should give the sample equivalent of the raw moments E(X^2), E(Y^2), etc. It only works for two variables right now, but it should be extendable if one feels the need. If you see some mistakes or unclean/unpython-nish code, feel free to comment.
from numpy import *
# this function should return something as
# moments[0] = 1
# moments[1] = mean(X), mean(Y)
# moments[2] = 1/n*X'X, 1/n*X'Y, 1/n*Y'Y
# moments[3] = mean(X'X'X), mean(X'X'Y), mean(X'Y'Y),
# mean(Y'Y'Y)
# etc
def getRawMoments(data, moment, axis=0):
a = moment
if (axis==0):
n = float(data.shape[1])
X = matrix(data[0,:]).reshape((n,1))
Y = matrix(data[1,:]).reshape((n,1))
else:
n = float(data.shape[0])
X = matrix(data[:,0]).reshape((n,1))
Y = matrix(data[:,1]).reshape((n,11))
result = 1
Z = hstack((X,Y))
iota = ones((1,n))
moments = {}
moments[0] = 1
#first, generate huge-ass matrix containing all x-y combinations
# for every power-combination k,l such that k+l = i
# for all 0 <= i <= a
for i in arange(1,a):
if i==2:
moments[i] = moments[i-1]*Z
# if even, postmultiply with X.
elif i%2 == 1:
moments[i] = kron(moments[i-1], Z.T)
# Else, postmultiply with X.T
elif i%2==0:
temp = moments[i-1]
temp2 = temp[:,0:n]*Z
temp3 = temp[:,n:2*n]*Z
moments[i] = hstack((temp2, temp3))
# since now we have many multiple moments
# such as x**2*y and x*y*x, filter non-distinct elements
momentsDistinct = {}
momentsDistinct[0] = 1
for i in arange(1,a):
if i%2 == 0:
data = 1/n*moments[i]
elif i == 1:
temp = moments[i]
temp2 = temp[:,0:n]*iota.T
data = 1/n*hstack((temp2))
else:
temp = moments[i]
temp2 = temp[:,0:n]*iota.T
temp3 = temp[:,n:2*n]*iota.T
data = 1/n*hstack((temp2, temp3))
momentsDistinct[i] = unique(data.flat)
return momentsDistinct(result, axis=1)