I stumbled on something rather unintuitive today:
It seems that the comparison of vectors is based on length, and the same is true for similarity.
This is really unexpected behaviour to me and all of my colleagues. Checking if two vectors are equal is different from checking of two vectors have the same length.
If the user wants a comparison of length it should be explicit, whereas the comparison of coordinates should be implicit.
Does anyone know why it is implemented this way? Do we have to create our own vector equality component? Please help before we all go insane!!
Short answer: yes
I am guessing they chose to do it so because of the way most people use it. The way I compare vectors is with the distance component.
Since vectors can be used interchangeably with points, I use the distance as a boolean (0=false, anything else=true) with the extra step of putting a not gate.
Grasshopper has many “user friendly” defaults such as using length of curves and vectors when only a single number is expected, as is the case with Equals. For vectors to be equal, all three (X, Y and Z) components of the vectors must be compared.
Another GH quirk is that vectors can be treated as points, so if the “distance” between two vectors (as points) is zero, the vectors (points) are the same (equal).
Thank you @anikolo and @Joseph_Oster. Sounds like someone has been thinking too hard about this! The works of a twisted mind perhaps…
That sounds about as rude to me as if I were to say:
“someone has not been thinking hard enough about this!”
If you prefer that I don’t respond to your questions, that can be arranged.
Sorry I think you misunderstood. I meant that the designer of the component has thought too hard about what vector equality means.
Thanks for your answer, very useful.
I think your comments are misguided.
Perhaps. Thank you anyway for your help.
I think probably what happens is not that the equality component has a special rule for vectors. It’s just that the vector is being cast to a single number, and equality then operates on that number, which is the only thing it’s designed to do. And if you want to reduce a vector to a single number, length is an intuitive way to do it, though some may argue you shouldn’t do this in the first place because the two types are so fundamentally different to begin with.
Grasshopper allows a lot of type conversions that some, especially those with a programming background, may not expect, for example allowing conversion from a curve to a number based on the length or a surface to a curve by taking the edges. It can be very useful but it can also make your code hard to read or result in unintuitive behavior if you don’t know that it does this. It isn’t really type casting, it’s property extraction disguised as type casting, mixed in with behaviors that are more akin to type casting such as int to number, point to vector, int to bool, etc. And that definitely can be confusing if you’re not used to it because I don’t know any other software that lets you do property extraction disguised as type casting like this.
My thoughts exactly. To me the design of the equality component is somewhat flawed. If it is limited to the equality of one-dimensional numbers, then anything else as an input parameter should at the very least raise a warning.
The current design leads to situations that seem absurd in my eyes: two points in different locations can evaluate as equal which is utter non-sense to me.
Anyhow, I got the gist of it now, I’ll just be mindful and try to avoid making assumption of its behaviour.
If we add to this the confusion that float comparison can lead to, we’re getting close to undefined behaviour!
It can be pretty useful… For example you can feed a closed planar curve into brep component and it just makes the boundary surface for you.
If you hover over the component input, you can see the data type, and this is the conversion it will try to do to whatever you plug into it.
I would recommend you just do these kinds of type conversions where you’re doing something less intuitive explicitly to make it clear, like have the conversion done in containers that are on their own on the canvas that can be labelled and are only used for this purpose, rather than let the conversion be done in the component input/output where it’s not as obvious that a conversion is happening. Just helps keep track of what’s going on and makes it the conversion a deliberate action.
FWIW Grasshopper 2 implements a proper vector equality test:
Nice tip thanks. Coming from Python, I tend to use parameter containers extensively. What caught me with the equality component is the limitation to one-dimensional numbers, I thought it would be able to process multi-dimensional arrays. I guess it’s up to me to make my own.
Great news! Looking forward to it!
Equality doesn’t compare vectors at all. It compares numbers. When you plug a vector into a number parameter it convert itself into a number which matches the length of the vector.
Thanks, that’s what I have understood now. My regret is that a vector gets converted to its length, as opposed to a list of 3 numbers which I believe would make more sense.
What I find questionable is that if the component doesn’t compare vectors at all, then why doesn’t it throw an error when the input parameter is a vector rather than spitting out a meaningless result?
Anyhow, I think this is fixed in Grasshopper 2 (I loved the little intro dialog you wrote about it, made me laugh ). Thank for all the good work, Grasshopper is a fantastic tool. And as any good tool, we want to improve it!
It’s just the way Grasshopper works. If there’s a defined conversion from a given type to an input type it will just do that without asking or complaining. Many times that’s what you want, sometimes it happens when you don’t expect it and then it causes confusion and tech support.
Another common problem case is the conversion from points to numbers, that one makes even less sense conceptually, and people often expect to be able to use points where curve parameters are required and they get wildly unexpected results.
The solution here is to make the Equals component aware of more data types, or to start generating warnings if a conversion is “weird”. In GH2 conversions have ‘merits’ associated with them, which would allow you to choose at what point an automatic conversion would flag up as a warning.
Here’s the list of conversion merits as defined in GH2 code:
/// Enumerates the predefined levels of conversion.
public enum Merit
// Note: Since these are sometimes used for indexing, they need sticky values.
// Note: The names are chosen to have an alphabetical sort order equal to their numerical sort order.
/// Indicates a direct conversion took place, either between identical or *very* related types.
/// For example from [int → long] or [char → string].
Direct = 0,
/// Indicates a straightforward conversion took place, probably between fairly related types.
/// For example from [bool → int] or [int → double]. Although a reasonable conversion
/// may be narrowing and even fail under certain circumstances, it should be considered
/// good coding practice to rely on them.
Fair = 1,
/// Indicates a non-straightforward, but a still somewhat reasonable conversion took place.
/// For example from [curve → domain], or [point → plane]. Ideally any plausible
/// conversion is also achievable through the use of standard components. Reliance on
/// plausible conversions should be avoided by beginners.
Plausible = 2,
/// Indicates a peculiar conversion was needed, but we allow that it might
/// make sense under special conditions, although it should also serve as a clue to
/// the user that they might be doing something wrong. It should always be possible
/// to mimic strange conversions using standard grasshopper components.
Strange = 3,
/// Indicates a weird and exotic conversion was necessary. Weird conversions are red
/// flags and should always be achievable through the use of standard components.
/// Even expert users should probably stay away from these as they negatively affect
/// the readability of an algorithm.
Weird = 4,
/// Indicates no conversion took place. For example [bool → brep] is not possible.
/// May also indicate a failed conversion due to out-of-bounds issues.
Zilch = 5
Very interesting. As I understand it, the merit of a conversion would be evaluated according to a map describing the semantic relation between types.
I’m getting excited about GH2 although I really shouldn’t!
Yes, when a developer adds a conversion to the centrally kept database they have to decide what merit it has (and you can already see the flaw in the system no doubt). Then the user can decide at what merit level they want to see a warning.