def triangle_normal(a,b,c):
(a,b,c) = [np.asarray(x) for x in (a,b,c)]<EOL>if len(a.shape) == <NUM_LIT:1> and len(b.shape) == <NUM_LIT:1> and len(c.shape) == <NUM_LIT:1>:<EOL><INDENT>return triangle_normal(*[np.transpose([x]) for x in (a,b,c)])[:,<NUM_LIT:0>]<EOL><DEDENT>(a,b,c) = [np.transpose([x]) if len(x.shape) == <NUM_LIT:1> else x for x in (a,b,c)]<EOL>if a.shape[<NUM_LIT:0>] in (<NUM_LIT:2>,<NUM_LIT:3>):<EOL><INDENT>dims = a.shape[<NUM_LIT:0>]<EOL>tx = True<EOL><DEDENT>else:<EOL><INDENT>dims = a.shape[<NUM_LIT:1>]<EOL>(a,b,c) = [x.T for x in (a,b,c)]<EOL>tx = False<EOL><DEDENT>n = (a.shape[<NUM_LIT:1>] if a.shape[<NUM_LIT:1>] != <NUM_LIT:1> else b.shape[<NUM_LIT:1>] if b.shape[<NUM_LIT:1>] != <NUM_LIT:1> else<EOL>c.shape[<NUM_LIT:1>] if c.shape[<NUM_LIT:1>] != <NUM_LIT:1> else <NUM_LIT:1>)<EOL>if dims == <NUM_LIT:2>:<EOL><INDENT>(a,b,c) = [np.vstack((x, np.zeros((<NUM_LIT:1>,n)))) for x in (a,b,c)]<EOL><DEDENT>ab = normalize(b - a)<EOL>ac = normalize(c - a)<EOL>res = np.cross(ab, ac, axisa=<NUM_LIT:0>, axisb=<NUM_LIT:0>)<EOL>return res.T if tx else res<EOL>
triangle_normal(a, b, c) yields the normal vector of the triangle whose vertices are given by
  the points a, b, and c. If the points are 2D points, then 3D normal vectors are still yielded,
  that are always (0,0,1) or (0,0,-1). This function auto-threads over matrices, in which case
  they must be in equivalent orientations, and the result is returned in whatever orientation
  they are given in. In some cases, the intended orientation of the matrices is ambiguous (e.g.,
  if a, b, and c are 2 x 3 matrices), in which case the matrix is always assumed to be given in
  (dims x vertices) orientation.