From Math.Stackexhange
https://math.stackexchange.com/questions/214187/point-on-the-left-or-right-side-of-a-plane-in-3d-space
Let $A,B,C$ be the points that determine the plane. Then the cross
product $(B-A) \times (C-A)$ gives us a normal ${\bf n}$ to the plane.
Now consider a test point $(x,0,0)$ where $x$ is a huge positive
number. This should be on the right side of the plane. Now $((x,0,0) -
> A) \cdot {\bf n}$ is just the first coordinate of ${\bf n}$ times $x$
minus some constant. For large enough $x$ the sign of the dot product
is then just the sign of the first coordinate of ${\bf n}$. Thus: If
the sign of first coordinate of $n$ is positive, then the right side
consists of the points $P$ with $(P - A) \cdot {\bf n} > 0$ and the
left side consists of the points with $(P - A) \cdot {\bf n} < 0$. The
inequalities are reversed if the sign of the first coordinate of $n$
is negative.
Have a group of fish objects "Cones" aligned -Y forward Z up. The x axis looks left and right. The plane of the fish is in ZY plane, and the normal X.
Calculate the "centroid fish" world matrix by averaging the world matrices of the fish. Its translation (loc
) is the centre of geometry of all fish. It's X axis is our norm to test aganst. The "centroid fish" is represented by a cone shaped empty.
For each fish, if the dot product of the centroid normal to fish.matrix_world.translation - loc
is greater than zero it's on the left, and colored red.
Simple test setup using single BI material with object color. Move the fishies around and run script
import bpy
from mathutils import Matrix, Vector
context = bpy.context
scene = context.scene
school = [f for f in scene.objects if f.name.startswith("Cone")]
N = len(school)
loc = sum([f.matrix_world.translation for f in school], Vector()) / N
mw = 1 / N * sum([f.matrix_world for f in school], Matrix())
left = mw.to_3x3().transposed()[0] # normal to the plane
left = -left if left.x < 0 else left # flip claus
# add an empty to show Centroid Location. (center of geom)
mt = scene.objects.get("Centroid")
if not mt:
mt = bpy.data.objects.new("Centroid", None)
scene.objects.link(mt)
mt.matrix_world = mw
mt.empty_draw_type = 'CONE'
mt.scale *= 4
mt.scale.y *= -1
mt.scale.x /= 5
for fish in school:
#calc the dot product
dot = left.dot((fish.matrix_world.translation - loc).normalized())
# red on fishes left
fish.color = (1, 0, 0, dot) if dot > 0 else (0, 1, 0, -dot)
Note: Suggest using center of mass instead of geometry.
The same test on other two axes will give forward / behind for Y and above / below for Z.