def clean_names(name, kwargs, kwargs_def=None, mode='msh'): """ Use this for name cleanup! Splits keyword arguments into names as used by the bpn module, and other keyword arguments to be used by the function. This has a similar purpose to pntools.clean_kwargs (in terms of splitting up keyword arguments, and ensuring defaults). In addition, it implements some name checking int he blender-environment. In the future, we can extend this functionality to include scene names, etc. simply by adding to default arguments. Usage: def mydrawingfunc(name=None, **kwargs): kwargs_names, kwargs_forthisfunc = clean_names(name, kwargs, defaultsformydrawingfunc) See: Draw class in bpn.turtle torus function in bpn.new """ assert mode in ('gp', 'msh', 'curve') if isinstance(name, str): kwargs[mode + '_name'] = name if mode + '_name' not in kwargs else kwargs[ mode + '_name'] kwargs['obj_name'] = name if 'obj_name' not in kwargs else kwargs[ 'obj_name'] kwargs_defdef = { mode + '_name': 'new_' + mode, 'obj_name': 'new_obj', 'coll_name': 'Collection', 'priority_obj': 'new', 'priority_' + mode: 'current', } if mode == 'gp': kwargs_defdef['layer_name'] = 'new_layer' if not kwargs_def: kwargs_def = {} kwargs_def, _ = pn.clean_kwargs(kwargs_def, kwargs_defdef) kwargs_names, kwargs_other = pn.clean_kwargs(kwargs, kwargs_def) # what to do if 'obj_name' and/or 'msh_name' already exist in the blender workspace if kwargs_names['priority_obj'] == 'new': kwargs_names['obj_name'] = new_name(kwargs_names['obj_name'], [o.name for o in bpy.data.objects]) if mode == 'msh': if kwargs_names['priority_msh'] == 'new': kwargs_names['msh_name'] = new_name( kwargs_names['msh_name'], [m.name for m in bpy.data.meshes]) if mode == 'gp': if kwargs_names['priority_gp'] == 'new': kwargs_names['gp_name'] = new_name( kwargs_names['gp_name'], [g.name for g in bpy.data.grease_pencils]) if mode == 'curve': if kwargs_names['priority_curve'] == 'new': kwargs_names['curve_name'] = new_name( kwargs_names['curve_name'], [g.name for g in bpy.data.curves]) return kwargs_names, kwargs_other
def __init__(self, name, **kwargs): names, kwargs00 = utils.clean_names(name, kwargs, { 'layer_name': 'main', 'priority_gp': 'current', 'priority_obj': 'current' }, mode='gp') gp_name = names['gp_name'] obj_name = names['obj_name'] coll_name = names['coll_name'] layer_name = names['layer_name'] # Create palette in the blender file kwargs01, kwargs02 = pn.clean_kwargs( kwargs00, { 'palette_list': ['MATLAB', 'blender_ax'], 'palette_prefix': ['MATLAB_', ''], 'palette_alpha': [1, 0.8], }) this_palette = {} for pal_name, pal_pre, pal_alpha in zip(kwargs01['palette_list'], kwargs01['palette_prefix'], kwargs01['palette_alpha']): this_palette = { **this_palette, **utils.color_palette(pal_name, pal_pre, pal_alpha) } # material library for this grease pencil for mtrl_name, rgba in this_palette.items(): # create material library utils.new_gp_color(mtrl_name, rgba) # will only create if it doesn't exist super().__init__(obj_name, core.GreasePencil(gp_name)) self.data.layer = layer_name # assign colors to this pencil's material slots for color in this_palette: self.color = color custom, _ = pn.clean_kwargs(kwargs02, { 'color': 'white', 'keyframe': 1 }) color = custom['color'] if isinstance(color, int): # NOTE: This is confusing, because the color behavior is core.greasepencilobject is different! color = 'MATLAB_{:02d}'.format(color) self.color = color self.keyframe = custom['keyframe'] self.to_coll(coll_name)
def torus(name=None, **kwargs): """ Make a torus in the x-y plane torus('mytorus', u=6, v=32, r=1, t=0.3) u = number of subdivisions in a saggital section (small circle) v = number of subdivisions in the horizontal section (big circle) r = radius of the doughnut t = thickness (radius) th = degrees to rotate the small circle """ names, kwargs = utils.clean_names(name, kwargs, {'msh_name':'torus', 'obj_name':'torus', 'priority_msh':'current', 'priority_obj':'new'}) kwargs_def = {'n_u':16, 'r_u':0.3, 'n_v':32, 'r_v':1, 'theta_offset_deg':'auto'} kwargs_alias = {'n_u':['n_u', 'u'], 'r_u':['r_u', 't', 'thickness'], 'n_v':['n_v', 'v'], 'r_v':['r_v', 'r'], 'theta_offset_deg':['theta_offset_deg', 'th', 'offset', 'th_off_deg', 'th_u']} kwargs, _ = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) a = turtle.Draw(**names) start = a.ngon(n=kwargs['n_u'], r=kwargs['r_u'], th_off_deg=kwargs['theta_offset_deg']) bmesh.ops.rotate(a.bm, verts=start.v, cent=(0, 0, 0), matrix=mathutils.Matrix.Rotation(np.radians(90.0), 3, 'Y')) for vert in start.v: vert.co += mathutils.Vector((0., -kwargs['r_v'], 0.)) end = a.spin(angle=2*np.pi-2*np.pi/kwargs['n_v'], steps=kwargs['n_v']-1, axis='z', cent=(0., 0., 0.)) a.join(start.e + end.e) tor = +a return tor
def pencil(name=None, **kwargs): """ Create a new grease pencil object. """ names, kwargs = utils.clean_names(name, kwargs, {'layer_name':'main', 'priority_gp': 'current', 'priority_obj': 'current'}, mode='gp') gp_name = names['gp_name'] obj_name = names['obj_name'] coll_name = names['coll_name'] layer_name = names['layer_name'] # Create palette in the blender file kwargs, _ = pn.clean_kwargs(kwargs, { 'palette_list': ['MATLAB', 'blender_ax'], 'palette_prefix': ['MATLAB_', ''], 'palette_alpha': [1, 0.8], }) this_palette = {} for pal_name, pal_pre, pal_alpha in zip(kwargs['palette_list'], kwargs['palette_prefix'], kwargs['palette_alpha']): this_palette = {**this_palette, **utils.color_palette(pal_name, pal_pre, pal_alpha)} # material library for this grease pencil for mtrl_name, rgba in this_palette.items(): # create material library utils.new_gp_color(mtrl_name, rgba) # will only create if it doesn't exist s = core.GreasePencilObject(obj_name, core.GreasePencil(gp_name)) s.layer = layer_name # assign colors to this pencil's material slots for color in this_palette: s.color = color s.color = 0 s.to_coll(coll_name) return s
def ngon(self, **kwargs): """ For basic polygon creation. See bpn.vef.ngon for math Supercedes a circle because of stupid return type in a circle. a = Draw() geom = a.ngon(n=6, r=1) """ kwargs_fun, _ = pn.clean_kwargs(kwargs, { 'n': 6, 'r': 1, 'theta_offset_deg': 'auto', 'fill': False }, { 'n': ['segments', 'seg', 'u', 'n'], 'r': ['radius', 'r'], 'theta_offset_deg': ['theta_offset_deg', 'th', 'offset', 'th_off_deg'], 'fill': ['fill'] }) v, e, f = vef.ngon(n=kwargs_fun['n'], r=kwargs_fun['r'], th_off_deg=kwargs_fun['theta_offset_deg']) if not kwargs_fun['fill']: f = [] self.addvef(v, e, f) return self.geom_last
def easycreate(mshfunc, name=None, **kwargs): """ **kwargs : u=16, v=8, r=0.5 for uv sphere **kwargs : size=0.5 for uv cube Warning: Avoid empty creates such as new.sphere()! """ names, kwargs = utils.clean_names(name, kwargs, {'priority_obj':'new', 'priority_msh':'current'}) if int(bpy.app.version_string.split('.')[0]) == 2: size_param_name = 'diameter' else: size_param_name = 'radius' # API change in version 3 # input control if str(mshfunc) == str(bmesh.ops.create_uvsphere): kwargs_def = {'u_segments':16, 'v_segments':8, size_param_name:0.5} kwargs_alias = {'u_segments': ['u', 'u_segments'], 'v_segments': ['v', 'v_segments'], size_param_name: ['r', 'radius', 'diameter']} kwargs, _ = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) if str(mshfunc) == str(bmesh.ops.create_cube): kwargs_def = {'size':1} kwargs_alias = {'size': ['size', 'sz', 's', 'r']} kwargs, _ = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) if str(mshfunc) == str(bmesh.ops.create_cone): kwargs_def = {'segments':12, f'{size_param_name}1':2, f'{size_param_name}2':0, 'depth':3, 'cap_ends':True, 'cap_tris':False} kwargs_alias = {'segments':['segments', 'seg', 'u', 'n'], f'{size_param_name}1':['diameter1', 'radius1', 'r1', 'r'], f'{size_param_name}2':['diameter2', 'radius2', 'r2'], 'depth':['depth', 'd', 'h'], 'cap_ends':['cap_ends', 'fill'], 'cap_tris':['cap_tris', 'fill_tri']} kwargs, _ = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) if str(mshfunc) == str(bmesh.ops.create_monkey): kwargs = {} if names['msh_name'] in [m.name for m in bpy.data.meshes]: msh = bpy.data.meshes[names['msh_name']] else: msh = bpy.data.meshes.new(names['msh_name']) bm = bmesh.new() mshfunc(bm, **kwargs) bm.to_mesh(msh) bm.free() msh.update() return mesh(msh_name=msh.name, obj_name=names['obj_name'], coll_name=names['coll_name'], pargs=kwargs)
def ngon(name=None, **kwargs): """Create a new n-sided polygon with one face inscribed in a circle of radius r.""" kwargs_def = {'n':6, 'r':1, 'theta_offset_deg':'auto', 'fill':True} kwargs_alias = {'n':['segments', 'seg', 'u', 'n'], 'r':['radius', 'r'], 'theta_offset_deg':['theta_offset_deg', 'th', 'offset', 'th_off_deg'], 'fill':['fill']} kwargs_fun, kwargs_msh = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) v, e, f = vef.ngon(n=kwargs_fun['n'], r=kwargs_fun['r'], th_off_deg=kwargs_fun['theta_offset_deg']) if not kwargs_fun['fill']: f = [] return mesh(name, v=v, e=e, f=f, **kwargs_msh)
def __init__(self, name=None, x=0, y=0, z=0, **kwargs): names, kwargs = utils.clean_names(name, kwargs, {'msh_name':'tube_msh', 'obj_name':'tube_obj', 'priority_obj':'new', 'priority_msh':'new'}) kwargs_ngon, _ = pn.clean_kwargs(kwargs, {'n':6, 'r':0.3, 'theta_offset_deg':-1}, {'n':['segments', 'seg', 'u', 'n'], 'r':['radius', 'r'], 'theta_offset_deg':['theta_offset_deg', 'th', 'offset', 'th_off_deg']}) spine = np.array([np.array((tx, ty, tz)) for tx, ty, tz in zip(x, y, z)]) normals = np.vstack((spine[1, :] - spine[0, :], spine[2:, :] - spine[:-2, :], spine[-1, :] - spine[-2, :])) a = turtle.Draw(**names) a.skin(spine, **kwargs_ngon) a_exp = a.export() this_obj = +a super().__init__(this_obj.name, this_obj.data) self.xsec = self.XSec(self, normals, a_exp)
def bezier_circle(name=None, **kwargs): """ Bezier circle of radius r. Returns core.Object """ names, kwargs = utils.clean_names(name, kwargs, {'curve_name':'Bezier', 'obj_name':'bezier', 'priority_curve':'current', 'priority_obj':'new'}, 'curve') kwargs, _ = pn.clean_kwargs(kwargs, {'r':1, 'h':None}) r = kwargs['r'] h = kwargs['h'] if h is None: h = r*(np.sqrt(2)/2 - 4*(0.5**3))/(3*(0.5**3)) # handle length for cubic bezier approx. of a circle path_obj = core.CurveObject(names['obj_name'], core.Curve(names['curve_name'])) path_obj.to_coll(names['coll_name']) spl = path_obj().data.splines.new(type='BEZIER') spl.bezier_points.add(3) spl.bezier_points[0].co = (-r, 0, 0) spl.bezier_points[1].co = (0, r, 0) spl.bezier_points[2].co = (r, 0, 0) spl.bezier_points[3].co = (0, -r, 0) spl.bezier_points[0].handle_right = (-r, h, 0) spl.bezier_points[0].handle_left = (-r, -h, 0) spl.bezier_points[1].handle_right = (h, r, 0) spl.bezier_points[1].handle_left = (-h, r, 0) spl.bezier_points[2].handle_right = (r, -h, 0) spl.bezier_points[2].handle_left = (r, h, 0) spl.bezier_points[3].handle_right = (-h, -r, 0) spl.bezier_points[3].handle_left = (h, -r, 0) spl.use_cyclic_u = True spl.order_u = 4 spl.order_v = 4 spl.resolution_u = 12 spl.resolution_v = 12 spl.tilt_interpolation = 'LINEAR' #('LINEAR', 'CARDINAL', 'BSPLINE', 'EASE') return path_obj
def __init__(self, parent, **kwargs): """ :param parent: (core.MeshObject) :param vi: (1D int32 numpy array) indices of parent vertices :param ei: (1D int32 numpy array) indices of parent edges :param fi: (1D int32 numpy array) indices of parent faces """ kwargs_def = { 'vi': [], 'ei': [], 'fi': [], 'tags': [], 'call_stack': None, 'frame': None } kwargs_alias = { 'vi': ['vi', 'v_idx'], 'ei': ['ei', 'e_idx'], 'fi': ['fi', 'f_idx'], 'tags': ['tags'], 'call_stack': ['call_stack'], 'frame': ['frame', 'coord_frame', 'm'] } kwargs_curr, _ = pn.clean_kwargs(kwargs, kwargs_def, kwargs_alias) self.parent = parent self.vi = kwargs_curr['vi'] self.ei = kwargs_curr['ei'] self.fi = kwargs_curr['fi'] self.tags = kwargs_curr['tags'] self.call_stack = kwargs_curr['call_stack'] if not kwargs_curr['frame']: self._frame = self.parent.frame else: if not isinstance(kwargs_curr['frame'], trf.CoordFrame): self._frame = trf.CoordFrame(m=kwargs_curr['frame']) else: self._frame = kwargs_curr['frame'] self._frame = self.frame # run the getter! This might seem trivial for this class, but look at the inherited classes and it should make sense
def loadSVG(svgfile, name=None, **kwargs): """ import an svg file into the blender scene. Originally created to import latex text into blender. Create text using the commands: pdflatex testdoc.tex pdftocairo -svg testdoc.pdf testdoc.svg Example: io.loadSVG(os.path.join(utils.PATH['cache'], 'testdoc.svg'), color=utils.color_palette('blender_ax')['crd_k']) """ if name is None: name = os.path.splitext(os.path.basename(svgfile))[0] kwargs_names, kwargs = utils.clean_names(name, kwargs, {'priority_curve': 'new'}, mode='curve') kwargs_def = { 'remove_default_coll': True, 'scale': (100, 100, 100), # svg imports are really small 'color': (1.0, 1.0, 1.0, 1.0), 'combine_curves': True, # this may not work!! 'halign': 'center', # 'center', 'left', 'right', None 'valign': 'middle', # 'top', 'middle', 'bottom', None } kwargs, _ = pn.clean_kwargs(kwargs, kwargs_def) @env.ReportDelta def _loadSVG(files): """ Import an SVG file into the blender scene. Hidden. Use loadSVG. """ if isinstance(files, str): files = [files] for f in files: if not os.path.exists(f): raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), f) bpy.ops.import_curve.svg(filepath=f) s = _loadSVG(svgfile) col_def = s['collections'][0] col = core.Collection(kwargs_names['coll_name'])() if kwargs['combine_curves']: utils.combine_curves(s['objects'], s['materials']) base_obj = s['objects'][0] base_curve = base_obj.data base_obj.name = kwargs_names['obj_name'] base_curve.name = kwargs_names['curve_name'] col.objects.link(base_obj) col_def.objects.unlink(base_obj) base_obj.scale = kwargs['scale'] for mtrl in base_curve.materials: mtrl.diffuse_color = kwargs['color'] # curve alignment utils.align_curve(base_obj.data, halign=kwargs['halign'], valign=kwargs['valign']) else: for obj in s['objects']: col.objects.link(obj) col_def.objects.unlink(obj) obj.scale = kwargs['scale'] # utils.align_curve(obj.data, halign=kwargs['halign'], valign=kwargs['valign']) for mtrl in s['materials']: mtrl.diffuse_color = kwargs['color'] if kwargs['remove_default_coll']: bpy.data.collections.remove(col_def) bpy.context.view_layer.update()