# polygon creation in VEX - ptnum order

## Recommended Posts

```float theta;
int   res_lon = 10; //longitude resolution
float start_theta = 0;
float end_theta = 0.5;
float theta_step = ((end_theta-start_theta)/float(res_lon));

float phi;
int   res_lat = 10; //latitude resolution
float start_phi = 0;
float end_phi = 0.6;
float phi_step = ((end_phi-start_phi)/float(res_lat));

float r_min = 1 ;
float r_max = 2 ;
int pt_n;

for (int i = 0; i < res_lat ; i++){

for (int j = 0; j < res_lon; j++){

theta = (start_theta + theta_step*j);
vector pos = set( r_min*cos(theta), 0, r_min*sin(theta) );

//latitude arcs

phi = start_phi + phi_step*i;

matrix rot = ident();
vector up = set( 0, 1, 0);
vector rot_axis = cross ( normalize(pos), up);
float angle= phi;
rotate(rot, angle, rot_axis);
pos *=rot;

pt_n = addpoint(geoself(), set(pos)); // ptnum created
}

}

for (int i = 0; i < res_lat ; i++){

for (int j = 0; j < res_lon; j++){

theta = start_theta + theta_step*j;
vector pos = set( r_max*cos(theta), 0, r_max*sin(theta) );

//latitude arcs

phi = start_phi + phi_step*i;

matrix rot = ident();
vector up = set( 0, 1 , 0);
vector rot_axis = cross ( normalize(pos), up);
float angle= phi;
rotate(rot, angle, rot_axis);
pos *=rot;

pt_n = addpoint(geoself(), set(pos)); // ptnum created
}

}

```
Hello OdForce!!!

I am trying to create a nice slice of cake in VEX.
The above code is written in an attribute wrangle SOP, run over Detail.
It generates the points just right, but the face connection is not correct.
I believe this is related to the order of creation of the points, hence the ptnum.

What is the right ptnum order to make the faces connect properly?
Any tips on how to achieve the result is much appreciated
and will be rewarded with a tasty slice of Cake!

thank you!

##### Share on other sites

From what I can tell from your code is that you are making a single primitive and then adding all your points to it. This won't work. The primitive is a polygon or n-gon. So decide how many points each polygon is going to be made of. I would choose four so your output ends up with quads. This means you need to collect up 4 pt_n variables then spit out a face made of them.

The point order should be clock wise. For example, to make a face in the lower right corner of your geometry you would need to use points 0,1,11,10 in that order.

Here is a python example of geometry creation taken from the Moon DEM reader which can be found in the link in my signature. It is close to what you are trying to do, a radial revolution.

```def mapPointsToFaces (passedRowCount, passedColumnCount):
points = geo.points()
if len(points):
for row_index in range(passedRowCount-1):
for column_index in range(passedColumnCount-1):
vertex_index = column_index+(row_index * passedColumnCount)
p1 = points[vertex_index]
p2 = points[vertex_index+1]
vertex_index = column_index+((row_index+1) * passedColumnCount)
p3 = points[vertex_index]
p4 = points[vertex_index+1]
poly = geo.createPolygon()
# Polygon vertex order is clock-wise.
```
Edited by Atom
• 1

##### Share on other sites

You need to create each primitive. Not just single polygon connecting ptnums.

```int   res_lon = chi("res_lon"); // Longitude resolution.
float theta_step = (end_theta - start_theta) / (res_lon-1);

int   res_lat = chi("res_lat"); // Latitude resolution.
float phi_step = (end_phi - start_phi) / (res_lat-1);

float r_min = ch("r_min");
float r_max = ch("r_max");

// Inner surface.
for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_min;
vector axis = cross(normalize(pos), {0,1,0});
rotate(r, phi, axis);

int pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{
addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Outer surface. Same except r_max used and reversed vertex order.
for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_max;
vector axis = cross(normalize(pos), {0,1,0});
rotate(r, phi, axis);

int pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{
// Create a new quad (reverse vertex order).
addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Side surfaces.
for (int i = 1; i < res_lon; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;

// Bottom.
addvertex(geoself(), prim, i - 1 + surface_ptnum);

// Top.
addvertex(geoself(), prim, i + surface_ptnum - res_lon);
addvertex(geoself(), prim, i - 1 + surface_ptnum - res_lon);
addvertex(geoself(), prim, i - 1 + surface_ptnum + surface_ptnum - res_lon);
addvertex(geoself(), prim, i + surface_ptnum + surface_ptnum - res_lon);
}
for (int i = 1; i < res_lat; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;

// Side A.
addvertex(geoself(), prim, i * res_lon - res_lon);
addvertex(geoself(), prim, i * res_lon - res_lon + surface_ptnum);
addvertex(geoself(), prim, i * res_lon + surface_ptnum);

// Side B.
addvertex(geoself(), prim, i * res_lon - 1);
addvertex(geoself(), prim, i * res_lon - 1 + res_lon);
addvertex(geoself(), prim, i * res_lon - 1 + res_lon + surface_ptnum);
addvertex(geoself(), prim, i * res_lon - 1 + surface_ptnum);
}
```

And we finally got our extruded piece of sphere.

Edited by f1480187
• 1

##### Share on other sites

Hi Atom!

it seems more complicated that i thought.

but ill try to work on it a little bit and get back to you!

--------

Hi f1480187!

thank you very much! you were definitely quicker that i was!

Edited by WLVL

##### Share on other sites

vector @vertices [] ;

@vertices = array({0,0,0}, {1,0,0},{0,1,0}, {1,1,0});

int @triangles [];

int pts [];

@triangles = array(0,2,1,1,2,3);

for (int i = 0 ; i <len(@vertices) ; i++) {

float x ;

float y;

x = @vertices.x;

y = @vertices.y;

for ( int t = 0; t < len(@triangles); t++) {

}

}

Edited by deniz

##### Share on other sites

Hello F1,

Would you mind clarify the vertex creation process?

I am not sure i understand how you manage to create the vertex with the right order, especially on the sides.

what im doing now, is create multiple copies of the sphere-slice along a ring. I am missing the side faces.

I basically insert your script in a function, and then i call that function in a for loop.

Thank you for helping!

```int segment(int res_lon;
float start_theta;
float end_theta;
int   res_lat;
float start_phi;
float end_phi;
float r_min;
float r_max;){

// Inner surface.

float theta_step = (end_theta - start_theta) / (res_lon-1);
float phi_step = (end_phi - start_phi) / (res_lat-1);

for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_min;
vector axis = cross(normalize(pos), {0,1,0});
rotate(r, phi, axis);

int pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{

addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Outer surface. Same except r_max used and reversed vertex order.
for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_max;
vector axis = cross(normalize(pos), {0,1,0});
rotate(r, phi, axis);

int pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{
// Create a new quad (reverse vertex order).
addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Side surfaces.
for (int i = 1; i < res_lon; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;

// Bottom.
addvertex(geoself(), prim, i - 1 + surface_ptnum);

// Top.
addvertex(geoself(), prim, i + surface_ptnum - res_lon);
addvertex(geoself(), prim, i - 1 + surface_ptnum - res_lon);
addvertex(geoself(), prim, i - 1 + surface_ptnum + surface_ptnum - res_lon);
addvertex(geoself(), prim, i + surface_ptnum + surface_ptnum - res_lon);
}
for (int i = 1; i < res_lat; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;

// Side A.
addvertex(geoself(), prim, i * res_lon - res_lon);
addvertex(geoself(), prim, i * res_lon - res_lon + surface_ptnum);
addvertex(geoself(), prim, i * res_lon + surface_ptnum);

// Side B.
addvertex(geoself(), prim, i * res_lon - 1);
addvertex(geoself(), prim, i * res_lon - 1 + res_lon);
addvertex(geoself(), prim, i * res_lon - 1 + res_lon + surface_ptnum);
addvertex(geoself(), prim, i * res_lon - 1 + surface_ptnum);
}
//return prim;
}

//RING CREATION

int   res_lon = chi("res_lon"); // Longitude resolution.

int   res_lat = chi("res_lat"); // Latitude resolution.

float r_min = ch("r_min");
float r_max = ch("r_max");
float theta = 0;
float phi = 0;

for ( int j=0; j<3; j++){
segment(res_lon, theta, theta+1, res_lat, phi, phi+.5, r_min, r_max);
theta += 1 + 0.5;
}```

PS. there is not a VEX syntax highlighting on odforce?

##### Share on other sites

You need to keep track of the last created point, to shift starting point from 0 for side surfaces. Otherwise sides will be created for first segment over and over again.

```void segment(int   res_lon;
float start_theta;
float end_theta;
int   res_lat;
float start_phi;
float end_phi;
float r_min;
float r_max)
{
float theta_step = (end_theta - start_theta) / (res_lon - 1);
float phi_step = (end_phi - start_phi) / (res_lat - 1);
int pt; // Keep track of last created point.

// Inner surface.
for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_min;
vector axis = cross(normalize(pos), {0, 1, 0});
rotate(r, phi, axis);

pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{

addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Outer surface. Same except r_max used and reversed vertex order.
for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_max;
vector axis = cross(normalize(pos), {0, 1, 0});
rotate(r, phi, axis);

pt = addpoint(geoself(), pos * r);

if (i > 0 && j > 0)
{
// Create a new quad (reverse vertex order).
addvertex(geoself(), prim, pt - res_lon - 1);
}
}
}

// Side surfaces.
for (int i = 1; i < res_lon; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;
int start_pt = i + pt - surface_ptnum - surface_ptnum + 1;

// Bottom.
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum);

// Top.
addvertex(geoself(), prim, start_pt + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt + surface_ptnum + surface_ptnum - res_lon);
}

for (int i = 1; i < res_lat; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;
int start_pt = i * res_lon + pt - surface_ptnum - surface_ptnum + 1;

// Side A.
addvertex(geoself(), prim, start_pt - res_lon + surface_ptnum);

// Side B.
addvertex(geoself(), prim, start_pt - 1 + res_lon);
addvertex(geoself(), prim, start_pt - 1 + res_lon + surface_ptnum);
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum);
}
}

// Ring creation.
int res_lon = chi("res_lon"); // Longitude resolution.

int res_lat = chi("res_lat"); // Latitude resolution.

float r_min = ch("r_min");
float r_max = ch("r_max");

int nsegs = chi("segments");

for (int i = 0; i < nsegs; i++)
{
float div_theta = (e_theta - s_theta) / nsegs;
float div_s_theta = div_theta * i;
float div_e_theta = div_theta * i + div_theta;

segment(res_lon, div_s_theta, div_e_theta, res_lat, s_phi, e_phi, r_min, r_max);
}```

Here the diff. Cluttered a bit with my autoformatter. int pt was declared on top level, this is how we keep offset for sides creation. Another notable changes starting from line 80, where we start to use this variable. I also changed main for loop logic, since the initial loop was broken anyway, but it is not important, I think, you could build your own argument values and randomize stuff.

Edited by f1480187
• 1

##### Share on other sites

Hi F1,

thank you again! your help is very much appreciated,

So what you are doing is making sure that for every segment in/out the pt order is the same (that's given by the creation order),

and that the vertex assignment order for every prim is also in the same turn (right-hand rule, thumb toward the inside of the segment).

I get lost when i try to understand how you do that.. but I will try my best to reveal the mistery and get back here if I really can't

in the meantime i am expanding the script to make multiple rings, to randomize stuff, and to prepare for animation (randomized based on segments).

I will post the script when I have some nice results.

Thanks for now!

##### Share on other sites

It is easier to understand if you simplify setup:

```for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_min;
vector axis = cross(normalize(pos), {0, 1, 0});
rotate(r, phi, axis);

pt = addpoint(geoself(), pos * r);
}
}```

At first, you create points at positions computed by certain algorithm. I guess, you understand what's going on there, since it is mostly your own code. Algorithm itself is unrelated, it's just returning some position given bounds as minimum and maximum angles and iteration numbers, which is common technique for doing such stuff in for loops. Then you just look at the points and their numbers and decide when and how you will start to create quads:

It seems, you can create a new quad at each point (ignore corner cases for now). Then we need to compute numbers. Given point 11, for example, we need to compute numbers 1, 0 and 10. To get 1, you need to compute and subtract some value equal to 10. It is necessary to decide this value based on our parameters, rather than hardcode it. It turns out, that this value is the length of the point's row that we specified. Apply same guessing for the rest points going counter-clockwise to compute numbers 0 and 10. Then we create a new prim after each point created:

```int prim = addprim(geoself(), "poly");
addvertex(geoself(), prim, pt - res_lon - 1);

Returning to corner cases, we must decide how to skip creation of quads when there are not enough points. So, we must skip 0, 1, ..., 9 and 0, 10, ..., 90. In this points either i of j variables are equal to zero. All we need is to add such check:

```if (i > 0 && j > 0)
{
}```

And final code:

```for (int i = 0; i < res_lat ; i++)
{
for (int j = 0; j < res_lon; j++)
{
// Calculate longitude arc point position.
float theta = start_theta + theta_step * j;
float phi = start_phi + phi_step * i;

matrix r = ident();
vector pos = set(cos(theta), 0, sin(theta)) * r_min;
vector axis = cross(normalize(pos), {0,1,0});
rotate(r, phi, axis);

int pt = addpoint(geoself(), pos * r);
if (i > 0 && j > 0)
{
addvertex(geoself(), prim, pt - res_lon - 1);

}
}
}```

Same scheme for side surfaces, but you don't need to create points, so, it is simpler. We iterate over longitude divisions to create top and bottom, and over latitude divisions to create sides. We can create two primitives in each iteration.

```for (int i = 0; i < res_lon; i++)
{
if (i > 0)
{
// Create bottom code.
// Create top code.
}
}```

Notice that check is useless there, since we don't create points and do any other stuff outside it's scope. So, we could set our initial variable right to 1:

```for (int i = 1; i < res_lon; i++)
{
// Create bottom code.
// Create top code.
}```

Observing the point numbers, when we start at point 1, we need to compute points 0, 100 and 101. What is the hundred we need to offset? It turns out that it is number of points per surface: longitude divisions multiplied with latitude divisions. Using other parameters, we can get different offset value, but it will always be computed by this formula.

And the code to create bottom surface:

```int surface_ptnum = res_lon * res_lat;

addvertex(geoself(), prim, i - 1 + surface_ptnum);```

Same logic for top. When we build multiple segments, we cannot start from i=1, because in this example we need to somehow start from 801:

So, easiest way to get this offset, is to keep track of created points in pt variable. The last created point was 999, so, we need to compute 801 from it using parameters we have. By guessing, we choose certain formula:

```int surface_ptnum = res_lon * res_lat;
int start_pt = i + pt - surface_ptnum - surface_ptnum + 1;```

And final code for top and bottom creation:

```// Side surfaces.
for (int i = 1; i < res_lon; i++)
{
int prim;
int surface_ptnum = res_lon * res_lat;
int start_pt = i + pt - surface_ptnum - surface_ptnum + 1;

// Bottom.
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum);

// Top.
addvertex(geoself(), prim, start_pt + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt - 1 + surface_ptnum + surface_ptnum - res_lon);
addvertex(geoself(), prim, start_pt + surface_ptnum + surface_ptnum - res_lon);
}```

And absolutely same logic for the left and right sides, but formulas are different, determined by looking at numbers and available parameters. Formulas, vertex creation order and parameters may be different. But they will always evaluate to same result.

Edited by f1480187
• 4

##### Share on other sites

that's perfectly clear now,

thank you a ton F1!!!!

##### Share on other sites

Hello Odforce!

hip attached of the first version of the " VEX sphere slices geo generator " (i am still thinking about the proper fancy name  )

Please tell me what you think about it, do you think some important features are missing?

To take it a step further i will need some help.

QUESTION 0 ORDERED MENU IN VEX

I want to be able to switch among different rotation type.

```float spin_rnd = ...;
float spin_trig = ...;
float spin_reg = ...;
string spin_type = chs("spin_type"); //ordered menu with names corresponding to the different types of rotation
float w = amp*spin_type;
// this gives me an error : call to undefined binary operator `float = float*string`

```

how do i make him interpret that spin_type string as the float variable?

QUESTION 1 RESOLUTION

I'd like the resolution of the segments to be proportional/based on their size. When using many rings (say 100) the inner ones don't need the same res of the outers.

this would be a nice optimization of the asset when generating a high number of rings.

currently the generation of the segments is "angle based", (theta+theta_step) what i am thinking is to convert the logic to "arc based"... i tried this but doesn't work as aspected

```  //proportional resolution
if(theta_step*r_max < ch("test_theta_step")){
res_lon -=1;
theta_step = (end_theta - start_theta) / (res_lon - 1);
}```

QUESTION 2

the code runs once over detail. why can't I use @Frame? isn't it a global variable?

(right now i'm channel referencing to a \$FF for animations)

QUESTION 2bis

is there a way to export attributes even if we run on detail? i think would be cool to have ring_id  down the line.

createVEX_v02.hip

VEXgeo_creation - v001.wmv

• 1

##### Share on other sites

0. Use chi("menu"), instead of chs("menu"). First will evaluate to menu item's index, second will give your a token string of menu item. Or switch behavior with if statements on this strings. You also could use string menu with numerical tokens and reference them as ch("menu") or chi("menu"), they will be converted automatically.

1. Here is a starting point for adaptive resolution.

```// Proportional longitude resolution.
float angle_lon = (in_theta + theta+arc_width) - (in_theta + theta);
float arc_length_lon = r_max * radians(angle_lon);
float step_lon = 1;
res_lon = max((int) (arc_length_lon / step_lon), 2);```

Use it right before calling segment() function. The res_lon parameter is not needed anymore if you didn't use it for some other calculation. Parameterize geometry with step length. By the way, the geometry is quite large, that's why hardcoded step value is 1 meter.

2. You could use it outside inner function scope and pass to the ring() function as an argument.

2b. Use setpointattrib. If attribute not exist, use it in pair with addpointattrib function at the beginning of the code. In code you always have all numbers info, so, you may set primitive and even vertex attributes in same way.

Edited by f1480187
Fixed resolution formula.
• 1

##### Share on other sites

thanks F1!!!

1,2 and 2b solved and updated.

0. I can't understand what I'm supposed to do.

I mean i understand i could use if statements based on a integer menu. What I was wondering is if there is a more direct way because i have more than 2 rotation type to choose from, let's say we have 10 different kind. With my basic knowledge of coding i would do 10 if statements. but maybe there are better ways

Edited by WLVL

##### Share on other sites

Menu parameters works differently. See the file.

`float w = amp * spin_type; `

This is number (probably) and some string multiplication. Like 3.5 * "foo". Which is not valid in VEX.

• 1

##### Share on other sites

Hi F1! thank you for the support  very last questions on this project (i hope!)

1

How come that if i simplify these two lines the result changes? should it?

20 hours ago, f1480187 said:

// Proportional longitude resolution.

float angle_lon = (in_theta + theta+arc_width) - (in_theta + theta);

float arc_length_lon = 2*PI * r_max * radians(angle_lon) / 2*PI;

float angle_lon = arc_width;

2

how would i keep track of the segment id? i would like to pull it off as an attribute. The problem is that it resets for each ring. I would need a way to store how many while loop have been done before, and use that as the initialization number of seg_id.

this is not strictly necessary for this code since i could just use a connectivity after the wrangle to get that segment_id. I am asking just for better understanding

##### Share on other sites
1. My mistake there. I don't know what is arc_width, but if it is equal to previous calculation, it should be okay. Result changes, because instead of dividing by 6.28, result was divided by 2 and then multiplied with 3.14. Parentheses would help, as well as simplifying, as you did. Use smaller segment lengths.
2. Pass iteration numbers to both functions. Example. Here segment and ring ids was added. You may create name attribute instead, or you can easily convert ids to classic name attribute by sprintf() both ids together, like "ring8_segment26":
`s@name = sprintf("ring%d_segment%d", i@ring_id, i@segment_id);`

##### Share on other sites

Attached the finished tool, time to move to something else!

thank you Odforce

huge thanks to F1!

ring_segmentizer_v001.hip

• 5

## Create an account

Register a new account