Skip to content

Commit

Permalink
slightly more pythonic naming & convenience
Browse files Browse the repository at this point in the history
  • Loading branch information
Rostislav V. Rjabow committed Dec 5, 2022
1 parent f75543b commit 5bad46a
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 90 deletions.
63 changes: 28 additions & 35 deletions blender2mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@
from bpy.utils import register_class, unregister_class
from .utils import *

g_maxvol = 1.0
g_keepratio = 1.0
g_mergetol = 0
g_dorepair = False
g_onlysurf = False
g_convtri = True
g_endstep = '9'
g_tetgenopt = ""
enum_endstep = [('1', 'Step 1: Convert objects to mesh', 'Convert objects to mesh'),
G_MAXVOL = 1.0
G_KEEPRATIO = 1.0
G_MERGETOL = 0
G_DOREPAIR = False
G_ONLYSURF = False
G_CONVTRI = True
G_ENDSTEP = '9'
G_TETGENOPT = ""
ENUM_ENDSTEP = [('1', 'Step 1: Convert objects to mesh', 'Convert objects to mesh'),
('2', 'Step 2: Join all objects', 'Join all objects'),
('3', 'Step 3: Intersect objects', 'Intersect objects'),
('4', 'Step 4: Convert to triangles',
Expand All @@ -60,22 +60,19 @@ class scene2mesh(bpy.types.Operator):

bl_options = {"REGISTER", "UNDO"}

maxvol: bpy.props.FloatProperty(default=g_maxvol, name="Maximum tet volume")
keepratio: bpy.props.FloatProperty(default=g_keepratio, name="Fraction edge kept (0-1)")
mergetol: bpy.props.FloatProperty(default=g_mergetol, name="Tolerance to merge nodes (0 to disable)")
dorepair: bpy.props.BoolProperty(default=g_dorepair, name="Repair mesh (single object only)")
onlysurf: bpy.props.BoolProperty(default=g_onlysurf,
maxvol: bpy.props.FloatProperty(default=G_MAXVOL, name="Maximum tet volume")
keepratio: bpy.props.FloatProperty(default=G_KEEPRATIO, name="Fraction edge kept (0-1)")
mergetol: bpy.props.FloatProperty(default=G_MERGETOL, name="Tolerance to merge nodes (0 to disable)")
dorepair: bpy.props.BoolProperty(default=G_DOREPAIR, name="Repair mesh (single object only)")
onlysurf: bpy.props.BoolProperty(default=G_ONLYSURF,
name="Return triangular surface mesh only (no tetrahedral mesh)")
convtri: bpy.props.BoolProperty(default=g_convtri, name="Convert to triangular mesh first)")
endstep: bpy.props.EnumProperty(default=g_endstep, name="Run through step", items=enum_endstep)
tetgenopt: bpy.props.StringProperty(default=g_tetgenopt, name="Additional tetgen flags")
convtri: bpy.props.BoolProperty(default=G_CONVTRI, name="Convert to triangular mesh first)")
endstep: bpy.props.EnumProperty(default=G_ENDSTEP, name="Run through step", items=ENUM_ENDSTEP)
tetgenopt: bpy.props.StringProperty(default=G_TETGENOPT, name="Additional tetgen flags")

@classmethod
def description(cls, context, properties):
hints = {}
for item in enum_endstep:
hints[item[0]] = item[2]
return hints[properties.endstep]
return [desc for idx, _, desc in ENUM_ENDSTEP if idx == properties.endstep][0]

def func(self):
outputdir = GetBPWorkFolder()
Expand Down Expand Up @@ -107,15 +104,15 @@ def func(self):
bpy.ops.object.convert(target='MESH')

# at this point, objects are converted to mesh if possible
if int(self.endstep) < 2:
if self.endstep < '2':
return

bpy.ops.object.select_all(action='SELECT')
if len(bpy.context.selected_objects) >= 2:
bpy.ops.object.join()

# at this point, objects are jointed
if int(self.endstep) < 3:
if self.endstep < '3':
return

bpy.ops.object.select_all(action='DESELECT')
Expand All @@ -129,27 +126,23 @@ def func(self):
print("use fast intersection solver")

# at this point, overlapping objects are intersected
if int(self.endstep) < 4:
if self.endstep < '4':
return

if self.convtri:
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='BEAUTY', ngon_method='BEAUTY')

# at this point, if enabled, surfaces are converted to triangular meshes
if int(self.endstep) < 5:
if self.endstep < '5':
return

# output mesh data to Octave
# this works only in object mode,
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_all(action='SELECT')
obj = bpy.context.view_layer.objects.active
verts = []
for n in range(len(obj.data.vertices)):
vert = obj.data.vertices[n].co
v_global = obj.matrix_world @ vert
verts.append(v_global)
verts = [obj.matrix_world @ vert.co for vert in obj.data.vertices]
edges = [edge.vertices[:] for edge in obj.data.edges]
faces = [(np.array(face.vertices[:]) + 1).tolist() for face in obj.data.polygons]
v = np.array(verts)
Expand All @@ -166,11 +159,11 @@ def func(self):
'dorepair': self.dorepair, 'tetgenopt': self.tetgenopt}}
jd.save(meshdata, os.path.join(outputdir, 'blendermesh.jmsh'))

if int(self.endstep) == 5:
if self.endstep == '5':
bpy.ops.blender2mesh.invoke_saveas('INVOKE_DEFAULT')

# at this point, all mesh objects are saved to a jmesh file under work-dir as blendermesh.json
if int(self.endstep) < 6:
if self.endstep < '6':
return

try:
Expand All @@ -189,7 +182,7 @@ def func(self):

oc.feval('blender2mesh', os.path.join(outputdir, 'blendermesh.jmsh'), nout=0)

# import volum mesh to blender(just for user to check the result)
# import volume mesh to blender(just for user to check the result)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()

Expand All @@ -206,7 +199,7 @@ def func(self):
bpy.context.space_data.shading.type = 'WIREFRAME'

# at this point, if successful, iso2mesh generated mesh objects are imported into blender
if int(self.endstep) < 7:
if self.endstep < '7':
return

ShowMessageBox(
Expand All @@ -232,7 +225,7 @@ class setmeshingprop(bpy.types.Panel):
bl_region_type = "UI"

def draw(self, context):
global g_maxvol, g_keepratio, g_mergetol, g_dorepair, g_onlysurf, g_convtri, g_tetgenopt, g_endstep
global G_MAXVOL, G_KEEPRATIO, G_MERGETOL, G_DOREPAIR, G_ONLYSURF, G_CONVTRI, G_TETGENOPT, G_ENDSTEP
self.layout.operator("object.dialog_operator")


Expand Down
28 changes: 13 additions & 15 deletions nii2mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
import os
from .utils import *

g_maxvol = 100
g_radbound = 10
g_distbound = 1.0
g_isovalue = 0.5
g_imagetype = "multi-label"
g_method = "auto"
G_MAXVOL = 100
G_RADBOUND = 10
G_DISTBOUND = 1.0
G_ISOVALUE = 0.5
G_IMAGETYPE = "multi-label"
G_METHOD = "auto"


class nii2mesh(bpy.types.Operator):
Expand All @@ -46,10 +46,10 @@ class nii2mesh(bpy.types.Operator):

bl_options = {"REGISTER", "UNDO"}

maxvol: bpy.props.FloatProperty(default=g_maxvol, name="Maximum tetrahedron volume")
radbound: bpy.props.FloatProperty(default=g_radbound, name="Surface triangle maximum diameter")
distbound: bpy.props.FloatProperty(default=g_distbound, name="Maximum deviation from true boundary")
isovalue: bpy.props.FloatProperty(default=g_isovalue, name="Isovalue to create surface")
maxvol: bpy.props.FloatProperty(default=G_MAXVOL, name="Maximum tetrahedron volume")
radbound: bpy.props.FloatProperty(default=G_RADBOUND, name="Surface triangle maximum diameter")
distbound: bpy.props.FloatProperty(default=G_DISTBOUND, name="Maximum deviation from true boundary")
isovalue: bpy.props.FloatProperty(default=G_ISOVALUE, name="Isovalue to create surface")
imagetype: bpy.props.EnumProperty(name="Volume type", items=[('multi-label', 'multi-label', 'multi-label'),
('binary', 'binary', 'binary'),
('grayscale', 'grayscale', 'grayscale')])
Expand Down Expand Up @@ -101,10 +101,8 @@ def vol2mesh(self):
n = len(regiondata.keys()) - 1

# To import mesh.ply in batches
for i in range(0, n):
surfkey = 'MeshTri3(' + str(i + 1) + ')'
if n == 1:
surfkey = 'MeshTri3'
for i in range(n):
surfkey = 'MeshTri3' if n == 1 else f'MeshTri3({i + 1})'
if not isinstance(regiondata[surfkey], np.ndarray):
regiondata[surfkey] = np.asarray(regiondata[surfkey], dtype=np.uint32)
regiondata[surfkey] -= 1
Expand Down Expand Up @@ -134,5 +132,5 @@ class setmeshingprop(bpy.types.Panel):
bl_region_type = "UI"

def draw(self, context):
global g_maxvol, g_radbound, g_distbound, g_imagetype, g_method
global G_MAXVOL, G_RADBOUND, G_DISTBOUND, G_IMAGETYPE, G_METHOD
self.layout.operator("object.dialog_operator")
21 changes: 9 additions & 12 deletions obj2surf.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
from bpy.utils import register_class, unregister_class
from .utils import *

g_action = 'repair'
g_actionparam = 1.0
g_convtri = True
enum_action = [
G_ACTION = 'repair'
G_ACTIONPARAM = 1.0
G_CONVTRI = True
ENUM_ACTION = [
('import', 'Import surface mesh from file', 'Import surface mesh from JMesh/STL/OFF/SMF/ASC/MEDIT/GTS to Blender'),
('export', 'Export selected to JSON/JMesh', 'Export selected objects to JSON/JMesh exchange file'),
('boolean-resolve', 'Boolean-resolve: Two meshes slice each other',
Expand Down Expand Up @@ -64,16 +64,13 @@ class object2surf(bpy.types.Operator):

bl_options = {"REGISTER", "UNDO"}

action: bpy.props.EnumProperty(default=g_action, name="Operation", items=enum_action)
actionparam: bpy.props.FloatProperty(default=g_actionparam, name="Operation parameter")
convtri: bpy.props.BoolProperty(default=g_convtri, name="Convert to triangular mesh first")
action: bpy.props.EnumProperty(default=G_ACTION, name="Operation", items=ENUM_ACTION)
actionparam: bpy.props.FloatProperty(default=G_ACTIONPARAM, name="Operation parameter")
convtri: bpy.props.BoolProperty(default=G_CONVTRI, name="Convert to triangular mesh first")

@classmethod
def description(cls, context, properties):
hints = {}
for item in enum_action:
hints[item[0]] = item[2]
return hints[properties.action]
return [desc for idx, _, desc in ENUM_ACTION if idx == properties.action][0]

def func(self):
outputdir = GetBPWorkFolder()
Expand Down Expand Up @@ -187,7 +184,7 @@ class setmeshingprop(bpy.types.Panel):
bl_region_type = "UI"

def draw(self, context):
global g_action, g_actionparam, g_convtri
global G_ACTION, G_ACTIONPARAM, G_CONVTRI
self.layout.operator("object.dialog_operator")


Expand Down
52 changes: 24 additions & 28 deletions runmmc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,42 +28,42 @@
import os
from .utils import *

g_nphoton = 10000
g_tend = 5e-9
g_tstep = 5e-9
g_method = "elem"
g_outputtype = "flux"
g_isreflect = True
g_isnormalized = True
g_basisorder = 1
g_debuglevel = "TP"
g_gpuid = "1"
G_NPHOTON = 10000
G_TEND = 5e-9
G_TSTEP = 5e-9
G_METHOD = "elem"
G_OUTPUTTYPE = "flux"
G_ISREFLECT = True
G_ISNORMALIZED = True
G_BASISORDER = 1
G_DEBUGLEVEL = "TP"
G_GPUID = "1"


class runmmc(bpy.types.Operator):
bl_label = 'Run MMC photon simulation'
bl_description = "Run mesh-based Monte Carlo simulation"
bl_idname = 'blenderphotonics.runmmc'

# creat a interface to set uesrs' model parameter.
# create an interface to set user's model parameter.

bl_options = {"REGISTER", "UNDO"}

nphoton: bpy.props.FloatProperty(default=g_nphoton, name="Photon number")
tend: bpy.props.FloatProperty(default=g_tend, name="Time gate width (s)")
tstep: bpy.props.FloatProperty(default=g_tstep, name="Time gate step (s)")
isreflect: bpy.props.BoolProperty(default=g_isreflect, name="Do reflection")
isnormalized: bpy.props.BoolProperty(default=g_isnormalized, name="Normalize output")
basisorder: bpy.props.IntProperty(default=g_basisorder, step=1, name="Basis order (0 or 1)")
method: bpy.props.EnumProperty(default=g_method, name="Raytracer (use elem)",
nphoton: bpy.props.FloatProperty(default=G_NPHOTON, name="Photon number")
tend: bpy.props.FloatProperty(default=G_TEND, name="Time gate width (s)")
tstep: bpy.props.FloatProperty(default=G_TSTEP, name="Time gate step (s)")
isreflect: bpy.props.BoolProperty(default=G_ISREFLECT, name="Do reflection")
isnormalized: bpy.props.BoolProperty(default=G_ISNORMALIZED, name="Normalize output")
basisorder: bpy.props.IntProperty(default=G_BASISORDER, step=1, name="Basis order (0 or 1)")
method: bpy.props.EnumProperty(default=G_METHOD, name="Raytracer (use elem)",
items=[('elem', 'elem: Saving weight on elements', 'Saving weight on elements'),
('grid', 'grid: Dual-grid MMC (not supported)', 'Dual-grid MMC')])
outputtype: bpy.props.EnumProperty(default=g_outputtype, name="Output quantity",
outputtype: bpy.props.EnumProperty(default=G_OUTPUTTYPE, name="Output quantity",
items=[('flux', 'flux= fluence rate', 'fluence rate (J/mm^2/s)'),
('fluence', 'fluence: fluence (J/mm^2)', 'fluence in J/mm^2'),
('energy', 'energy: energy density J/mm^3', 'energy density J/mm^3')])
gpuid: bpy.props.StringProperty(default=g_gpuid, name="GPU ID (01 mask,-1=CPU)")
debuglevel: bpy.props.StringProperty(default=g_debuglevel, name="Debug flag [MCBWDIOXATRPE]")
gpuid: bpy.props.StringProperty(default=G_GPUID, name="GPU ID (01 mask,-1=CPU)")
debuglevel: bpy.props.StringProperty(default=G_DEBUGLEVEL, name="Debug flag [MCBWDIOXATRPE]")

def preparemmc(self):
# save optical parameters and source source information
Expand Down Expand Up @@ -104,7 +104,7 @@ def preparemmc(self):
oc = op.start_matlab()
except ImportError:
raise ImportError(
'To run this feature, you must install the oct2py or matlab.engine Python modulem first, based on '
'To run this feature, you must install the oct2py or matlab.engine Python modules first, based on '
'your choice of the backend')

oc.addpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'script'))
Expand All @@ -128,13 +128,9 @@ def preparemmc(self):
mmcoutput = jd.load(os.path.join(outputdir, 'mmcoutput.json'))
mmcoutput['logflux'] = np.asarray(mmcoutput['logflux'], dtype='float32')

def normalize(x, maximum, minimum):
x = (x - minimum) / (maximum - minimum)
return x

colorbit = 10
colorkind = 2 ** colorbit - 1
weight_data = normalize(mmcoutput['logflux'], np.max(mmcoutput['logflux']), np.min(mmcoutput['logflux']))
weight_data = normalize(mmcoutput['logflux'])
weight_data_test = np.rint(weight_data * colorkind)

new_vertex_group = obj.vertex_groups.new(name='weight')
Expand Down Expand Up @@ -170,5 +166,5 @@ class setmmcprop(bpy.types.Panel):
bl_region_type = "UI"

def draw(self, context):
global g_nphoton, g_tend, g_tstep, g_method, g_outputtype, g_isreflect, g_isnormalized, g_basisorder, g_debuglevel, g_gpuid
global G_NPHOTON, G_TEND, G_TSTEP, G_METHOD, G_OUTPUTTYPE, G_ISREFLECT, G_ISNORMALIZED, G_BASISORDER, G_DEBUGLEVEL, G_GPUID
self.layout.operator("object.dialog_operator")
11 changes: 11 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,14 @@ def JMeshFallback(meshobj):
if ('MeshNode' in meshobj) and (not ('MeshVertex3' in meshobj)):
meshobj['MeshVertex3'] = meshobj.pop('MeshNode')
return meshobj


def normalize(x, minimum=None, maximum=None, inplace=False):
if minimum is None:
minimum = np.min(x)
if maximum is None:
maximum = np.max(x)
if not inplace:
return (x - minimum) / (maximum - minimum)
x = (x - minimum) / (maximum - minimum)
return x

0 comments on commit 5bad46a

Please sign in to comment.