I see someone asked this before (for example: here). So how to automatically set the pivot to the bottom of the model?
To do that:
Step 1: find the bottom point.
Step 2: Set the pivot to the bottom point.
Here is the mel script help you do that:
string $sel[]= `ls -sl`;
//$sel[0] != "" to check if the first item is empty, but `size $sel` == 1 already cover that
if(`size $sel` > 0)
{
int $vtxIdx;
int $vCount[];
float $lowestY = 2147483647.0;
float $crtY = 0.0;
float $pos[];
string $item;
for ($item in $sel)
{
$vCount = `polyEvaluate -vertex $item`; //Get vertex count
for ($vtxIdx = 0; $vtxIdx < $vCount[0]; $vtxIdx++)//Loop through vetex
{
$pos = `xform -q -ws -t ($item+".vtx["+$vtxIdx+"]")`;//Get vertex position
$crtY = $pos[1];
if($crtY < $lowestY)
{
$lowestY = $crtY;//Get the lowest Y
}
}
$pos = `xform -q -ws -t ($item)`;
xform -ws -a -piv $pos[0] $lowestY $pos[2] ($item);
print ($lowestY);
}
}
Usage:
step 1: select the objects that need to set pivot to bottom
step 2: execute the script
The pivot point should be set like this
If your requirement is: the pivot point must be inside the model, then you should edit this script a little bit.
Thank you! I used your code above, 123iamking but I found I wanted to drop pivot to world y0 instead of bounding box.
so I changed some stuff around.
Works on Maya 2020.
string $sel[]= `ls -sl`;
//$sel[0] != "" to check if the first item is empty, but `size $sel` == 1 already cover that
if(`size $sel` > 0)
{
float $pos[];
string $item;
for ($item in $sel)
{
xform -ws -a -cp;
CenterPivot;
$pos = `xform -q -ws -a -piv ($item)`;//Get piv position
xform -ws -a -piv $pos[0] 0 $pos[2] ($item); //set pivot to 0 in world y
}
};
Related
I'm looping through multiple objects, but the loop stops before going to the next object.
Created a loop with condition. If condition is met, it calls a ReduceEdge() function. Problem is it will only iterate once and not go to the next object and repeat the procedure.
global proc ReduceEdge()
{
polySelectEdgesEveryN "edgeRing" 2;
polySelectEdgesEveryN "edgeLoop" 1;
polyDelEdge -cv on;
}
string $newSel[] = `ls -sl`;
for($i = 0; $i < size($newSel); $i++)
{
select $newSel[$i];
int $polyEval[] = `polyEvaluate -e $newSel[$i]`;
int $temp = $polyEval[0];
for($k = 0; $k < $temp; $k++)
{
string $polyInfo[] = `polyInfo -fn ($newSel[$i] + ".f[" + $k + "]")`;
$polyInfo = stringToStringArray($polyInfo[$i]," ");
float $vPosX = $polyInfo[2];
float $vPosY = $polyInfo[3];
float $vPosZ = $polyInfo[4];
if($vPosX == 0 && $vPosY == 0 && $vPosZ == 1.0)
{
select ($newSel[$i] + ".e[" + $k + "]");
ReduceEdge();
}
}
}
Expected results:
If I select 4 cylinders, all their edges will reduce by half the current amount.
Actual results:
When 4 cylinders are selected, only one reduces down to half the edges. The rest stay the same.
Since my comment did help you out, I'll try and give a more thorough explanation.
Your first loop (with $i) iterates over each object in your selection. This is fine.
Your second loop (with $k) iterates over the number of edges for the current object in the loop. So far, so good. Though, I'm wondering if it would be more correct to loop of the number of faces...
Now you ask for an array of all face normals of the face at index $k at object $i, with string $polyInfo[] = `polyInfo -fn ($newSel[$i] + ".f[" + $k + "]")`;.
If you try and print the size and values in $polyInfo, you'll realize you have an array with one element, which is the face normal of the particular face you queried just before. Therefore, it will always be element 0, and not $i, which would increases with every iteration.
I have made a Python/PyMEL version of the script, which may be nice for you to see.
import pymel.core as pm
import maya.mel as mel
def reduceEdge():
mel.eval('polySelectEdgesEveryN "edgeRing" 2;')
mel.eval('polySelectEdgesEveryN "edgeLoop" 1;')
pm.polyDelEdge(cv=True)
def reducePoly():
selection = pm.ls(sl=True)
for obj in selection:
for i, face in enumerate(obj.f):
normal = face.getNormal()
if (normal.x == 0.0 and normal.y == 0.0 and normal.z == 1.0):
pm.select(obj + '.e[' + str(i) + ']')
reduceEdge()
reducePoly()
There are some old animations that I want to reuse, but the old animation use different axis (For example: old: Face negative Z, Y as up; new: Face Y, Negative Z as up). So I fix this by grouping the animation and rotate the group so that it faces the right axis. But when I ungroup, it's only work for current frame (I turn Auto key on).
I have search several forums:
Grouping animated objects, Scaling, Ungrouping
Need help maintaining offset during ungroup
ungrouping but keeping animation
Grouping animated objects, Scaling, Ungrouping
But nothing works.
Ok, it works for the current frame when you ungroup. So to make it work for the entire animation, you can group & un-group for all the frames. Of course I won't do it by hand but I'll do it with script.
proc GetPlaybackRange(string $bone, int $outStartEndTime[])
{
float $arrKey[] = `keyframe -q $bone`;
$arrKeyLength=size($arrKey);
$outStartEndTime[0] = floor($arrKey[0]);
$outStartEndTime[1] = ceil($arrKey[$arrKeyLength-1]);
}
proc UngroupAndGroupNextFrame(int $frame, string $groupName, float $transform[])
{
currentTime $frame ;
ungroup;
currentTime ($frame + 1) ;
group -n $groupName;
xform -worldSpace -matrix
$transform[0]
$transform[1]
$transform[2]
$transform[3]
$transform[4]
$transform[5]
$transform[6]
$transform[7]
$transform[8]
$transform[9]
$transform[10]
$transform[11]
$transform[12]
$transform[13]
$transform[14]
$transform[15]
$groupName;
}
proc UnGroupForAnimation()
{
string $sel[]= `ls -sl`;
string $groupName = $sel[0];
float $transform[];
$transform = `xform -q -worldSpace -matrix $groupName`;
string $bone[] = `listRelatives -children $groupName`;
int $startEndTime[];
GetPlaybackRange($bone[0], $startEndTime);
for($i = $startEndTime[0]; $i <= $startEndTime[1]; $i++)
{
UngroupAndGroupNextFrame($i, $groupName, $transform);
}
currentTime ($startEndTime[1] + 1) ;
ungroup;
timeSliderClearKey;
print ($bone[0] + " range "+$startEndTime[0]+" : "+$startEndTime[1]);
}
UnGroupForAnimation();
Usage of the script:
Step 1. Select the group (the children of the group should be the bone)
Step 2. Run the script.
And that's it.
I have a list of xy points that I'm trying to sum together and identify the centroid, but it only uses the last value in the row. I'm trying to create a centroid for each state, Here's the code:
Total_X1 = 0
Total_Y1 = 0
TotalPop1 = 0
#Cat = "cali"
cntyName1 = "cnty"
stateName1 = "statename"
for row in cursor:
#if row[0] >= : ### for condition that is met
#if row[0]== []:
TheStateName1 = row[0]
thecntyName1 = row[4]
idpoly1 = row[5]
idobject1 = row[6]
stateFIPS1 = row[7]
countyFIPS1 = row[8]
fips1 = row[9]
fipSnum1 = row[10]
fipsNumer1 = row[11]
#totarea = row[12]
XPoint = row [13]
YPoint = row[14]
#print Cat
print TheStateName1
print thecntyName1
print row ### do something with that value!
Total_X1 += row[2] *row[3]
print Total_X1
Total_Y1 += row[1] *row[3]
print Total_Y1
TotalPop1 += row[3]
print TotalPop1
print ""
print "X is: " , Total_X1
print "POP is: " , TotalPop1
centroid_X1 = Total_X1/TotalPop1
print "your x centroid is: ",centroid_X1
print ""
#print Cat
print thecntyName1
print TheStateName1
Any Suggestions, Thanks!
The cursor can only 'see' one row at a time, you have to pull info from that row and store it elsewhere.
loc_list = [(row[0], row[1]) for row in arcpy.da.SearchCursor(dataset, ['X_coord', 'Y_coord'])
Will give you a list of X,Y tuples from your attribute table.
After that you've got multiple options for turning that list of tuples into a spatial dataset before calculating the mean - start by reading the ESRI documentation for arcpy.Point and all the related topics linked, and go from there. If you have 10.3 or above you can use Mean Center once you have a point layer.
You'll probably get a wrong answer if you just take the mean of the X and Y without projecting first, so don't.
I have an AoA construct with four columns and many rows. Following is an example of data (input).
DQ556929 103480190 103480214 154943
DQ540839 103325247 103325275 2484
DQ566549 103322763 103322792 99
DQ699634 103322664 103322694 0
DQ544472 103322664 103322692 373
DQ709105 103322291 103322318 46
DQ705937 103322245 103322273 486
DQ699398 103321759 103321788 1211
DQ710151 103320548 103320577 692251
DQ548430 102628297 102628326 1
DQ558403 102628296 102628321 855795
DQ692476 101772501 101772529 481463
DQ544274 101291038 101291068 484047
DQ723982 100806991 100807020 1
DQ709023 100806990 100807020 3
DQ712307 100806987 100807014 0
DQ709654 100806987 100807012 571051
DQ707370 100235936 100235962 1481849
I want to group and write into a file all the row elements (sequentially).
Conditions are if column four values less than 1000 and minimum two values are next to each other, group them else if the value less than 1000 and lies between the values more than 1000 treat them as single and append separately in the same file and the values which are more than 1000 also write as a block but with out affecting the order of the 2nd and third column.
This file is output of my previous program, now for this I have tried implementing my hands but getting some weird results. Here is my chunk of code, but non functional. Guys I need just help if i am executing my logic well here, I am open for any comments as a beginner. And also correct me anywhere.
my #dataf= sort{ $a->[1]<=> $b->[1]} #data;
#dataf=reverse #dataf;
for(my $i>=0;$i<=$#Start;$i++)
{
print "$sortStart[$i]\n";
my $diff = $sortStart[$i] - $sortStart[$i+1];
$dataf[$i][3]= $diff;
# $IDdiff{$ID[$i]}=$diff;
}
#print Dumper(#dataf);
open (CLUST, ">> ./clustTest.txt" );
for (my $k=0;$k<=$#Start;$k++)
{
for (my $l=0;$l<=3;$l++)
{
# my $tempdataf = shift $dataf[$k][$l];
# print $tempdataf;
if ($dataf[$k][3]<=1000)
{
$flag = 1;
do
{
print CLUST"----- Cluster $clustNo -----\n";
print CLUST"$dataf[$k][$l]\t";
if ($dataf[$k][3]<=1000)
{
$flag1 = 1;
}else {$flag1=0;}
$clustNo++;
}until($flag1==0 && $data[$k][3] > 1000);
if($flag1==0 && $data[$k][3] > 1000)
{
print CLUST"Singlet \n";
print CLUST"$dataf[$k][$l]\t";
next;
}
#print CLUST"$dataf[$k][$l]\t"; ##IDdiff
}
print CLUST"\n";
}
}
Expected output in file:
Singlets
DQ556929 103480190 103480214 154943
DQ540839 103325247 103325275 2484
Cluster1
DQ566549 103322763 103322792 99
DQ699634 103322664 103322694 0
DQ544472 103322664 103322692 373
DQ709105 103322291 103322318 46
DQ705937 103322245 103322273 486
Singlets
DQ699398 103321759 103321788 1211
DQ710151 103320548 103320577 692251
DQ548430 102628297 102628326 1
DQ558403 102628296 102628321 855795
DQ692476 101772501 101772529 481463
DQ544274 101291038 101291068 484047
Cluster2
DQ723982 100806991 100807020 1
DQ709023 100806990 100807020 3
DQ712307 100806987 100807014 0
Singlets
DQ709654 100806987 100807012 571051
DQ707370 100235936 100235962 1481849
This seems to produce the expected output. I'm not sure I understood the specification correctly, so there might be errors and edge cases.
How it works: it remembers what kind of section it's currently outputting ($section, Singlet or Cluster). It accumulates lines in the #cluster array if they belong together, when an incompatible line arrives, the cluster is printed and a new one is started. If the cluster to print has only one member, it's treated as a singlet.
#!/usr/bin/perl
use warnings;
use strict;
my $section = q();
my #cluster;
my $cluster_count = 1;
sub output {
if (#cluster > 1) {
print "Cluster$cluster_count\n";
$cluster_count++;
} elsif (1 == #cluster) {
print $section = 'Singlet', "s\n" unless 'Singlet' eq $section;
}
print for #cluster;
#cluster = ();
}
my $last = 'INF';
while (<>) {
my ($id, $from, $to, $value) = split;
if ($value > 1000 || 1000 < abs($last - $from)) {
output();
} else {
$section = 'Cluster';
}
push #cluster, $_;
$last = $to;
}
output();
I'm looking for a Gremlin version of a customizable PageRank algorithm. There are a few old versions out there, one (from: http://www.infoq.com/articles/graph-nosql-neo4j) is pasted below. I'm having trouble fitting the flow into the current GremlinGroovyPipeline-based structure. What is the modernized equivalent of this or something like it?
$_g := tg:open()
g:load('data/graph-example-2.xml')
$m := g:map()
$_ := g:key('type', 'song')[g:rand-nat()]
repeat 2500
$_ := ./outE[#label='followed_by'][g:rand-nat()]/inV
if count($_) > 0
g:op-value('+',$m,$_[1]/#name, 1.0)
end
if g:rand-real() > 0.85 or count($_) = 0
$_ := g:key('type', 'song')[g:rand-nat()]
end
end
g:sort($m,'value',true())
Another version is available on slide 55 of http://www.slideshare.net/slidarko/gremlin-a-graphbased-programming-language-3876581. The ability to use the if statements and change the traversal based on them is valuable for customization.
many thanks
I guess I'll answer it myself in case somebody else needs it. Be warned that this is not a very efficient PageRank calculation. It should only be viewed as a learning example.
g = new TinkerGraph()
g.loadGraphML('graph-example-2.xml')
m = [:]
g.V('type','song').sideEffect{m[it.name] = 0}
// pick a random song node that has 'followed_by' edge
def randnode(g) {
return(g.V('type','song').filter{it.outE('followed_by').hasNext()}.shuffle[0].next())
}
v = randnode(g)
for(i in 0..2500) {
v = v.outE('followed_by').shuffle[0].inV
v = v.hasNext()?v.next():null
if (v != null) {
m[v.name] += 1
}
if ((Math.random() > 0.85) || (v == null)) {
v = randnode(g)
}
}
msum = m.values().sum()
m.each{k,v -> m[k] = v / msum}
println "top 10 songs: (normalized PageRank)"
m.sort {-it.value }[0..10]
Here's a good reference for a simplified one-liner:
https://groups.google.com/forum/m/#!msg/gremlin-users/CRIlDpmBT7g/-tRgszCTOKwJ
(as well as the Gremlin wiki: https://github.com/tinkerpop/gremlin/wiki)