Jump to content

dose anybody have a VOP to solve a triangle?


meshsmooth

Recommended Posts

dose anybody have a VOP to solve a triangle?

By solve a triangle I mean if you supply it a side and 2 angles or 3 sides or 1 angle and 2 sides, you can have it work out all other angles and side lengths. I was planning to make one and just thought I would ask, saving my self reinventing stuff.

I will be making it with inner and outer VEX code so it will be easy to shair and convert into a commercial version by somebody else.

So dose anybody have anything like this lieing about or will I get coding?

Link to comment
Share on other sites

So dose anybody have anything like this lieing about or will I get coding?

13435[/snapback]

Hey Robert,

I'd say "get coding" regardless! :)

This is a great exercise in the sense that it isn't that complicated, so you're pretty much guaranteed to be able to solve it even if your trig is rusty, and yet what you learn in the process is priceless.

To nudge you in the right direction: solving triangles usually involves two things called "The Law of Cosines" and "The Law of Sines". Which one you choose depends on the context, but 9 times out of 10, you'll be using the law of cosines. These laws come from trigonometry, but they are also implicit in vector algebra (vector spaces), so that the famous "Dot Product" between two vectors, for example, incorporates the law of cosines.

And a very good link to get you going: Wolfram

PS: If you're doing a job and need this right away, then I can give you the answers, of course, but if you're not, then I think you'd be better off trying to solve it on your own, and a lot of people here can help you along if you get stuck.

Good Luck! :)

Link to comment
Share on other sites

just a cupple other questions. What should go in the inner code and what should go in the outter code? is there any vops or vex exprressions that turn a set of booleans into an intager, or do i just create that one too? (i have a set of 4 booleans, it may be easer to test the integer that they equate to )

and on a compleatly difrent noat what would a bitwise function be good for?

Link to comment
Share on other sites

just a cupple other questions. What should go in the inner code and what should go in the outter code?

13442[/snapback]

Anything you like B)

There's no "rule" about this, but typically, the outer code (code external to the context function) will have one or more functions that do the actual work (compute the thing you want to compute), and the inner code (code that will go inside the context function when the vex operator gets built by the VOPNET) will contain a call to these "outer code" functions.

A couple of little "gotchas" to keep in mind:

1. Names of local variables (i.e: any variable that is not a global) inside the inner code section should be preceded by a dollar sign ($) to keep them from getting renamed by the VOPNET tokenizer/parser/whatever_they_call_it. Otherwise it will rename variables in an effort to avoid name collisions.

So; instead of writing:

vector v = 1, amp = 2;
P += v * amp;

you would write:

vector $v = 1, $amp = 2;
P += $v * $amp;

Strictly speaking, this is only necessary when the vars are used in expressions (line 2), not in declarations (line 1), but it doesn't hurt to use it in both, and that way you don't need to remember any rules about them ;)

2. Even though a similar situation exists in the outer code section (i.e: name collision can also happen there, and should therefore be avoided), you can wrap the entire outer code with a couple of #define statements (which are referred to as "multiple inclusion guards"), which look like this:

#ifndef ROBERT_SOLVETRIANGLE__GUARD
#define ROBERT_SOLVETRIANGLE__GUARD

// All your outer-code stuff goes here...

#endif

Note that the label "ROBERT_SOLVETRIANGLE__GUARD" is just something I made up; it can be anything, but try to come up with something that has a chance of being unique -- i.e: unlikely to exist in any of the other VOPs that a user might use inside the same VOPNET. This trick ensures that the stuff inside the #define directives will be seen only once by the compiler. (a GoodThing ;) )

is there any vops or vex exprressions that turn a set of booleans into an intager, or do i just create that one too? (i have a set of 4 booleans, it may be easer to test the integer that they equate to )

13442[/snapback]

Vex does not have support for a "boolean" data type. This means that you will normally use integer types for this purpose. So yes, you have no choice but to "test the integer that they equate to" since they will all be integers to start with.

and on a compleatly difrent noat what would a bitwise function be good for?

13442[/snapback]

Bitwise operators are slightly more esoteric, so I wouldn't worry too much about them for now. But in brief, the "bit shift" operators (<< and >>) can be used to do quick multiplication/division by powers of two, or flip the "endian-ness" of data types, and other strange (but really useful) trickery. And the bitwise comparison operators (& | ^) are normally used to "mask" bits when testing a wider data type that has been hijacked to contain multiple bit flags. They can be used for all sorts of other "strange" things, but like I said, don't worry about them -- you can solve a triangle without using them.

Hope that helps,

Cheers!

Link to comment
Share on other sites

elaborating on what Mario said, a classic, easy to understand example for bitwise operations is in games. Memory space is a premium so you want to use as little of it as possible. Say you have a game level, a platform game on the Gameboy Advance, every floor tile needs four additional attriibutes; an attribute for floor type, an attribute for the sound it will give off when the player walks on it, an attribute for friction, and an attribute for how much damage the player recieves if he falls on it.

So 4 floor types (grass, stone, ice, and steel)

4 sound variations for each floor type

4 friction values for each floor type

4 damage values for falling on the floor (0%, 10%, 25%, and instant death)

Each of these attributes can be represented with integers 1-4. Common sense says you need 4 integer attributes to store these values. You don't have memory to waste storing 4 integers for every piece of floor in the level. Use a single integer instead.

00000000

That's an integer, eight places. Each place is on (1) or off (0). Using bitwise operators you can flip and test the bits individually to store all the attibutes you need for a piece of floor in one quarter the memory required if you had used a seperate integer for each attribute. The first two places (from righ to left) are floor type, the next two are sound type, the next two are friction, and the last two on th left are damage. Each of those pairings can produce four possible results. So...

01000111

Means I want a steel floor with sound 2 using friction value 1 that does 10% damage if the player falls.

11011010

10 Floor type 3: Ice

10 Sound Type 3

01 Friction Value 2

11 Instant death if the player falls

The main reasons for doing things like this is for efficiency and speed (which is actually becoming less and less an issue nowadays). Efficiency because you can save alot of memory, and speed because it's far quicker to look at a single integer and test it's bits than it is to check 4 sperate integers.

Bitwise operators are also used quite extensively for 2d graphics and textures in games. Rather than storing the color values in an image for each pixel in an 16 bit image (16 zeros per pixel) The image contains no actual color information itself. 8 bits in each pixel are actually indexed references to a CLUT (Color Look Up Table, basicly a list of colors) 1 bit used to reference one of two different CLUTs, and 7 bits of each pixel reserved to create transparency (128 levels of transparency). The result is you get a color image with up to 512 colors with an alpha channel using half the memory it would take had you used 32 bit images. The quality is noticably poorer, but this is usually done for game textures, so the images tend to be quite small and don't require millions of colors to begin with.

Link to comment
Share on other sites

Hey Mike, that's a really wonderful explanation. Cheers! :thumbsup:

I'd say go forth and wikify that thang! ;)

< The only bummer is that I don't think VEX supports bit-shift ops, so in VEX, the values wouldn't always be 1-4, since it would depend on where in the integer they lived -- so just tweak that bit (no pun intended ;) ). >

Hey Robert,

You can look at many of the VOPs that ship with Houdini for examples of how these things are put together, but here's a very simple (and not very useful) example just to show the main parts of it:

Say we want to write a multi-context VOP that will take one (and optionally two) vector(s) and spit out a perpendicular vector, with an option to normalize the result.

We search around to check the math involved in doing something like this, and learn that:

1. If we're given two vectors that are not collinear, we can produce a perpendicular by taking the cross-product of the two:

Vperp = cross(V1,V2);

2. If we're given just one vector (or two vectors that are collinear), then we can produce a perpendicular with the following:

Vperp = set ( V1.z-V1.y, V1.x-V1.z, V1.y-V1.x );

In this case, there are an infinite number of possible solutions; the above just chooses one of them.

Additionally, this means that when given two vectors as input, we'd need some way to determine whether they are collinear (exact same direction, or exactly opposite each other). To test for this we can see if their cross product is zero.

Armed with this (which is equivalent to you finding out about the law of cosines), we can start to put together our VOP:

1. In Houdini, select File -> New Operator Type...

2. In the popup window, set the following values:

Operator Name: perp

Operator Label: Perpendicular

Operator Style: VEX Builder Type

Network Type: VEX Builder Operator

Save To Library: perp.otl

Install Library To: Current HIP File Only

[Accept]

The Operator Type Properties dialog should pop up. Leave this dialog up throughout the next steps.

3. Switch to the VEX_builder desktop and add any generator you like; let's say a "VEX Surface Shader" generator. Then go inside it, and add our VOP, which you will find as "Perpendicular" in the tab list. This is just so you can see the op as we build it.

4. Go to the Parameters tab in the Type Properties dialog, and create the following:

Create New: Toggle

Name: t_norm

Label: Normalize Output

Defaults: 1

Hit [Apply], and you should see the toggle param show up in our VOP.

5. Go to the "Input/Output" tab in the Type Properties dialog, and create the following:

New Input: vector

Name: V1

Label: Vector 1

New Input: vector

Name: V2

Label: Vector 2

New Input: int

Name: t_norm

Label: Normalize Output

New Output: vector

Name: Vperp

Label: Perpendicular

Hit [Apply], and you should see these inputs/outputs in the VOP. Notice that the toggle input (t_norm) matches the parameter name. This means that you can set this value by wiring stuff into the input. Also note that I didn't add parameters for V1 and V2; this is so that we can switch between different solutions based on whether anything is connected to them, as relying on "signatures" wouldn't work in this case. In general though, you would normally match your inputs to your parameters, so the user can set their values either explicitely, or by wiring stuff into the inputs.

6. Now comes the meat! ;) Go to the VEX Code tab in the Type Properties dialog, and select the Outer Code tab, and input the following:

#ifndef ROBERT_PERP__GUARD
#define ROBERT_PERP__GUARD

// Perpendicular to a single vector
vector perp1(vector v) {
   return set(v.z-v.y,v.x-v.z,v.y-v.x);
}

// Perpendicular to two vectors
vector perp2(vector v1,v2) {
   vector result = cross(v1,v2);
   if(result=={0,0,0}) result = perp1(v1);
   return result;
}

#endif

These are the actual implementation of what I talked about earlier: the functions that actually calculate the perpendicular.

7. Now go to the Inner Code section and add:

// Initialize the result to "error" (zero-vector)
$Vperp = 0;

// Calculate only if one or both inputs is connected
if($isconnected_V1 || $isconnected_V2) {
   if($isconnected_V1 &amp;&amp; $isconnected_V2) $Vperp=perp2($V1,$V2);
      else $Vperp = $isconnected_V1 ? perp1($V1) : perp1($V2);
   if($t_norm) $Vperp = normalize($Vperp);
}

Here we just check if one or both of V1 and V2 are connected, and call the Outer Code functions accordingly.

Sprinkle with an icon, and add help cards to taste; hit [Accept], and you're done! :)

Hope that helps,

Cheers!

Link to comment
Share on other sites

Now that is an explanation !!!!11!!1one!!!

Thank You.

FYI my coding background is a bit of JAVA

In houdini can you overload a function? ie.

int stuff(int a, B){
return a + b;
}

and then also have

int stuff(int a, b, c){
return a + b;
}

and

int stuff(int a, b, c, d){
return a + b;
}

Link to comment
Share on other sites

Thank You.

You're welcome :)

In houdini can you overload a function?

Nope. That's why in my example those two functions are named perp1() and perp2(), instead of simply overloading perp().

You could overload functions if you wrote them as dso's (dll's for Windoze); effectively creating a new VEX function (notice how the built-in VEX functions are overloaded)... but that's a different thread.

Link to comment
Share on other sites

Hey Robert,

I'm guessing that you've already spent some time trying to come up with a solution. And, to be honest, I'm feeling a little guilty since I might have put you on the spot there (which was never my intention).

I just think that, spending some time solving it by yourself will enable you to A) Understand a solution if one is given to you, and B ) Get you started on a methodology that you can use for writing your own VOPs in the future.

But it was never meant to be a test; so here's the breakdown for one possible solution.

Stop reading if you're still working on a solution! :)

The Math

First, you have to determine the absolute minimum (and sufficient) amount of information you'll need in order to solve the triangle. Looking at the Wolfram site that I mentioned, you find that if we use that law of cosines, you will need the length of any two sides, plus the angle between them -- that's three parameters.

But in order to get a "length", we need to measure a line segment; and a line segment is defined by two points. So to get the two lengths of two sides of a triangle, we will need three points -- meaning that if we go this route, we'll need four parameters: three points plus one angle. Also note that if the user already has three points, then he already knows the lengths of the three sides: length(P2-P1), length(P3-P2), and length(P1-P3).

But if you read further in the Wolfram page, you'll notice that the law of cosines can also be derived from the difference between two vectors dotted with itself (the dot product of a vector with itself gives you the square of the length of that vector). We also know that A) We can get the cosine of the angle (and therefore the angle) between two vectors by taking the dot product of the unit-length versions of the two vectors, and B ) We can easily find the length of any vector (using the length() function). If we choose this route, then all we need is two parameters: two vectors.

In my opinion, it is a more common situation to be working with vectors instead of lengths, so I would be inclined to choose the vector-based solution. Here are the details of that particular approach:

If we are given two vectors V1 and V2, then we can calculate the missing vector V3, the three lengths L1, L2, L3, and the three angles A1, A2, A3, as follows:

V3 = V2 - V1

L1 = length(V1)

L2 = length(V2)

L3 = length(V3)

A1 = acos(dot(normalize(V1),normalize(V2)))

A2 = acos( (L3^2 - L1^2 + L2^2) / ( 2.0 * L3 * L2) )

A3 = PI - A1 - A2

Note that all solutions will need to deal with at least two degenerate cases: 1) When any of the given vectors has zero length, and 2) When the two given vectors are identical, in which case all angles are zero, and one side has length zero (i.e: not a triangle). The other possibility is when the two vectors are 180 degrees from each other; but we can consider this case "legal" since all sides have non-zero lengths.

The VOP

Parameters, inputs, and outputs:

Parameters:
============

Type        Name        Label                Default
----------------------------------------------------
vector      v1          Vector 1             {1,0,0}
vector      v2          Vector 2             {0,1,0}
toggle      t_rad       Angles in Radians    1


Inputs:
=======

Type        Name        Label           
----------------------------------------------------
vector      v1          Vector 1
vector      v2          Vector 2


Outputs:
========

Type        Name        Label           
----------------------------------------------------
int         success     Whether it could be solved
float       L1          Length of side 1
float       L2          Length of side 2
float       L3          Length of side 3
float       A12         Angle between vectors 1 and 2
float       A23         Angle between vectors 2 and 3
float       A31         Angle between vectors 3 and 1

Outer Code:

//---------------------------------------------------
// Outer Code
//---------------------------------------------------

#ifndef ROBERT_TRIANGLE__GUARD
#define ROBERT_TRIANGLE__GUARD

#include &lt;math.h&gt;

int solveTri(vector s1,s2; float l1,l2,l3, a12,a23,a31) {
   int ok = 0;
   float ca12 = dot(normalize(s1),normalize(s2));
   if(ca12&lt;1.0) {
      l1  = length(s1);
      l2  = length(s2);
      if(l1!=0.0 &amp;&amp; l2!=0.0) {
         l3  = length(s1-s2);
         a12 = acos(ca12);
         if(a12 &gt;= M_PI) {
            a12 = M_PI;
            a23 = a31 = 0.0;
         } else {
            a23 = acos((l3*l3 - l1*l1 + l2*l2) / (2.*l3*l2));
            a31 = M_PI - (a12+a23);
         }
         ok = 1;
      }
   }
   return ok;
}

#endif

And Inner Code:

//---------------------------------------------------
// Inner Code
//---------------------------------------------------

$success = solveTri ( $v1,$v2, 
                       $L1,  $L2,  $L3, 
                       $A12, $A23, $A31
                    );
if(!$success) {
   $L1 = $L2 = $L3 = 0.0;
   $A12 = $A23 = $A31 = 0.0;
} else if(!$t_rad) {
   $A12 = degrees($A12);
   $A23 = degrees($A23);
   $A31 = degrees($A31);
}

Cheers!

Link to comment
Share on other sites

Well after having little to no freelance work for a while, my bills needed to be paid so i ended up doing plenty of manual labour this last week. Strange how after doing physical labour you brain seems to be tired too. As a result i was scribbling down note during the day and spending about 5 minutes in Houdini before loosing momentum. So on the weekend i had the time and energy to do it.

Which i spent barking up the wrong tree. i was trying to make it so the vop had 6 inputs 3 sides and 3 angles, and the idea was to work out what solution should be used to solve the triangle from the inputs that were connected. That is a case of dealing with 6 boolean values resulting in 64 potabilities, i looked into bitwise operations again so i could pass around a single int to test what the state of the connected inputs were. This lead to me being nice and cosy with binary numbers now. and in the end i decided to just test the following

a,b,c : all sides

a,b,C : 2 sides & included angle

a,b,A : 2 sides & non-included angle // this solution can be ambiguous!

a,A,B : any side any 2 angles

Then I was going down one path where i was outputting only the other 3 values, then there was the problem of how to communicate what values go where and how to communicate to the user what are sides and angles etc. then i realised that it would be simpler just to output all 6 values removing most ambiguity.

How if only i could rename the labels on the inputs on the fly. My programming boots are back on after a bit of a rusty start.

In an hour or so i should have the VOP finished

And isn't it always the way, now that I was getting off my ass and doing some manual labour, next thing I have freelance 3d work coming out my ears, well for the next couple of weeks anyway.

Link to comment
Share on other sites

Hey Robert,

i was trying to make it so the vop had 6 inputs 3 sides and 3 angles, and the idea was to work out what solution should be used to solve the triangle from the inputs that were connected. That is a case of dealing with 6 boolean values resulting in 64 potabilities, i looked into bitwise operations again so i could pass around a single int to test what the state of the connected inputs were.

I see. Yes; that would complicate things quite a bit for not much gain. That's not to say that it couldn't be done that way, but:

1) You'd be asking the user to input lengths, which is something the VOP could calculate on its own.

2) You'd be asking for 1 or more angles, which the user would very likely have to compute by comparing vectors... which again, the VOP could do on its own.

3) If you did go this way, you'd be probably better off staying away from bit fields, and using an if-then-else cascade instead.

But hey! The whole point is that you tried these things, which is the only way to get to know them. So good on you!

And isn't it always the way, now that I was getting off my ass and doing some manual labour, next thing I have freelance 3d work coming out my ears, well for the next couple of weeks anyway.

13656[/snapback]

Good luck on your freelance work! :)

Cheers!

Link to comment
Share on other sites

  • 2 weeks later...

What i have come up with is alright on its own but i have found that my functions are not protected from each other and even the $PI variable is swapped out for PI1 when there are more than one instance of my triangle VOPs in the VOP net

http://members.optusnet.com.au/meshsmooth/...ls/triangle.rar the otl file with my 2 aproches to the problem.

Link to comment
Share on other sites

Woops some of the test and other crap get left in there. It has been reduced to rk_triOps and rk_solveTriangle. M_PI has been inserted through the ops. Just re-download it from http://members.optusnet.com.au/meshsmooth/...ls/triangle.rar

rk_solveTriangle was made first and rk_triOps was made second. I believe I will code up most of my more complex operations as a VOP and insert them into the place where they will be used, in a hope to make them more reusable.

If a pattern is made as a VOP it can get reused by anybody, quickly.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...