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()