Skip to content

Instantly share code, notes, and snippets.

@pink-vertex
Created October 29, 2015 23:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pink-vertex/c9fd49e8348ea866a1b5 to your computer and use it in GitHub Desktop.
Save pink-vertex/c9fd49e8348ea866a1b5 to your computer and use it in GitHub Desktop.
breakdown text curve into characters (experiment - tested on ubuntu 2.75a 64bit)
import bpy
import mathutils
import ctypes
c_int_p = ctypes.POINTER(ctypes.c_int)
c_float_p = ctypes.POINTER(ctypes.c_float)
class BezTriple(ctypes.Structure):
_pack_ = 8
_fields_ = [
("vec", (ctypes.c_float * 3) * 3),
("alfa", ctypes.c_float),
("weight", ctypes.c_float),
("radius", ctypes.c_float),
("ipo", ctypes.c_char),
("h1", ctypes.c_char),
("h2", ctypes.c_char),
("f1", ctypes.c_char),
("f2", ctypes.c_char),
("f3", ctypes.c_char),
("hide", ctypes.c_char),
("easing", ctypes.c_char),
("back", ctypes.c_float),
("amplitude", ctypes.c_float),
("period", ctypes.c_float),
("pad", ctypes.c_char * 4)
]
def get_coordinates(self):
v = self.vec
return (mathutils.Vector((v[0][0], v[0][1], v[0][2])),
mathutils.Vector((v[1][0], v[1][1], v[1][2])),
mathutils.Vector((v[2][0], v[2][1], v[2][2])))
class BPoint(ctypes.Structure):
_pack_ = 8
_fields_ = [
("vec", ctypes.c_float * 4),
("alfa", ctypes.c_float),
("weight", ctypes.c_float),
("f1", ctypes.c_short),
("hide", ctypes.c_short),
("radius", ctypes.c_float),
("pad", ctypes.c_float),
]
def get_coordinates(self):
v = self.vec
return mathutils.Vector((v[0], v[1], v[2], v[3]))
BezTriple_p = ctypes.POINTER(BezTriple)
BPoint_p = ctypes.POINTER(BPoint)
class Nurb(ctypes.Structure):
_pack_ = 8
_fields_ = [ # x64
("prev", ctypes.c_void_p), # 8
("next", ctypes.c_void_p), # 16
("type", ctypes.c_short), # 18
("mat_nr", ctypes.c_short), # 20
("hide", ctypes.c_short), # 22
("flag", ctypes.c_short), # 24
("pntsu", ctypes.c_int), # 32
("pntsv", ctypes.c_int), # 40
("pad", ctypes.c_short * 2), # 44
("resolu", ctypes.c_short), # 46
("resolv", ctypes.c_short), # 48
("orderu", ctypes.c_short), # 50
("orderv", ctypes.c_short), # 52
("flagu", ctypes.c_short), # 54
("flagv", ctypes.c_short), # 56
("knotsu", c_float_p), # 64
("knotsv", c_float_p), # 72
("bp", BPoint_p), # 80
("bezt", BezTriple_p), # 88
("tilt_interp", ctypes.c_short), # 92
("radius_interp", ctypes.c_short), # 96
("char_idx", ctypes.c_int), # 104
]
def get_prev(self):
return (Nurb.from_address(self.prev) if self.prev else
None)
def get_next(self):
return (Nurb.from_address(self.next) if self.next else
None)
class ID(ctypes.Structure):
_pack_ = 8
_fields_ = [
("next", ctypes.c_void_p),
("prev", ctypes.c_void_p),
("newid", ctypes.c_void_p),
("lib", ctypes.c_void_p),
("name", ctypes.c_char * 66),
("flag", ctypes.c_short),
("us", ctypes.c_int),
("icon_id", ctypes.c_int),
("pad2", ctypes.c_int),
("properties", ctypes.c_void_p)
]
class Entry(ctypes.Structure):
_pack_ = 8
_fields_ = [
("next", ctypes.c_void_p),
("key", ctypes.c_void_p)
]
def get_next(self):
return (Entry.from_address(self.next) if self.next else
None)
def as_ghash_entry(self):
#ctypes.cast does not work here :/
return GHashEntry.from_address(ctypes.addressof(self))
class GHashEntry(ctypes.Structure):
_pack_ = 8
_fields_ = [
("e", Entry),
("val", ctypes.c_void_p)
]
Hash_fp = ctypes.CFUNCTYPE(ctypes.c_uint, ctypes.c_void_p)
Cmp_fp = ctypes.CFUNCTYPE(ctypes.c_uint, ctypes.c_void_p, ctypes.c_void_p)
Entry_pp = ctypes.POINTER(ctypes.POINTER(Entry))
class GHash(ctypes.Structure):
_pack_ = 8
_fields_ = [
("hashfp", Hash_fp),
("cmpfp", Cmp_fp),
("buckets", Entry_pp),
("entrypool", ctypes.c_void_p),
("nbuckets", ctypes.c_uint),
("limit_grow", ctypes.c_uint),
("limit_shrink", ctypes.c_uint),
("cursize", ctypes.c_uint),
("size_min", ctypes.c_uint),
("nentries", ctypes.c_uint),
("flag", ctypes.c_uint)
]
GHash_p = ctypes.POINTER(GHash)
class VFontData(ctypes.Structure):
_pack_ = 8
_fields_ = [
("characters", GHash_p),
("name", ctypes.c_char * 128),
("scale", ctypes.c_float)
]
VFontData_p = ctypes.POINTER(VFontData)
class VFont(ctypes.Structure):
_pack_ = 8
_fields_ = [
("id", ID),
("name", ctypes.c_char * 1024),
("data", VFontData_p),
("packedfile", ctypes.c_void_p),
("temp_pf", ctypes.c_void_p)
]
class VChar(ctypes.Structure):
_pack_ = 8
_fields_ = [
("first", ctypes.c_void_p),
("last", ctypes.c_void_p),
("index", ctypes.c_uint),
("width", ctypes.c_float),
]
if __name__ == "__main__":
scene = bpy.context.scene
text_curve = bpy.data.curves['Text']
vfont = text_curve.font
pointer = vfont.as_pointer()
vfont_struct = VFont.from_address(pointer)
print("\nName: %s" % vfont_struct.name)
print( "Name: %s" % vfont_struct.id.name)
print( "Name: %s" % vfont_struct.data.contents.name)
body = text_curve.body
char_indices = [ord(char) for char in body]
entries = [None] * len(body)
gh = vfont_struct.data.contents.characters.contents
for index, char_idx in enumerate(char_indices):
hash = gh.hashfp(char_idx)
bucket_index = hash % gh.nbuckets
e = gh.buckets[bucket_index].contents
while(e):
if e.key == char_idx:
entries[index] = e.as_ghash_entry()
break
e = e.get_next()
def create_bezier_curve(scene, data, name):
cu = bpy.data.curves.get(name)
if not cu:
cu = bpy.data.curves.new(name, "CURVE")
else:
cu.splines.clear()
for nu in data:
# will be created with one bezier point
spline = cu.splines.new("BEZIER")
spline.bezier_points.add(nu.pntsu - 1)
spline.use_cyclic_u = True
# readonly
Nurb.from_address(spline.as_pointer()).char_idx = nu.char_idx
for i, point in enumerate(spline.bezier_points):
(point.handle_left,
point.co,
point.handle_right) = nu.bezt[i].get_coordinates()
obj = bpy.data.objects.get(name)
if not obj:
obj = bpy.data.objects.new(name, cu)
scene.objects.link(obj)
else:
obj.data = cu
return obj
offset = 0.0
splines = [None] * 8
for entry, char_idx in zip(entries, char_indices):
vc = VChar.from_address(entry.val)
print("Ctypes: %d, python: %d" % (vc.index, char_idx))
nu = Nurb.from_address(vc.first) if vc.first else None
count = 0
while(nu):
splines[count] = nu
count += 1
if(ctypes.addressof(nu) == vc.last):
break
else:
nu = nu.get_prev()
cu_obj = create_bezier_curve(scene, splines[:count], "Char%04d" % vc.index)
cu_obj.location.x = offset
cu_obj.location.y = 1.0
offset += vc.width
class ListBase(ctypes.Structure):
_pack_ = 8
_fields_ = [
("first", ctypes.c_void_p),
("last", ctypes.c_void_p)
]
bl = ctypes.cdll.LoadLibrary('')
func_convert = bl.BKE_vfont_to_curve_ex
func_free = bl.BKE_nurbList_free
NULL = ctypes.c_void_p()
bmain = ctypes.c_void_p(bpy.data.as_pointer())
ob = ctypes.c_void_p(bpy.data.objects['Text'].as_pointer())
mode = 0
r_nubase = ctypes.pointer(ListBase(NULL, NULL))
r_text = NULL
r_text_len = NULL
r_text_free = NULL
r_chartransdata = NULL
result = func_convert(
bmain, ob, mode, r_nubase,
r_text, r_text_len, r_text_free,
r_chartransdata)
from collections import deque
splines = deque()
lb = r_nubase.contents
nu = Nurb.from_address(lb.first) if lb.first else None
while(nu):
splines.append(nu)
if ctypes.addressof(nu) == lb.last:
break
else:
nu = nu.get_prev()
cu_obj = create_bezier_curve(scene, splines, "Result")
cu_obj.location.y = 2.0
print("Success: %s" % result)
print(*(spline.character_index for spline in cu_obj.data.splines))
func_free(r_nubase)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment