0
$\begingroup$

I've been working on a script that will invoke LaTeX under the hood to generate mathematical expressions, which are then imported into Blender as SVG paths. I then convert the paths to meshes and extrude to give them some depth. However, I sometimes encounter specific cases of these objects not being extruded and not appearing in the final render, such as the fraction bar in the screenshot (with the problematic object selected), either via script or manual manipulation in the GUI. Attempting to extrude it as a region via the GUI simply results in the object moving. It is as if these pathological objects are stuck as one-dimensional objects. Any clues as to what might be going wrong?

enter image description here

EDIT: Low and behold, examining the object's vertices from the console, it appears that the fraction bar is a bloody line with only two vertices:

>>> so = bpy.context.active_object
>>> verts = so.data.vertices
>>> edges = so.data.edges
>>> faces = so.data.polygons
>>> for v in verts:
...     print(v.co)
... 
<Vector (0.0120, 0.0106, 0.0000)>
<Vector (0.0375, 0.0106, 0.0000)>

It is still unclear why this is happening and how best to detect and address it from within my script. For detection, I suppose I could check for the case of an object where faces.items() is an empty set, or where verts.items() has only two entries. From there, it is not clear what actions I should take to fix the pathological object.

Final Edit Finally found a workaround. When iterating through the objects, I identify the pathological cases and replace the two vertices with 4:

            verts=ob.data.vertices
            if len(verts) <= 2: # Is this an object with only two verts?
                newverts = [(verts[0].co.x - 0.0001,verts[0].co.y + 0.0001,0),(verts[1].co.x + 0.0001,verts[1].co.y + 0.0001,0),(verts[1].co.x + 0.0001,verts[1].co.y - 0.0001,0),(verts[0].co.x - 0.0001,verts[0].co.y - 0.0001,0)] 
                newfaces = [[0,1,2,3]] 
                newedges = []  
                ob.data.clear_geometry()
                ob.data.from_pydata(newverts,newedges,newfaces) 

Here is the code that I am using:

bl_info = {
    "name": "Equation Panel",
    "author": "Glen Martin",
    "version": (1, 0),
    "blender": (2, 92, 0),
    "location": "View3D > Sidebar > Equation Panel",
    "description": "Automated workflow for animating LaTeX equations.",
    "warning": "",
    "wiki_url": "",
    "category": "3D View"}

import bpy
import os

class TEST_OT_test_op(bpy.types.Operator):
    bl_idname = "test.test_op"
    bl_label = "Add LaTeX Object"

    def execute(self, context):
        # Create the file
        myEqnString = "$" + context.scene.my_string_prop + "$"
        f = open("C:/temp/TMP_EQN.tex", "w+")
        fstring = "\\documentclass{standalone} \n" + \
        "\\usepackage{lmodern} %or whatever you like \n" + \
        "\\usepackage[intlimits]{amsmath} \n" + \
        "\\usepackage{graphicx} \n" + \
        "\\usepackage{amsthm, amssymb, amsfonts} %Useful stuff \n" + \
        "\\begin{document} \n \\huge \n \\resizebox{.9\\hsize}{!}{" + myEqnString + "} \n" + \
        "\\end{document} "
        f.write(fstring)
        f.close()
        os.system("wsl cd /mnt/c/temp ; pdflatex ./TMP_EQN.tex ; pdftocairo -svg ./TMP_EQN.pdf ./EQN.svg")
        self.report({'INFO'}, f"Generated LaTeX SVG object {self.bl_idname}")
        # Import SVG file into Blender
        
        C = bpy.context
        
        # Names of all objects before importing the SVG 
        names_pre_import = set([ o.name for o in C.scene.objects ]) 
        
        
        # Import SVG as curve 
        fp = '/temp/EQN.svg' 
        bpy.ops.import_curve.svg(filepath = fp) 
         
        
        # Names of all objects after importing the SVG 
        names_post_import = set([ o.name for o in C.scene.objects ]) 
         
        
        # Perform set difference to find the new name and store it 
        new_object_name = names_post_import - names_pre_import
        
        bpy.ops.object.select_all(action='DESELECT') 
 
        for o in bpy.data.objects:
            # Check for given object names 
            if o.name in new_object_name: 
                o.select_set(True) 
               
        # Convert to Mesh and set attributes
        bpy.context.area.ui_type='VIEW_3D'
        bpy.ops.view3d.snap_cursor_to_selected()
        bpy.ops.transform.resize(value=(50, 50, 500), orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=True, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False)
        bpy.context.area.ui_type='VIEW_3D'
        
        mat = bpy.data.materials.get("VioletMetal")
        for ob in C.selected_objects:
            C.view_layer.objects.active = ob
            bpy.ops.object.convert(target='MESH',keep_original=False)
            # Go to edit mode, face selection mode and select all faces
            bpy.ops.object.mode_set( mode   = 'EDIT'   )
            bpy.ops.mesh.select_mode( type  = 'FACE'   )
            bpy.ops.mesh.select_all( action = 'SELECT' )
            
            bpy.ops.mesh.extrude_region_move(
                TRANSFORM_OT_translate={"value":(0, 0, 0.003)}
            )
            bpy.ops.object.mode_set( mode = 'OBJECT' )

            if len(ob.data.materials) == 0:
                ob.data.materials.append(mat)
            else:
                ob.data.materials[0]=mat
        
        # Rename collection to EQN
        
        # Clean up
        os.system("wsl cd /mnt/c/temp ; rm TMP_EQN.*; rm EQN.svg")
        
        return {'FINISHED'}

class MY_PT_EqnPanel(bpy.types.Panel):
    bl_label = "Equation Panel"
    bl_idname = "MY_PT_EqnPanel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = 'Eqn'
    
    def draw(self, context):
        layout = self.layout
        
        row = layout.row()
        row.label(text = "Add an equation", icon = 'TEXT')
        row = layout.row()
        col = self.layout.column(align = True)
        col.prop(context.scene, "my_string_prop")
        row = layout.row()
        row.operator("TEST_OT_test_op")
       
classes = (TEST_OT_test_op, MY_PT_EqnPanel)
        
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.my_string_prop = bpy.props.StringProperty \
      ( 
        name = "LaTeX String", 
        description = "LaTeX String", 
        default = "default" 
      ) 
    
def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.my_string_prop
    
if __name__== "__main__":
    register()

```
$\endgroup$
1
  • $\begingroup$ given the effort you put into debugging and providing an updated code describing both the original problem and how to fix it in your post I would suggest you provide the update as an answer to your question and mark as accepted so that the post is retained longer and searches will show that there is an answer. $\endgroup$
    – Ratt
    Apr 20 '21 at 21:12
0
$\begingroup$

I finally found a workaround. When iterating through the objects, I identify the pathological cases and replace the two vertices with 4:

            verts=ob.data.vertices
            if len(verts) <= 2: # Is this an object with only two verts?
                newverts = [(verts[0].co.x - 0.0001,verts[0].co.y + 0.0001,0),(verts[1].co.x + 0.0001,verts[1].co.y + 0.0001,0),(verts[1].co.x + 0.0001,verts[1].co.y - 0.0001,0),(verts[0].co.x - 0.0001,verts[0].co.y - 0.0001,0)] 
                newfaces = [[0,1,2,3]] 
                newedges = []  
                ob.data.clear_geometry()
                ob.data.from_pydata(newverts,newedges,newfaces) 
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.