In Blender 2.6 API, PoseBone is used for animating a bone. PoseBone.matrix is one way to do that. API says PoseBone.matrix is in "object space".
http://www.blender.org/documentation/blender_python_api_2_63_5/bpy.types.PoseBone.html#bpy.types.PoseBone.matrix
PoseBone.matrix is nothing I've seen at all.
I still can't get my animation importer working.
What's the deal with PoseBone.matrix? In Blender 2.4 API there were two matrices: one in local space, one in armature space.
But the new PoseBone.matrix is neither! It's not a local matrix:
Position isn't local, it's global.
But rotation
<Euler (x=1.5708, y=-0.7854, z=-0.0000), order='XYZ'>
is.
So what is this "object space" that the API says PoseBone.matrix is in?
I'm trying to assign my armature-space matrices to a hierarchial armature and I can't get them right.
I tried decomposing the matrices I have, undoing parent rotations then recomposing the matrix again before setting it as "PoseBone.matrix".
It just doesn't work.
oldmatrix = myMatrix
loc, rot, scale = oldmatrix.decompose()
#rot = rot * pose.bones[bonename].parent.rotation_quaternion.conjugated()
for i in pose.bones[bonename].parent_recursive:
rot = rot * i.conjugated()
newmatrix = rot.to_matrix().to_4x4()
newmatrix[0][3] = loc.x
newmatrix[1][3] = loc.y
newmatrix[2][3] = loc.z
pose.bones[bonename].matrix = newmatrix
EDIT:
I found the solution that transforms these Object Space transforms to Bone Local Space.
To cut the long story short - here's a code snippet that accomplishes that task:
poseBone = C.object.pose.bones[2] # <----- set your bone here
# poseBone.matrix is in object space - we need to convert it to local space
if poseBone.parent is not None:
parentRefPoseMtx = poseBone.parent.bone.matrix_local
boneRefPoseMtx = poseBone.bone.matrix_local
parentPoseMtx = poseBone.parent.matrix
bonePoseMtx = poseBone.matrix
boneLocMtx = ( parentRefPoseMtx.inverted() * boneRefPoseMtx ).inverted() * ( parentPoseMtx.inverted() * bonePoseMtx )
else:
boneRefPoseMtx = poseBone.bone.matrix_local
bonePoseMtx = poseBone.matrix
boneLocMtx = boneRefPoseMtx.inverted() * bonePoseMtx
loc, rot, scale = boneLocMtx.decompose()
MY OLD QUESTION:
Have you found the solution yet?
I'm dealing with exactly the same problem.
Object space matrix should be the bone local space matrix expressed with respect to the parent's object space matrix, so:
boneOS = parentOS * boneLS
But as you noticed - it doesn't give the correct solution. The bone is still offset.
One thing that came to my mind was that it's offset by the rest pose transform, so I tried rolling it back, but that didn't work either, but perhaps the equations I used were wrong.
Here's how I tried calculating the local space transform of a pose bone ( let's assume bone 1 is the bone of my interest, and bone 0 is its parent, and that 0 doesn't have a parent ):
boneOS = C.object.pose.bones[2].matrix
parentBoneOS = C.object.pose.bones[2].parent.matrix
boneRP = C.object.pose.bones[2].bone.matrix_local # rest pose matrix in bone local space
parentBoneRP = C.object.pose.bones[1].bone.matrix_local # parent bone's rest pose matrix in bone local space
boneLS = ( parentBoneRP * boneRP ).inverted() * parentOS.inverted() * boneOS
The matrix is in object-space and it should behave like you expect from your code. What's happening is that the bones' matrices aren't being updated immediately after changing the matrix. So far I've found a hack-solution of updating the entire scene after every bone with
bpy.context.scene.update()
Related
I am trying to create a volume in Gmsh (using Python API) by cutting some small cylinders from a bigger one.
When I do that, I expect to have one surface for each cutted region, instead, I get the result in the figure. I have highlighted in red the surfaces that give me the problem (some cutted regions behave as expected), as you can see, instead of one surface I get two, that sometimes aren't even equal.
gmsh creates more surfaces than expected:
So, my questions are:
Why gmsh behaves like that?
How can I fix this as I need predictable behavior?
Below is the code I used to generate the geometry.
The code to work requires some parameters such as core_height, core_inner_radius and core_outer_radius, the number of small cylinders and their radius.
gmsh.initialize(sys.argv)
#gmsh.initialize()
gmsh.clear()
gmsh.model.add("circle_extrusion")
inner_cyl_tag = 1
outer_cyl_tag = 2
inner_cyl = gmsh.model.occ.addCylinder(0,0,0, 0, 0, core_height, core_inner_radius, tag = inner_cyl_tag)
outer_cyl = gmsh.model.occ.addCylinder(0,0,0, 0, 0, core_height, core_outer_radius, tag = outer_cyl_tag)
core_tag = 3
cut1 = gmsh.model.occ.cut([(3,outer_cyl)],[(3,inner_cyl)], tag = core_tag)
#create a set of filled cylinders
#set position
angle_vector = np.linspace(0,2*np.pi,number_of_hp+1)
pos_x = hp_radial_position*np.cos(angle_vector)
pos_y = hp_radial_position*np.sin(angle_vector)
pos_z = 0.0
#cut one cylinder at the time and assign the new core tag
for ii in range(0,len(angle_vector)):
old_core_tag = core_tag
heat_pipe = gmsh.model.occ.addCylinder(pos_x[ii], pos_y[ii], pos_z, 0, 0, core_height,hp_outer_radius, tag =-1)
core_tag = heat_pipe+1
core = gmsh.model.occ.cut([(3,old_core_tag)],[(3,heat_pipe)], tag = core_tag)
gmsh.model.occ.synchronize()
#get volume entities and assign physical groups
volumes = gmsh.model.getEntities(dim=3)
solid_marker = 1
gmsh.model.addPhysicalGroup(volumes[0][0], [volumes[0][1]],solid_marker)
gmsh.model.setPhysicalName(volumes[0][0],solid_marker, "solid_volume")
#get surfaces entities and apply physical groups
surfaces = gmsh.model.getEntities(dim=2)
surface_markers= np.arange(1,len(surfaces)+1,1)
for ii in range(0,len(surfaces)):
gmsh.model.addPhysicalGroup(2,[surfaces[ii][1]],tag = surface_markers[ii])
#We finally generate and save the mesh:
gmsh.model.mesh.generate(3)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) #save in ASCII 2 format
gmsh.write(mesh_name+".msh")
# Launch the GUI to see the results:
#if '-nopopup' not in sys.argv:
# gmsh.fltk.run()
gmsh.finalize()
I do not think that you have additional surfaces in the sense of gmsh.model.occ surfaces. To me this looks like your volume mesh is sticking out of your surface mesh, i.e. volume and surface mesh do not fit together.
Here is what I did to check your case:
First I added the following lines at the beginning of our code to get a minimum working example:
import gmsh
import sys
import numpy as np
inner_cyl_tag = 1
outer_cyl_tag = 2
core_height = 1
core_inner_radius = 0.1
core_outer_radius = 0.2
number_of_hp = 5
hp_radial_position = 0.1
hp_outer_radius = 0.05
What I get with this code is the following:
To visualize it like this go to "Tools"-->"Options"-->"Mesh" and check "2D element faces", "3D element edges" and "3D element faces".
You can see that there are some purple triangles sticking out of the green/yellowish surfaces triangles of the inner surfaces.
You could try to visualize your case the same way and check <--> uncheck the "3D element faces" a few times.
So here is the solution for this behaviour, I did not know that gmsh behaves like this myself. It seems that when you create your mesh and refine it the refinement will be applied on the 2D surface mesh and the 3D volume mesh seperately, which means that those two meshes are not connected after the refinement anymore. What I did next was to try what happens if you create the 2D mesh only, refine it, and then create the 3D mesh, i.e.:
replace:
gmsh.model.mesh.generate(3)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
by:
gmsh.model.mesh.generate(2)
gmsh.model.mesh.refine()
gmsh.model.mesh.refine()
gmsh.model.mesh.generate(3)
The result then looks like this:
I hope that this was actually your problem. But in future it would be good if you could provide us a minimum working example of code that we can copy-paste and get the same visualization you showed us in your image.
I have a gray scale image that I want to rotate. However, I need to do optimization on it. Therefore, I cannot use pillow or opencv.
I want to reshape this image using python with numpy.reshape into an one dimensional vector (where I use the default settings C-style reshape).
And thereafter, I want to rotate this image around a point using matrix multiplication and addition, i.e. it should be something like
rotated_image_vector = A # vector + b # (or the equivalent in homogenious coordinates).
After this operation I want to reshape the outcome back to two dimensions and have the rotated image.
It would be best if it would as well use linear interpolation between the pixels that do not fit exactly to an other pixel.
The mathematical theory tells it is possible, and I believe there is a very elegant solution to this problem, but I do not see how to create this matrix. Did anyone already have this problem or sees an immediate solution?
Thanks a lot,
Eike
I like your approach but there is a slight misconception in it. What you want to transform are not the pixel values themselves but the coordinates. So you don't reshape your image but rather do a np.indices on it to obtain coordinates to each pixel. For those a rotation around a point looks like
rotation_matrix#(coordinates-fixed_point)+fixed_point
except that I have to transpose a bit to get the dimensions to align. The cove below is a slight adoption of my code in this answer.
As an example I am going to use the Wikipedia-logo-v2 by Nohat. It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
First I read in the picture, swap x and y axis to not get mad and rotate the coordinates as described above.
import numpy as np
import matplotlib.pyplot as plt
import itertools
image = plt.imread('wikipedia.jpg')
image = np.swapaxes(image,0,1)/255
fixed_point = np.array(image.shape[:2], dtype='float')/2
points = np.moveaxis(np.indices(image.shape[:2]),0,-1).reshape(-1,2)
a = 2*np.pi/8
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
Now I set up a little class to interpolate between the pixels that do not fit exactly to an other pixel. And finally I swap the axis back and plot it.
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [(1-(x % 1)).reshape(*x.shape,1), (x % 1).reshape(*x.shape,1)]
weights_y = [(1-(y % 1)).reshape(*x.shape,1), (y % 1).reshape(*x.shape,1)]
start_x = np.floor(x)
start_y = np.floor(y)
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
image_model = Image_knn()
image_model.fit(image)
transformed_image = image_model.predict(*rotated_coordinates.T).reshape(*image.shape)
plt.imshow(np.swapaxes(transformed_image,0,1))
And I get a result like this
Possible Issue
The artifact in the bottom left that looks like one needs to clean the screen comes from the following problem: When we rotate it can happen that we don't have enough pixels to paint the lower left. What we do by default in image_knn is to clip the coordinates to an area where we have information. That means when we ask image knn for pixels coming from outside the image it gives us the pixels at the boundary of the image. This looks good if there is a background but if an object touches the edge of the picture it looks odd like here. Just something to keep in mind when using this.
Thank you for your answer!
But actually it is not a misconception that you could let this roation be represented by a matrix multiplication with the reshaped vector.
I used your code to generate such a matrix (its surely not the most efficient way but it works, most likely you see a more efficient implementation immediately XD. You see I really need it as a matix multiplication :-D).
What I basically did is to generate the representation matrix of the linear transformation, by computing how every of the 100*100 basis images (i.e. the image with zeros everywhere und a one) is mapped by your transformation.
import sys
import numpy as np
import matplotlib.pyplot as plt
import itertools
angle = 2*np.pi/6
image_expl = plt.imread('wikipedia.jpg')
image_expl = image_expl[:,:,0]
plt.imshow(image_expl)
plt.title("Image")
plt.show()
image_shape = image_expl.shape
pixel_number = image_shape[0]*image_shape[1]
rot_mat = np.zeros((pixel_number,pixel_number))
for i in range(pixel_number):
vector = np.zeros(pixel_number)
vector[i] = 1
image = vector.reshape(*image_shape)
fixed_point = np.array(image.shape, dtype='float')/2
points = np.moveaxis(np.indices(image.shape),0,-1).reshape(-1,2)
a = -angle
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
x,y = rotated_coordinates.T
image = image.astype('float')
weights_x = [(1-(x % 1)).reshape(*x.shape), (x % 1).reshape(*x.shape)]
weights_y = [(1-(y % 1)).reshape(*x.shape), (y % 1).reshape(*x.shape)]
start_x = np.floor(x)
start_y = np.floor(y)
transformed_image_returned = sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
rot_mat[:,i] = transformed_image_returned
if i%100 == 0: print(int(100*i/pixel_number), "% finisched")
plt.imshow((rot_mat # image_expl.reshape(-1)).reshape(image_shape))
Thank you again :-)
I'd like to render an ASCII art world map given this GeoJSON file.
My basic approach is to load the GeoJSON into Shapely, transform the points using pyproj to Mercator, and then do a hit test on the geometries for each character of my ASCII art grid.
It looks (edit: mostly) OK when centered one the prime meridian:
But centered on New York City (lon_0=-74), and it suddenly goes haywire:
I'm fairly sure I'm doing something wrong with the projections here. (And it would probably be more efficient to transform the ASCII map coordinates to lat/lon than transform the whole geometry, but I am not sure how.)
import functools
import json
import shutil
import sys
import pyproj
import shapely.geometry
import shapely.ops
# Load the map
with open('world-countries.json') as f:
countries = []
for feature in json.load(f)['features']:
# buffer(0) is a trick for fixing polygons with overlapping coordinates
country = shapely.geometry.shape(feature['geometry']).buffer(0)
countries.append(country)
mapgeom = shapely.geometry.MultiPolygon(countries)
# Apply a projection
tform = functools.partial(
pyproj.transform,
pyproj.Proj(proj='longlat'), # input: WGS84
pyproj.Proj(proj='webmerc', lon_0=0), # output: Web Mercator
)
mapgeom = shapely.ops.transform(tform, mapgeom)
# Convert to ASCII art
minx, miny, maxx, maxy = mapgeom.bounds
srcw = maxx - minx
srch = maxy - miny
dstw, dsth = shutil.get_terminal_size((80, 20))
for y in range(dsth):
for x in range(dstw):
pt = shapely.geometry.Point(
(srcw*x/dstw) + minx,
(srch*(dsth-y-1)/dsth) + miny # flip vertically
)
if any(country.contains(pt) for country in mapgeom):
sys.stdout.write('*')
else:
sys.stdout.write(' ')
sys.stdout.write('\n')
I made edit at the bottom, discovering new problem (why there is no Canada and unreliability of Shapely and Pyproj)
Even though its not exactly solving the problem, I believe this attitude has more potential than using pyproc and Shapely and in future, if you will do more Ascii art, will give you more possibilites and flexibility. Firstly I will write pros and cons.
PS: Initialy I wanted to find problem in your code, but I had problems with running it, because pyproj was returning me some error.
PROS
1) I was able to extract all points (Canada is really missing) and rotate image
2) The processing is very fast and therefore you can create Animated Ascii art.
3) Printing is done all at once without need to loop
CONS (known Issues, solvable)
1) This attitude is definetly not translating geo-coordinates correctly - too plane, it should look more spherical
2) I didnt take time to try to find out solution to filling the borders, so only borders has '*'. Therefore this attitude needs to find algorithm to fill the countries. I think it shouldnt be problem since the JSON file contains countries separated
3) You need 2 extra libs beside numpy - opencv(you can use PIL instead) and Colorama, because my example is animated and I needed to 'clean' terminal by moving cursor to (0,0) instead of using os.system('cls')
4) I made it run only in python 3. In python 2 it works too but I am getting error with sys.stdout.buffer
Change font size on terminal to lowest point so the the printed chars fit in terminal. Smaller the font, better resolution
The animation should look like the map is 'rotating'
I used little bit of your code to extract the data. Steps are in the commentaries
import json
import sys
import numpy as np
import colorama
import sys
import time
import cv2
#understand terminal_size as how many letters in X axis and how many in Y axis. Sorry not good name
if len(sys.argv)>1:
terminal_size = (int(sys.argv[1]),int(sys.argv[2]))
else:
terminal_size=(230,175)
with open('world-countries.json') as f:
countries = []
minimal = 0 # This can be dangerous. Expecting negative values
maximal = 0 # Expecting bigger values than 0
for feature in json.load(f)['features']: # getting data - I pretend here, that geo coordinates are actually indexes of my numpy array
indexes = np.int16(np.array(feature['geometry']['coordinates'][0])*2)
if indexes.min()<minimal:
minimal = indexes.min()
if indexes.max()>maximal:
maximal = indexes.max()
countries.append(indexes)
countries = (np.array(countries)+np.abs(minimal)) # Transform geo-coordinates to image coordinates
correction = np.abs(minimal) # because geo-coordinates has negative values, I need to move it to 0 - xaxis
colorama.init()
def move_cursor(x,y):
print ("\x1b[{};{}H".format(y+1,x+1))
move = 0 # 'rotate' the globe
for i in range(1000):
image = np.zeros(shape=[maximal+correction+1,maximal+correction+1]) #creating clean image
move -=1 # you need to rotate with negative values
# because negative one are by numpy understood. Positive one will end up with error
for i in countries: # VERY STRANGE,because parsing the json, some countries has different JSON structure
if len(i.shape)==2:
image[i[:,1],i[:,0]+move]=255 # indexes that once were geocoordinates now serves to position the countries in the image
if len(i.shape)==3:
image[i[0][:,1],i[0][:,0]+move]=255
cut = np.where(image==255) # Bounding box
if move == -1: # creating here bounding box - removing empty edges - from sides and top and bottom - we need space. This needs to be done only once
max_x,min_x = cut[0].max(),cut[0].min()
max_y,min_y = cut[1].max(),cut[1].min()
new_image = image[min_x:max_x,min_y:max_y] # the bounding box
new_image= new_image[::-1] # reverse, because map is upside down
new_image = cv2.resize(new_image,terminal_size) # resize so it fits inside terminal
ascii = np.chararray(shape = new_image.shape).astype('|S4') #create container for asci image
ascii[:,:]='' #chararray contains some random letters - dunno why... cleaning it
ascii[:,-1]='\n' #because I pring everything all at once, I am creating new lines at the end of the image
new_image[:,-1]=0 # at the end of the image can be country borders which would overwrite '\n' created one step above
ascii[np.where(new_image>0)]='*' # transforming image array to chararray. Better to say, anything that has pixel value higher than 0 will be star in chararray mask
move_cursor(0,0) # 'cleaning' the terminal for new animation
sys.stdout.buffer.write(ascii) # print into terminal
time.sleep(0.025) # FPS
Maybe it would be good to explain what is the main algorithm in the code. I like to use numpy whereever I can. The whole thing is that I pretend that coordinates in the image, or whatever it may be (in your case geo-coordinates) are matrix indexes. I have then 2 Matrixes - Real Image and Charray as Mask. I then take indexes of interesting pixels in Real image and for the same indexes in Charray Mask I assign any letter I want. Thanks to this, the whole algorithm doesnt need a single loop.
About Future posibilities
Imagine you will also have information about terrain(altitude). Let say you somehow create grayscale image of world map where gray shades expresses altitude. Such grayscale image would have shape x,y. You will prepare 3Dmatrix with shape = [x,y,256]. For each layer out of 256 in the 3D matrix, you assign one letter ' ....;;;;### and so on' that will express shade.
When you have this prepared, you can take your grayscale image where any pixel will actually have 3 coordinates: x,y and shade value. So you will have 3 arrays of indexes out of your grascale map image -> x,y,shade. Your new charray will simply be extraction of your 3Dmatrix with layer letters, because:
#Preparation phase
x,y = grayscale.shape
3Dmatrix = np.chararray(shape = [x,y,256])
table = ' ......;;;;;;;###### ...'
for i in range(256):
3Dmatrix[:,:,i] = table[i]
x_indexes = np.arange(x*y)
y_indexes = np.arange(x*y)
chararray_image = np.chararray(shape=[x,y])
# Ready to print
...
shades = grayscale.reshape(x*y)
chararray_image[:,:] = 3Dmatrix[(x_indexes ,y_indexes ,shades)].reshape(x,y)
Because there is no loop in this process and you can print chararray all at once, you can actually print movie into terminal with huge FPS
For example if you have footage of rotating earth, you can make something like this - (250*70 letters), render time 0.03658s
You can ofcourse take it into extreme and make super-resolution in your terminal, but resulting FPS is not that good: 0.23157s, that is approximately 4-5 FPS. Interesting to note is, that this attitude FPS is enourmous, but terminal simply cannot handle printing, so this low FPS is due to limitations of terminal and not of calculation as calculation of this high resolution took 0.00693s, that is 144 FPS.
BIG EDIT - contradicting some of above statements
I accidentaly opened raw json file and find out, there is CANADA and RUSSIA with full correct coordinates. I made mistake to rely on the fact that we both didnt have canada in the result, so I expected my code is ok. Inside JSON, the data has different NOT-UNIFIED structure. Russia and Canada has 'Multipolygon', so you need to iterate over it.
What does it mean? Dont rely on Shapely and pyproj. Obviously they cant extract some countries and if they cant do it reliably, you cant expect them to do anything more complicated.
After modifying the code, everything is allright
CODE: This is how to load the file correctly
...
with open('world-countries.json') as f:
countries = []
minimal = 0
maximal = 0
for feature in json.load(f)['features']: # getting data - I pretend here, that geo coordinates are actually indexes of my numpy array
for k in range((len(feature['geometry']['coordinates']))):
indexes = np.int64(np.array(feature['geometry']['coordinates'][k]))
if indexes.min()<minimal:
minimal = indexes.min()
if indexes.max()>maximal:
maximal = indexes.max()
countries.append(indexes)
...
I'm trying to optimize the porosity distribution of a certain material. I would like to visualize the results. I can visualize the different materials using 'visualize->material' however he gives every material a random color. I would like the least dense materials to be blue and the densest to be red. So the same as for the stress plots.
Is there a way to do this in Abaqus?
If there's no simple way to do this in the GUI, I was wondering would it be possible by using scripting? I tried to change a single color which resulted in the following code:
session.viewports['Viewport: 1'].enableMultipleColors()
session.viewports['Viewport: 1'].setColor(initialColor='#BDBDBD')
cmap=session.viewports['Viewport: 1'].colorMappings['Material']
session.viewports['Viewport: 1'].setColor(colorMapping=cmap)
session.viewports['Viewport: 1'].disableMultipleColors()
session.viewports['Viewport: 1'].enableMultipleColors()
session.viewports['Viewport: 1'].setColor(initialColor='#BDBDBD')
cmap = session.viewports['Viewport: 1'].colorMappings['Material']
cmap.updateOverrides(overrides={'IMPLANT_MATERIAL0':(True, '#FF0000',
'Default', '#FF0000')})
session.viewports['Viewport: 1'].setColor(colorMapping=cmap)
session.viewports['Viewport: 1'].disableMultipleColors()
session.viewports['Viewport: 1'].enableMultipleColors()
session.viewports['Viewport: 1'].setColor(initialColor='#BDBDBD')
cmap = session.viewports['Viewport: 1'].colorMappings['Material']
session.viewports['Viewport: 1'].setColor(colorMapping=cmap)
session.viewports['Viewport: 1'].disableMultipleColors()
If you're looking for something like stress plot visualisation, you'll have to write your own FieldOutput data. It's generally easier to output the data directly to an external visualiser, but it's possible (if not a bit convoluted) to do this in Abaqus.
The general process is this:
Generate a FieldOutput object; syntax is FO = odbModel.steps.values()[-1].frames[-1].FieldOutput(name=data_name, description=data_description, type=SCALAR), where
odbModel is an opened Odb object,
steps.values()[-1] or a named step steps[...] is the step you want to output to,
frames[-1] is the last frame (or a frame of your choice) that you want to output to in this step,
data_name and data_description are strings (for stress contour plots, data_name would be equivalent to the label S in the odb output)
SCALAR is a parameter from the abaqusConstants module
Get the rootAssembly.instance objects, and their associated element elementSets and sectionAssignments which have clear links to a section with a material which has a density attribute.
Update the FieldOutput object with the addData command; syntax is addData(position=CENTROID, instance=instance, labels=labels, data=data)
CENTROID is a parameter from the abaqusConstants module (assuming you just want to have element densities at the element centroid; you can stick them at integration points too, if you really wanted)
instance is the instance associated with the element set (or more generally the region assigned with this material)
labels is an iterable (list, tuple) of integers specifying the element labels of the associated instance for which the data is to be written at
data is an iterable of iterables of floats, specifying the data. In your case, a single density value means that data is an iterable of length-1 iterables, each containing one value of density. The length of data must be equal to the length of labels, as each member of data exactly corresponds to the elementLabel in the same position in labels.
Example script below (WARNING: Heavily recommend backing up the .odb in case something didn't turn out right)
import odbAccess
from abaqusConstants import SCALAR, CENTROID # Import constants
odbModel = odbAccess.openOdb(odb_file_path) # Put the file path of the `odb` in odb_file_path
FO = odbModel.steps.values()[-1].frames[-1].FieldOutput(name='Density', description='', type=SCALAR)
# Loop through `rootAssembly.instances`
for instance in odbModel.rootAssembly.instances.values():
valid_materials = [] # Valid material names which have `density`
# Loop through `instance.sectionAssignments` to check if the associated `section` has a `material` with the attribute `density`
for i in range(len(instance.sectionAssignments)):
sectionName = instance.sectionAssignments[i].sectionName
matName = odbModel.sections[sectionName].material
if hasattr(odbModel.materials[matName], 'density'):
valid_materials.append(matName)
sectionNames = [] # Build list of valid section names which are associated with a material which has the attribute `density`
for s in odbModel.sections.values():
if s.material in valid_materials:
sectionNames.append(s.name)
if sectionNames:
# Loop through `sectionAssignments` and get all the `elementLabels` in the `region` of the `sectionAssignment`
for sa in instance.sectionAssignments:
sa_labels = []
if sa.sectionName in sectionNames:
# Get labels
if sa.region.elements is not None:
for e in sa.region.elements:
sa_labels.append(e.label)
# Get material
matName = odbModel.sections[sa.sectionName].material
sa_data = [(odbModel.materials[matName].density.table[0][0],)]*len(sa_labels)
# Update `fieldOutput` object
FO.addData(position=CENTROID, instance=instance, labels=sa_labels, data=sa_data)
# Save odb model. The FieldOutput object only exists as reference from `FO` unless the odb model is saved.
odbModel.save()
odbModel.close()
odbModel = odbAccess.openOdb(odb_file_path) # Reopen the model for visualisation. If you can't find the data_name in the list, expand the model to the step and frame for which the data is saved.
I don't work with density, but here's an example output for Young's modulus for a model with two materials assigned to various elements.
It's probably not the perfect method but this works.
Limitations:
-You need to manually put in the amount of materials.
-Your materials should be ranked according to density(mat1,mat2->density1
-You should put your material name in the script (in my case it was 'Implant')
Suggestions for improvement are always welcome, this was just quick and dirty.
from math import floor
diminishing_factor = 10 #This factor diminishes the amount of colors to:
amount of materials/diminishing factor. This is necessary
#because apparently abaqus can only handle a limited amount of colors (+-50)
def create_color_lst(amount_of_mat):
color_lst=[]
total_length = 256*4-1 #0 telt ook dus -1
interval = floor(total_length/(amount_of_mat-1)) #*10 because we'll give
10 consequent materials the same color, because abaqus can't handle it
for i in range(0,amount_of_mat):
pos = int(floor(i/diminishing_factor))*diminishing_factor*interval
if pos<256: #Green is rising
col_pos=pos
code = (0,col_pos,255)
elif pos<512: #Blue is diminishing
col_pos=pos-255
code = (0,255,255-col_pos)
elif pos<768:
col_pos = pos - 511
code = (col_pos,255,0)
elif pos<1024:
col_pos = pos - 767
code = (255,255-col_pos,0)
else:
raise ValueError('Color position is too high: '+str(pos))
hex_code='#%02x%02x%02x' % code
color_lst.append(hex_code.upper())
return color_lst
def update_colors(color_lst):
session.viewports['Viewport: 1'].enableMultipleColors()
session.viewports['Viewport: 1'].setColor(initialColor='#BDBDBD')
cmap = session.viewports['Viewport: 1'].colorMappings['Material']
for i in range(0,amount_of_mat):
material = 'IMPLANT_MATERIAL'+str(i)
cmap.updateOverrides(overrides={material:(True, color_lst[i],
'Default', color_lst[i])})
if i%10==0:
print(i)
session.viewports['Viewport: 1'].setColor(colorMapping=cmap)
session.viewports['Viewport: 1'].disableMultipleColors()
amount_of_mat=494 #We can't get this you should always check this! (you
probably could but I'm to lazy to search it)
color_lst = create_color_lst(amount_of_mat) #Creates a list with strings
that contain the color names
update_colors(color_lst) #Updates the display (it's possible that you still
need to go to the display color dialog and press apply)
I have been looking for thi answer but i don't seem to figure it out anywhere, so i hope i could get my answer here...
I'm in Maya Python API and i want to apply a transformation Matrix to a mesh.
This is how i made the mesh:
mesh = om.MFnMesh()
ShapeMesh = cmds.group(em=True)
parentOwner = get_mobject( ShapeMesh )
meshMObj = mesh.create(NumVerts, len(FaceCount), VertArray, FaceCount, FaceArray ,parentOwner)
cmds.sets( ShapeMesh, e=True,forceElement='initialShadingGroup')
defaultUVSetName = ''
defaultUVSetName = mesh.currentUVSetName(-1)
mesh.setUVs ( UArray, VArray, defaultUVSetName )
mesh.assignUVs ( FaceCount, FaceArray, defaultUVSetName )
This is how i create the TFM:
m = struct.unpack("<16f",f.read(64))
mm = om.MMatrix()
om.MScriptUtil.createMatrixFromList(m,mm)
mt = om.MTransformationMatrix(mm)
Basically i read 16 floats and convert them into a Transformation Matrix, however i don't know how to apply the mt matrix to my mesh...
I managed to get the Position,Rotation and Scale from this though, maybe it helps, this way:
translate = mt.translation(om.MSpace.kWorld)
rotate = mt.rotation().asEulerRotation()
scaleUtil = om.MScriptUtil()
scaleUtil.createFromList([0,0,0],3)
scaleVec = scaleUtil.asDoublePtr()
mt.getScale(scaleVec,om.MSpace.kWorld)
scale = [om.MScriptUtil.getDoubleArrayItem(scaleVec,i) for i in range(0,3)]
Now my last step comes in applying this Matrix to the mesh, but i can't find a good way to do it, does someone know how to do this on maya?
Thanks in advance:
Seyren.
Not sure what you mean by applying the matrix to your mesh, but if you want to update the position of each point by transforming them with that matrix, then here you go for a given MFnMesh mesh and a given MMatrix matrix:
import banana.maya
banana.maya.patch()
from maya import OpenMaya
mesh = OpenMaya.MFnMesh.bnn_get('pCubeShape1')
matrix = OpenMaya.MMatrix()
points = OpenMaya.MPointArray()
mesh.getPoints(points)
for i in range(points.length()):
points.set(points[i] * matrix, i)
mesh.setPoints(points)
If you don't want to directly update the points of the mesh, then you need to apply the matrix to the transformation node by retrieving its parent transform and using the MFnTransform::set() method.
Note that I've used in my code snippet a set of extensions that I've wrote and that might be helpful if you're using the Maya Python API. The code is available on GitHub and it also comes with a documentation to give you an idea.