def add_slice(_glob, _groups, *_zs): globber = pather.format_glob(_glob, *_zs) zFinder = lambda x: map(str, _zs) # Test if any files exist if not _zs: matcher = fnmatch.translate(globber) grouper = matcher.replace('.*','(.*)') rematch = re.compile(grouper).match def zFinder(x): m = rematch(x) g = m.groups('') if m else () return (i for i in g if i.isdigit()) # For all files that match for ifile in glob.iglob(globber): # Look for Plane in SUB and VOL GROUPS sub_vol = [_groups['SUB'], _groups['VOL']] g_planes = mover.in_groups(sub_vol, 'Plane*') g_plane = next(g_planes, None) # Format name of z slice znum = int(next(zFinder(ifile))) zname = '{:d}'.format(znum) # Add plane if needed if not g_plane: g_plane = add_plane(_groups, zname, ifile) else: c_mat = g_plane.active_material cycler.tex(c_mat, zname, ifile) # Scale to current Z mover.move_z(g_plane, znum) log.yaml('Imported', globber) return {'FINISHED'}
def import_id(_glob, _groups, *_ids): status = set() context = bpy.context globber = pather.format_glob(_glob, *_ids) idFinder = lambda x: map(str, _ids) # Test if any files exist if not _ids: matcher = fnmatch.translate(globber) grouper = matcher.replace('.*', '(.*)') rematch = re.compile(grouper).match def idFinder(x): m = rematch(x) g = m.groups('') if m else () return (i for i in g if i.isdigit()) # For all files that match for ifile in glob.iglob(globber): try: status |= read_id(ifile, idFinder, _groups) except err.MeshLabelError: raise err.MeshLabelError(globber, ifile) # Set scale for newly imported mesh new_obj = context.active_object new_obj.scale = _groups['SRC'].from_mesh # Translate mesh by origin and offset vol_origin = Vector(_groups['VOL'].origin) sub_offset = Vector(_groups['SUB'].offset) new_obj.location = vol_origin + sub_offset # add modifiers mod0 = new_obj.modifiers.new('Simple', 'DECIMATE') mod1 = new_obj.modifiers.new('Smooth', 'SUBSURF') mod1.render_levels = 2 mod0.ratio = 0.16 # Apply modifiers full_mesh = new_obj.data applier = context.scene, True, 'RENDER' new_mesh = new_obj.to_mesh(*applier) new_obj.modifiers.clear() new_obj.data = new_mesh bpy.data.meshes.remove(full_mesh) # add to all groups for g in _groups.values(): bpy.ops.object.group_link(group=g.name) if not status: log.yaml('Warning, No files match', globber) return {'CANCELLED'} log.yaml('Imported', globber) return status
def add_slices(versions, arg): _groups = {} # Get group matching arguments for s,n in linker.groups(versions, arg): _groups[s] = bpy.data.groups[n] # Set default image paths _glob = os.path.join(arg.folder, arg.file) status = set() if arg.list: # Try to add all given slices for _z in sizer.parse_list(arg.list): status |= add_slice(_glob, _groups, _z) else: # Try to import all slices in folder status |= add_slice(_glob, _groups) # Warn if any imports did not finish unfinished = status - set(['FINISHED']) if len(unfinished): log.yaml('Warning, some slices', unfinished)
def import_all(versions, arg): _groups = {} # Get group matching arguments for s, n in linker.groups(versions, arg): _groups[s] = bpy.data.groups[n] # Set default mesh paths _glob = os.path.join(arg.folder, arg.file) status = set() if arg.list: # Try to import all given IDs for _id in sizer.parse_list(arg.list): status |= import_id(_glob, _groups, _id) else: # Try to import all IDs in folder status |= import_id(_glob, _groups) # Warn if any imports did not finish unfinished = status - set(['FINISHED']) if len(unfinished): log.yaml('Warning, some meshes', unfinished)
def read_id(_path, idFinder, _groups): context = bpy.context ext = _path.split(".")[-1] tmp_root = _groups['VOL'].tmp attributes = { '3ds': 'autodesk_3ds', } ext = attributes.get(ext, ext) # Need to know name if ext in ['stl', 'ply']: try: name = next(idFinder(_path)) except StopIteration: raise err.MeshLabelError('', _path) log.yaml('Importing {}'.format(name), _path) importer = getattr(bpy.ops.import_mesh, ext) # Create a symbolic link to change path name tmp_link = pather.link(_path, tmp_root, name, ext) status = importer(filepath=tmp_link) if 'FINISHED' in status: # Set color for new object new_obj = context.active_object mat = bpy.data.materials.new(name=name) mat.diffuse_color = color_label(name) new_obj.data.materials.append(mat) return status # Names and details in file if ext in ['autodesk_3ds', 'obj', 'x3d']: log.yaml('Importing Scene', _path) importer = getattr(bpy.ops.import_scene, ext) return importer(filepath=_path) # Cannot import log.yaml('Warning, unknown extension', _path) return {'CANCELLED'}
def find_links(word_loop): """ Arguments ---------- word_loop: iter(str) """ def fmt_fwd(p,q): return ' '.join([ 'SELECT ?item ?V WHERE {', 'VALUES ?roots {wd:Q%s} .' % q, '?tree (wdt:p{})* ?item'.format(p), 'OPTIONAL {', '?item wdt:P{} ?V'.format(p), '} }']) def fmt_rev(p,q): return ' '.join([ 'SELECT ?item ?V WHERE {', 'VALUES ?roots {wd:Q%s} .' % q, '?item (wdt:P{})* ?roots'.format(p), 'OPTIONAL {', '?item wdt:P{} ?V'.format(p), '} }']) WIKI_BASE = "query.wikidata.org/sparql?{}" WIKI_ID = "www.wikidata.org/w/api.php?format=json&formatversion=2&action=wbsearchentities&continue=0&language={l}&search={0}&type={t}&uselang={l}" WIKI_NAME = "www.wikidata.org/w/api.php?format=json&action=wbgetentities&props=labels&ids={t}{0}&languages={l}" rev_verb_fmt = lambda x: WIKI_NAME.format(x, t='P', l='en') rev_noun_fmt = lambda x: WIKI_NAME.format(x, t='Q', l='en') verb_fmt = lambda x: WIKI_ID.format(x, t='property', l='en') noun_fmt = lambda x: WIKI_ID.format(x, t='item', l='en') # Try to get items or properties for word in word_loop: props = wget_key(verb_fmt(word),'search') items = wget_key(noun_fmt(word),'search') if not len(items): log.yaml('ERR, No item', word) #raise err.Tree('no item', 'word', word) # Filter for matching labels (no spaces) label_props = filter_all(props,'label','description') label_items = filter_all(items,'label','description') ok_props = filter_label(label_props, no=' ') ok_items = filter_label(label_items, no=' ') # Get queries and properties get_ld = get_keys('label', 'description') word_log = { 'verb': list(map(get_ld, ok_props)), 'noun': list(map(get_ld, ok_items)), } word_log = { k: v for k, v in word_log.items() if len(v) } get_label = lambda x: x.get('labels', {}).get('en', {}).get('value') # Make tree request: if 'noun' in word_log: ok_i = next(iter(ok_items), None) if not ok_i: log.yaml('ERR {}'.format(word), 'No item matches') break ok_title = ''.join(i for i in ok_i['title'] if i.isdigit()) tree_ql = fmt_rev(31, ok_title) tree_param = urllib.parse.urlencode({ 'query': tree_ql, 'format': 'json', }) tree_url = WIKI_BASE.format(tree_param) res_key = ' '.join(get_ld(ok_i)) res = wget_key(tree_url,'results') short_res = [] for r in res.get('bindings',[]): v_raw = r.get('V', {}).get('value',None) if not v_raw: short_res.append('?') continue # Get the short name of the result v_id = v_raw.split('/')[-1] v_url = rev_noun_fmt(v_id[1:]) v_search = wget_key(v_url, 'entities') v_vals = v_search.values() v_en = filter(None,map(get_label, v_vals)) short_res.append(next(v_en, None)) log.yaml(res_key, short_res) URL = "tools.wmflabs.org/wikidata-todo/tree.html" UI = "https://angryloki.github.io/wikidata-graph-builder"
def animate(arg, versions): context = bpy.context scene = context.scene # Rate must be positive if arg.zps <= 0: 'zps {} must be >0'.format(arg.zps) log.yaml('Error', msg) return # Convert inputs to world units known = { 'um/w': [ sizer.UM, ] * 3, 'um/XYZ': sizer.parse_list(arg.XYZ, 3), 'um/VOL': sizer.parse_list(arg.VOL, 3), } w_VOL = sizer.convert(known, ['um/VOL'], ['um/w']) w_XYZ = sizer.convert(known, ['um/XYZ'], ['um/w']) # Where to animate (in world units) given = { 'volume': w_VOL, 'origin': w_XYZ, } # Select group to animate vol_k = list(given.keys()) vol_v = versions[0]['Items']['VOL'] groups = linker.match(given, vol_v, vol_k) group, likeness = next(groups) if not group: log.yaml('Error', 'No group') return # Get planes in group planes = set(mover.in_groups([group], 'Plane*')) if not planes: log.yaml('Error', 'No plane') return # Get sources for planes def plane_src(src, x): x_g = x.users_group x_s = mover.match_name(x_g, 'SRC*') return src | set(x_s) sources = reduce(plane_src, planes, set()) if not sources: log.yaml('Error', 'No source') return # Convert between world and voxels any_source = next(iter(sources)) vox_w = list(any_source['to_vox']) zvox_w = vox_w[-1] # World resolution w_min = w_XYZ[-1] w_max = w_XYZ[-1] + w_VOL[-1] # Voxel resolution v_min = int(w_min * zvox_w) v_max = int(w_max * zvox_w) log.yaml('Voxel bounds', [v_min, v_max]) if arg.zspan: def v_clamp(v): return sorted([v, v_min, v_max])[1] zspan = sizer.parse_list(arg.zspan, 2) v_span = [v_clamp(v) for v in zspan] w_min, w_max = [v / zvox_w for v in v_span] v_min, v_max = v_span log.yaml('Voxel span', [v_min, v_max]) # World step size and step count w_step = arg.zps / zvox_w slices = len(range(v_min, v_max, arg.zps)) slicer = lambda i: w_min + w_step * i # Set frames per second scene.render.fps = arg.fps # Clear all keyframes for p in planes: p.animation_data_clear() # Actually animate current_frame = 0 msg = '{1} seconds × {0:.3g} × %dμm' % sizer.UM log.yaml('Debug', msg.format(w_step, slices)) # Use world Z value for all slices for world_z in map(slicer, range(slices)): # Jump frames per z slice current_frame += arg.fps scene.frame_set(current_frame) # Move slice and change texture keyframe(group, planes, world_z) # Set first and last frame scene.frame_start = arg.fps scene.frame_end = current_frame # Return group for callbacks return group