How to get instance objects to change position horizontally - python

I'm creating an instance python command where the primary purpose is to generate objects in neat horizontal rows. Even though I can randomize rotation and set the range, I still can't figure out how to get the objects to appear in horizontal rows.
I already tried to use the xform command to get the objects to move along the x coordinates.
import maya.cmds as MC
import random as RN
def ChainmailGenerator():
thing = MC.ls(sl=True)
print thing
if not thing:
MC.error (" ***Error - you need to select an object *** ")
# create a group node
grp = MC.group(empty=True, name=thing[0] + '_grp#')
#Loop though the items below with the range of a
for i in range (0,25):
instanceObj = MC.instance(thing, name=thing[0]+'instance#', smartTransform=True)
rx = RN.uniform(-1,1)*5
ry = RN.uniform(-1,1)*5
rz = RN.uniform(-1,1)*5
MC.rotate (rx,ry,rz, instanceObj)
MC.xform (r=True, ro=(90, 0, 0) )
tx = 5
MC.xform ( instanceObj, t=(0,15+1,0))
MC.parent (instanceObj,grp)
print "*** chainmail ***"
ChainmailGenerator()
The expectations are for the objects to generate in horizontal increments as if they're forming neat rows.

here is an example to create 10 spheres along x, moving them with xform :
step = 1
tx = 0
for x in range(10):
sphere = cmds.polySphere()[0]
cmds.xform(sphere, t=[tx,0,0])
tx+= step

The reason yours is placing everything in the same place now is because you aren't multiplying it against a value that keeps increasing. Normally you could hard-code some random value to space each one out, but this would yield inconsistent results.
Here's a generic way to go about it that seems to work with any object.
The idea is to use the mesh's bounding box to determine what the spacing should be by looking at the size of its x axis. You can also move it in place with xform, but you do need to include its worldspace parameter so that it doesn't move it relative to its current position.
import maya.cmds as cmds
def cloneInRow(count):
# Get selection.
thing = cmds.ls(sl=True)
if not thing:
cmds.error("You need to select an object")
# Get selection's mesh shape.
mesh = cmds.listRelatives(thing[0], shapes=True, f=True, ni=True, type="mesh")
if not mesh:
cmds.error("Unable to find a mesh on the selected object")
# Determine spacing by looking at object's bounding box. Use its `x` axis size.
bb_min = cmds.getAttr(mesh[0] + ".boundingBoxMin")[0]
bb_max = cmds.getAttr(mesh[0] + ".boundingBoxMax")[0]
spacing = bb_max[0] - bb_min[0]
# Create a root transform to parent to.
grp = cmds.group(empty=True, name=thing[0] + '_grp#')
# Create instance, and move it in place.
for i in range (0, count):
instanceObj = cmds.instance(thing[0], name=thing[0] + 'instance' + str(i), smartTransform=True)
cmds.xform(instanceObj, ws=True, t=(i * spacing, 0, 0))
cmds.parent(instanceObj, grp)
cmds.select(grp)
cloneInRow(10)
With this I can take this crossbow:
And clone any of its objects and get nice spacing:
The only catch is rotation. If your pivot isn't centered to the mesh, then randomizing its rotation will lose its place in space (since rotating would also effects its position!) So if you got weird pivots then it won't look nice when you add back on rotations.

Related

Force an arrow to enter a node from the left in graphviz python

I am trying to make a nice graph (see image), but has a certain requirement. The nodes that look like half rectangles called annotations should have the arrow enter into the node from the left and not from the bottom. Does anyone know how to do this in graphviz python.
Some remarks about the code:
The model object contains edge objects. Each edge object contains a source and a target node which are basically string objects.
I also kept a list of nodes that should have that half rectangle shape called annotations where I use that shape. The only thing I want in short is to get the incoming edge to move to the left.
The exact code can be found below:
g = Digraph('G', filename='./output/model_' + str(mi), format='png',strict=True)
g.graph_attr.update(rankdir='BT')
g.attr('node',shape='note')
sources= list()
targets = list()
annotations=list()
PossibleModel.MAX_DEPTH = 3
"""Let us first see what the possible inputs are"""
"""
for edge in model.get_edges():
sources.append(str(edge.source))
targets.append(str(edge.target))
"""
for edge in model.get_edges():
sources.append(edge.source.small_string2())
targets.append(edge.target.small_string2())
difference = list(set(sources) - set(targets))
print('difference',difference)
for edge in model.get_edges():
edge_contained = ''
for rule in br_rules:
if str(edge.source) in rule or str(edge.target) in rule:
edge_contained += rule + '\n'
if edge_contained == '':
g.edge(edge.source.small_string2(), edge.target.small_string2(),label=str(edge.weight),color='red')
g.edge(edge.source.small_string2(), edge.source.small_string3(),color='black',arrowhead='none',style='dashed')
g.edge(edge.target.small_string2(), edge.target.small_string3(),color='black',arrowhead='none',style='dashed')
annotations.append(edge.source.small_string3())
annotations.append(edge.target.small_string3())
else:
g.attr('edge', color='blue')
g.edge(str(edge.source), str(edge.target), label=edge_contained)
for node in difference:
g.node(node,style='rounded',shape='ellipse')
for node in targets:
g.node(node, shape='box')
for node in annotations:
g.node(node,color='transparent', image='test.png',fontsize='11')`
Use "ports" (see p.18 "node ports" http://www.graphviz.org/pdf/dotguide.pdf and https://graphviz.org/docs/attr-types/portPos/)
So instead of a -> b try a -> b:w (:w indicating west or left side)

How to plot a "heatmap" of thousands of timeseries in python?

I am looking for a way to visualize, for the lack of a better word, the "density" or "heatmap" of some synthetic time series I have created.
I have a loop that creates a list, which are values of one time series. I don't think it matters but just in case, here is the code of what's going on. This is a Markov Process, so with each i, which represents the hour, i create a new value, depending on the former i and state:
for x in range(10000):
start_h = 0
start_s = 1
generated_values_list = []
for i in range(start_h,120):
if i>=24:
i=i%24
print(str(start_s)+" | " +str(i))
pot_value_list = GMM_vals_container_workingdays_spring["State: "+ str(start_s)+", hour: "+str(i)]
if len(pot_value_list)>50:
actual_value = random.choice(pot_value_list)#
#cdf, gmm_x, gmm = GMM_erstellen(pot_value_list,50)
#actual_value = gmm.sample()[0][0][0]
#print("made by GMM")
else:
actual_value = random.choice(pot_value_list)
#print("made not by GMM")
generated_values_list.append(actual_value)
probabilities_next_state = TPMs_WD[i][start_s-1]
next_state = random.choices(states,weights=probabilities_next_state)
start_s = next_state[0]
plt.plot(generated_values_list)
But - I think - the only part that matters is this:
for x in range(10000):
#some code that creates the generated_values_list
plt.plot(generated_values_list)
This creates, as expected a picture like this:
It is not clear from here which are the most common paths so I would like to make values that are hit frequently are more colorful while not so frequent values are rather grey.
I think seaborn library has something for that but I don't seem to understand the docs.

How do I resample a high-resolution GRIB grid to a coarser resolution using xESMF?

I'm trying to resample a set of GRIB2 arrays at 0.25 degree resolution to a coarser 0.5 degree resolution using the xESMF package (xarray's coarsen method does not work here because there is an odd number of coordinates in the latitude).
I have converted the GRIB data to xarray format through the pygrib package, which then subsets out the specific grid I need:
fhr = 96
gridDefs = {
"0.25":
{'url': "https://noaa-gefs-retrospective.s3.amazonaws.com/landsfc.pgrb2.0p25"},
"0.5":
{'url': "https://noaa-gefs-retrospective.s3.amazonaws.com/landsfc.pgrb2.0p50"},
}
fileDefs = {
"0.25":
{'url': "https://noaa-gefs-retrospective.s3.amazonaws.com/GEFSv12/reforecast/2019/2019051900/c00/Days%3A1-10/tmp_pres_2019051900_c00.grib2",
'localfile': "tmp_pres.grib2"},
"0.5":
{'url': "https://noaa-gefs-retrospective.s3.amazonaws.com/GEFSv12/reforecast/2019/2019051900/c00/Days%3A1-10/tmp_pres_abv700mb_2019051900_c00.grib2",
'localfile': "tmp_pres_abv_700.grib2"},
}
def grib_to_xs(grib, vName):
arr = xr.DataArray(grib.values)
arr = arr.rename({'dim_0':'lat', 'dim_1':'lon'})
xs = arr.to_dataset(name=vName)
return xs
gribs = {}
for key, item in gridDefs.items():
if not os.path.exists(item['url'][item['url'].rfind('/')+1:]):
os.system("wget " + item['url'])
lsGrib = pygrib.open(item['url'][item['url'].rfind('/')+1:])
landsea = lsGrib[1].values
gLats = lsGrib[1]["distinctLatitudes"]
gLons = lsGrib[1]["distinctLongitudes"]
gribs["dataset" + key] = xr.Dataset({'lat': gLats, 'lon': gLons})
lsGrib.close()
for key, item in fileDefs.items():
if not os.path.exists(item['localfile']):
os.system("wget " + item['url'])
os.system("mv " + item['url'][item['url'].rfind('/')+1:] + " " + item['localfile'])
for key, item in fileDefs.items():
hold = pygrib.open(item['localfile'])
subsel = hold.select(forecastTime=fhr)
#Grab the first item
gribs[key] = grib_to_xs(subsel[1], "TT" + key)
hold.close()
The above code downloads two constant files (landsfc) at the two grid domains (0.25 and 0.5), then downloads two GRIB files at each of the resolutions as well. I'm trying to resample the 0.25 degree GRIB file (tmp_pres.grib2) to a 0.5 degree domain as such:
regridder = xe.Regridder(ds, gribs['dataset0.5'], 'bilinear')
print(regridder)
ds2 = regridder(ds)
My issue is that I generate two warning messages when trying to use the regridder:
/media/robert/HDD/Anaconda3/envs/wrf-work/lib/python3.8/site-packages/xarray/core/dataarray.py:682: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
return key in self.data
/media/robert/HDD/Anaconda3/envs/wrf-work/lib/python3.8/site-packages/xesmf/backend.py:53: UserWarning: Latitude is outside of [-90, 90]
warnings.warn('Latitude is outside of [-90, 90]')
The output xarray does have the correct coordinates, however the values inside the grid are way off (Outside the maxima/minima of the finer resolution grid), and exhibit these strange banding patterns that make no physical sense.
What I would like to know is, is this the correct process to upscale an array using xEMSF, and if not, how would I address the problem?
Any assistance would be appreciated, thanks!
I would recommend first trying conservative instead of bilinear (it's recommended on their documentation) and maybe check if you're using the parameters correctly because it seems something is wrong, my first guess would be that something you're doing moves the latitud around for some reason, I'm leaving the docs link here and hope someone knows more.
Regridder docs:
https://xesmf.readthedocs.io/en/latest/user_api.html?highlight=regridder#xesmf.frontend.Regridder.__init__
Upscaling recommendation (search for upscaling, there's also a guide for increasing resolution):
https://xesmf.readthedocs.io/en/latest/notebooks/Compare_algorithms.html?highlight=upscaling
Thanks to the documentation links and recommendations provided by MASACR 99, I was able to do some more digging into the xESMF package and to find a working example of resampling methods from the package author (https://github.com/geoschem/GEOSChem-python-tutorial/blob/main/Chapter03_regridding.ipynb), my issue was solved by two changes:
I changed the method from bilinear to conservative (This required also adding in two fields to the input array (boundaries for latitude and longitude).
Instead of directly passing the variable being resampled to the resampler, I instead had to define two fixed grids to create the resampler, then pass individual variables.
To solve the first change, I created a new function to give me the boundary variables:
def get_bounds(arr, gridSize):
lonMin = np.nanmin(arr["lon"].values)
latMin = np.nanmin(arr["lat"].values)
lonMax = np.nanmax(arr["lon"].values)
latMax = np.nanmax(arr["lat"].values)
sizeLon = len(arr["lon"])
sizeLat = len(arr["lat"])
bounds = {}
bounds["lon"] = arr["lon"].values
bounds["lat"] = arr["lat"].values
bounds["lon_b"] = np.linspace(lonMin-(gridSize/2), lonMax+(gridSize/2), sizeLon+1)
bounds["lat_b"] = np.linspace(latMin-(gridSize/2), latMax+(gridSize/2), sizeLat+1).clip(-90, 90)
return bounds
For the second change, I modified the regridder definition and application to use the statically defined grids, then passed the desired variable to resample:
regridder = xe.Regridder(get_bounds(gribs['dataset0.25'], 0.25), get_bounds(gribs['dataset0.5'], 0.5), 'conservative')
print(regridder)
ds2 = regridder(ds)

Find closest point on mesh in specific axis (maya)

Let's say I have one locator above a polyPlane. What I want to do is a lookup or trace from the locator in negative or positive y until it hits the polyPlane and return the position of the closest point/vertex/uv/
I imagine this have been done one million times but the only examples I have found works by locating the closest point based on all axis which in my case is close to useless.
I would appreciate any help I could get!
Edit:
Added image of the difference between the first suggested solution and what I want to achieve
What we can do is use OpenMaya (Maya's API) to loop over the faceVerts gathered in an array, check to see which is shortest distance from the locator position compared to the current facevert, if it is shorter than the last shortest distance, save it as the closestVertex variable.
import maya.OpenMaya as OpenMaya
from pymel.core import *
geo = PyNode('pSphere1')
pos = PyNode('locator1').getRotatePivot(space='world')
nodeDagPath = OpenMaya.MObject()
try:
selectionList = OpenMaya.MSelectionList()
selectionList.add(geo.name())
nodeDagPath = OpenMaya.MDagPath()
selectionList.getDagPath(0, nodeDagPath)
except:
warning('OpenMaya.MDagPath() failed on %s' % geo.name())
mfnMesh = OpenMaya.MFnMesh(nodeDagPath)
pointA = OpenMaya.MPoint(pos.x, pos.y, pos.z)
pointB = OpenMaya.MPoint()
space = OpenMaya.MSpace.kWorld
util = OpenMaya.MScriptUtil()
util.createFromInt(0)
idPointer = util.asIntPtr()
mfnMesh.getClosestPoint(pointA, pointB, space, idPointer)
idx = OpenMaya.MScriptUtil(idPointer).asInt()
faceVerts = [geo.vtx[i] for i in geo.f[idx].getVertices()]
closestVertex = None
minLength = None
for v in faceVerts:
thisLength = (pos - v.getPosition(space='world')).length()
if minLength is None or thisLength < minLength:
minLength = thisLength
closestVertex = v
select(closestVertex)
This could probably be done with python without the API, but if you've got maya, you've got access to the API :)
I hope this helps

Flipping Images

I am trying to complete an image editing task in my learning Python book. I need help with the horizontal flip.
The instructions are: Write a function called "flip_horizontal" which will flip the picture horizontally. That is, the pixel that is on the far right end of the row ends up on the far left of the row and vice versa (remember to preserve RGB order!).
My code does not flip the image horizontally when I open it. Also, how can I write my effects to different files (use the original file and apply one function the original file and output it, and then apply another function to the original file and output it to another file). Please, keep in mind that I am only 11 years old and have a very basic understanding of Python and image editing, it is just an interest of mine.
class PPM(object):
def __init__(self, infile, outfile):
self.infile=infile
self.outfile=outfile
#Read in data of image
data= open(self.infile,"r")
datain=data.read()
splits=datain.split()
#Header info
self.type=splits[0]
self.columns=splits[1]
self.row=splits[2]
self.colour=splits[3]
self.pixels=splits[4:]
def negate_red(self):
for b in range (0, (len(self.pixels)) , 3):
remainder=255-self.colour
def writetofile(self):
dataout= open(self.outfile,"w")
dataout.write(self.type +"\n" + self.columns + "\n" + self.row +"\n"+ self.colour +"\n"+ " ".join (self.pixels))
def grey_scale(self):
if int(self.columns) > 1000:
return "ERROR!! Number of columns is larger than what can be held in a buffer."
else:
for b in range(0, (len(self.pixels)) , 3):
sum = int(self.pixels[b]) + int(self.pixels[b+1]) + int(self.pixels[b+2])
avg = int(sum/3)
self.pixels[b] = str(avg)
self.pixels[b+1] = str(avg)
self.pixels[b+2] = str(avg)
def flatten_red(self):
for colour in range (0,len(self.pixels),3):
self.pixels [colour]=str(0)
#Create a 2d lists with the smaller lists containing the rgb values and append lists of lists
def horizontal_flip(self):
if int(self.columns) > 1000:
return "ERROR!! Number of columns is larger than what can be held in a buffer."
else:
temp_list = []
for b in range(int(self.row)):
column_list = []
column_list += self.pixels[0:int(self.columns) * 3]
self.pixels = self.pixels[int(self.columns) * 3 : ]
temp_list.append(column_list)
#print temp_list
new_list = []
for i in range(int(len(temp_list))):
new_list.append (temp_list[i][0])
new_list.append (temp_list[i][1])
new_list.append (temp_list[i][2])
temp_list[i] = temp_list[i][::-1]
sample= PPM("cake.ppm", "Replica.ppm")
sample.flatten_red()
sample.horizontal_flip()
sample.greyscale()
sample.negate_red()
Imagine a row of pixels.
i.imgur.com/1iZesZL.jpg
Now, what we want to do is to flip it so that the right-most pixel is on the left-most place, right?
So if we have the pixel on the far-left with the coordinates (x,y) then the pixel on the far-right has the coordinates (x+n, y) where n = the width of the picture in pixels.
i.imgur.com/EE7Qj5r.jpg
Now, a horizontal flip would look like this, right?
i.imgur.com/fbNLCuX.jpg
So what we do is we go from the far right and the far left, swap the values of the current pixels and go one step to the right and one step to the left until they meet in the middle.
In pseudo-code this might look something like this:
n = width
x = 0
y = whatever row we're on currently
while n != width/2
temporary = (x,y) # (x,y) refers to a specific pixel in the picture
(x,y) = (n, y)
(n, y) = temporary
n = n-1
x = x+1
Do you think that's enough to solve the rest yourself? Wouldn't want to take the fun out of it :-)
Are you really 11 years old?
It looks like each element of your temp_list is a column of the image to reverse the order of the columns you just have to do temp_list = temp_list[::-1], but you're doing temp_list[i] = temp_list[i][::-1] which I think flips the image up-down (I might have it backwards though). Either way, once you get the flip, you'll need to flatten the image again and replace self.pixels, something like:
pixels = []
for column in temp_list:
pixels.extend(column)
self.pixels = pixels
You're not doing much with new_list, I don't think you need it. Also if you want to save the image to different files, take the filename as an argument to writetofile, if you do that you won't need to have it in __init__, so something like:
class PPM(object):
def __init__(self, infile):
self.infile=infile
# More code here
def writetofile(self, outfile):
dataout = open(outfile,"w")
dataout.write(self.type +"\n" + self.columns + "\n" + self.row +"\n"+ self.colour +"\n"+ " ".join (self.pixels))
dataout.close()
sample = PPM("cake.ppm")
sample.horizontal_flip()
sample.writetofile("Replica1.ppm")
sample.negate_red()
sample.writetofile("Replica2.ppm")
Maybe not for you since you want to practice but I came here looking for a solution to the same Problem after Research I found this and wanted to share the following:
OpenCV provides a function to flip an  image.
void flip(array src, array dst, int flipCode)
Flips a 2D array around vertical, horizontal or both axes.
Parameters:
src – The source Array
dst – The destination array; will have the same size and same type as src
flipCode – Specifies how to flip the array: 0 means flipping around the x-axis, positive (e.g., 1) means flipping around y-axis, and negative (e.g., -1) means flipping around both axes.The function flip flips the array in one of three different ways (row and column indices are 0-based).
Example code:
cv.flip(original_image,flip_image,1);

Categories