# Making sense of hair_key coordinates

I am trying to convert hair_key.co_local to world coordinates. As I understand it, every hair particle has a bunch of hair_keys (type:ParticleHairKey) that represent strand segments. The ParticleHairKey type has two coordinate fields:

• co - always (0,0,0) on read; writing does nothing
• co_local - read produces non-zero vectors; write DOES have some sort of an effect

So my guess is, co field is not implemented / supported and I have to work with co_local instead.

according to the docs ParticleHairKey co_local is "Location of the hair key in its local coordinate system, relative to the emitting face". and there is co_object method that returns key location (I assume in object coordinates). No matter what I tried however, the resulting coordinate doesn't seem to map to world coordinates in any meaningful way.

For example, I have a single strand of hair with 3 segments. particle.location here is already interpretable - if I put 3d cursor at that position, it matches particle location in the viewport (see pic).

obj = bpy.data.objects["gen82"]
psMod = (obj.modifiers)[1]
ps = obj.particle_systems["ParticleSettings"]

for m, particle in enumerate(ps.particles):
print("particle location", particle.location)
bpy.context.scene.cursor.location = particle.location
for k in range(0, len(particle.hair_keys)):
print(f"key {k}", particle.hair_keys[k].co_local)


Moves 3d cursor as expected.

and produces following console output

particle location <Vector (0.0953, -3.7172, -0.6447)>
key 0 <Vector (0.0000, 0.0000, 0.0000)>
key 1 <Vector (-0.0036, 0.0030, 0.1666)>
key 2 <Vector (-0.0073, 0.0060, 0.3332)>
key 3 <Vector (-0.0109, 0.0090, 0.4998)>


As you can see, hair_key.co_local seems to match description from the docs - it gets progressively farther from (0,0,0) as key index increases. But I have real difficulty working with those values. I tried calling co_object and transforming to world coordinates, tried offsetting by particle.location etc. - nothing produced world coordinates. Is there a way of transforming this coordinate to object or world coordinates ?

• Are you using 2.77? AFAIK this is different in 2.8 using the depsgraph (can post an answer if interested in answer for 2.8,) Recommend a simple plane, off global centre eg (1, 1, 1), example with a couple of hairs and results you obtained for each coord type. From local_co desc one would assume will need a space matrix centered on face center aligned with face normal (tricky). In 2.8 co returns the world coord – batFINGER Jun 7 at 5:30
• I am using 2.82. Thanks for drawing my attention back to co. I googled why it is always zero vector and found this gem developer.blender.org/T60083 – Andrew Butenko Jun 7 at 15:29
• Added 2.8 answer. Assumed you were on 2.77 re docs link above. – batFINGER Jun 7 at 16:22

Use evaluated object in 2.8+

Example for 2.8+ adds an empty at each hair key location. Note: since it adds an empty via op, test on an object with lowish hair count

import bpy

context = bpy.context
dg = context.evaluated_depsgraph_get()
# evaluated object
ob = context.object.evaluated_get(dg)
mw = ob.matrix_world

for p in ob.particle_systems[0].particles:
for k in p.hair_keys: