# ScriptingText Ascii Art to 3d Text Mesh - Ways to preserve spaces when converting txt to 3d Object

So I'm trying to import some actual ascii art into the scripting window's txt editor, aiming to convert it into an extruded 3d mesh to create the basis for some quick mazes.

But upon conversion despite the looking fine in the text editor, the formatting is completely thrown off when it turns into a 3d Object, and messing with the alignment didn't help much.

I feel there is a logical answer to this problem, but have found no way to resolve my issue, any feedback on this would be nice

• I don't really know what you're doing there, that thing on the left is a text object or what? To me it looks like the decades old problem of trying to do something with a proportional font what needs an anti-proportional font. I mean, go into word, put this line art there and format it with Courier New font. I guess it would look okay. Then format it with Arial or Times New Roman and all is broken. Maybe you should choose a different font for the text object? May 21, 2021 at 8:53

You need to set the font to a monospaced font. BFont is proportional and so the characters are different sizes.

I used Consolas which is a monospaced font, and the text is exactly as it looks in the editor. I believe Consolas ships with Windows.

This is with the default BFont:

Here is a script which turns ASCII art into fully connected geometry:

It supports the characters "|_/\", and variable font size. By default, it replace the geometry of the active mesh object.

Run it with bpy.MazeGen.ASCIIGrid(D.texts["Maze"], run=True) in the console panel.

import bpy, bmesh

class ASCIIGrid():
def __init__(self, text, chardims=(0.6, 1.0), run=False, ctx=None):
self.text = "\n".join(l.body for l in text.lines) if isinstance(text, bpy.types.Text) else text
self.chardims = tuple(chardims)
self.ctx = ctx or bpy.context
if run:
self.Run()
def MakeBMesh(self):
self.bm = bmesh.new()
self.lines = self.text.split("\n")
self.height, self.width = len(self.lines)+1, (max(len(l) for l in self.lines)+1)*2
self.grid = [[self.bm.verts.new((c/2*self.chardims[0], -r*self.chardims[1], 0)) for c in range(self.width)] for r in range(self.height)]
return self
def ConnectBMesh(self):
for r, li in enumerate(self.lines):
for c, ch in enumerate(li):
if ch == "|":
self.bm.edges.new((self.grid[r][c*2+1], self.grid[r+1][c*2+1]))
elif ch == "_":
self.bm.edges.new((self.grid[r+1][c*2], self.grid[r+1][c*2+1]))
self.bm.edges.new((self.grid[r+1][c*2+1], self.grid[r+1][c*2+2]))
elif ch == "/":
self.bm.edges.new((self.grid[r+1][c*2], self.grid[r][c*2+2]))
elif ch == "\\":
self.bm.edges.new((self.grid[r][c*2], self.grid[r+1][c*2+2]))
return self
def FillBMesh(self):
for r, ro in enumerate(self.grid):
for c, ve in enumerate(ro):
if {self.grid[r+1][c] if r < self.height-1 else ..., self.grid[r-1][c] if r else ...}.intersection({v for e in ve.link_edges for v in e.verts}):
left, right = self.grid[r][c-1] if c else ..., self.grid[r][c+1] if c < self.width-1 else ...
for side in (left, right):
if side is ...:
continue
self.bm.edges.new((ve, side))
return self
def CleanBMesh(self):
used = {v for e in self.bm.edges for v in e.verts}
for v in self.bm.verts:
if v not in used:
self.bm.verts.remove(v)
def InjectToMesh(self, mesh=None):
if mesh is None:
mesh = self.ctx.object.data
self.bm.to_mesh(mesh)
return self
def Run(self, mesh=None):
self.MakeBMesh()
self.ConnectBMesh()
self.FillBMesh()
self.CleanBMesh()
self.InjectToMesh(mesh=mesh)
return self

class O:
def __init__(self, d):
self.__dict__ = d

bpy.MazeGen = O(globals())