To follow up the following works:
def placeSymbolOnSurfaceAlignedWithUVDelta(u,v, surfaceHandel, symbolName, du,dv):
# get the 3d point and vector normal to the surface
ptExists, pt, normal = vs.EvaluateNurbsSurfacePointAndNormal(surfaceHandel, u, v)
if ptExists:
# place the symbol at (0,0,0) then move it. Assumes symbol is defined to be aligned by its insertion point
vs.Symbol(symbolName, (0,0), 0)
symbolHandel = vs.LNewObj()
# create an orientation vector using a point either side of u,v (u-du,v-dv), (u+du,v+dv) where du,dv are small compared to the surface curvature and their relative magnatudes define the orientation in the u,v plane eg u=0,v=0.0001 will result in a vector in the direction of v at the point
orentation = tuple(map(sub, vs.EvaluateNurbsSurfacePointAndNormal(surfaceHandel, u+du, v+dv)[1], vs.EvaluateNurbsSurfacePointAndNormal(surfaceHandel, u-du, v-dv)[1]))
# this is a good estiment but it fails because it is not exactly orthogonal to the normal vector. Correct by projecting onto the plane tangent to the surface at u,v
uVec = tuple(map(sub,orentation, [vs.DotProduct(orentation,normal)*i for i in normal]))
# w-vector is the normal vector
wVec = normal
# we know the other 2 and they are all orthogonal so v-vector is the cross product of u x w
vVec = vs.CrossProduct(uVec,wVec)
# offset is the 3d point because the symbol is currently at 0,0,0
offsetVec = pt
vs.SetEntityMatrixN(symbolHandel, uVec, vVec, wVec, offsetVec)
return symbolHandel