def test_namespaces_to_string(): xml = XML.from_string( """<?xml version="1.0" encoding="UTF-8"?><robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="panda"><xacro:bamboo/></robot>""" ) xml_string = xml.to_string(prettify=True) assert b'xmlns:xacro="http://www.ros.org/wiki/xacro"' in xml_string assert b'<xacro:bamboo' in xml_string or b'<ns0:bamboo' in xml_string
def test_namespace_expansion_from_file(namespaces_file): xml = XML.from_file(namespaces_file) assert xml.root.tag == '{https://ethz.ch}main' assert list(xml.root)[0].tag == '{https://ethz.ch}item' assert list(list(xml.root)[0])[0].tag == '{https://ethz.ch}subitem' assert list(list(list( xml.root)[0])[0])[0].tag == '{https://sub.ethz.ch}cat'
def test_nested_default_namespaces_from_file(default_nested_namespace_file): xml = XML.from_file(default_nested_namespace_file) assert xml.root.attrib['xmlns'] == 'https://ita.arch.ethz.ch/' assert xml.root.attrib[ 'xmlns:xsi'] == 'http://www.w3.org/2001/XMLSchema-instance' # first element redefines default namespace assert list(xml.root)[1].attrib['xmlns'] == 'https://ethz.ch' assert list(xml.root)[1].attrib['name'] == 'item2'
def test_no_root_default_namespace(): xml = XML.from_string("""<?xml version="1.0"?> <main name="test-xml"> <item name="item1"><subitem /></item> <item xmlns="https://ethz.ch" name="item2"><subitem /></item> </main>""") assert not xml.root.attrib.get('xmlns') assert list(xml.root)[1].attrib['xmlns'] == 'https://ethz.ch' assert list(xml.root)[1].attrib['name'] == 'item2'
def test_nested_default_namespaces(): xml = XML.from_string("""<?xml version="1.0"?> <main xmlns="https://ita.arch.ethz.ch/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="test-xml"> <item name="item1"><subitem /></item> <item xmlns="https://ethz.ch" name="item2"><subitem /></item> </main>""") assert xml.root.attrib['xmlns'] == 'https://ita.arch.ethz.ch/' assert xml.root.attrib[ 'xmlns:xsi'] == 'http://www.w3.org/2001/XMLSchema-instance' # first element redefines default namespace assert list(xml.root)[1].attrib['xmlns'] == 'https://ethz.ch' assert list(xml.root)[1].attrib['name'] == 'item2'
def test_namespace_expansion(): xml = XML.from_string("""<?xml version="1.0"?> <main xmlns="https://ethz.ch" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="test-xml"> <item> <subitem xmlns:magic="https://sub.ethz.ch"> <magic:cat /> </subitem> </item> </main>""") assert xml.root.tag == '{https://ethz.ch}main' assert list(xml.root)[0].tag == '{https://ethz.ch}item' assert list(list(xml.root)[0])[0].tag == '{https://ethz.ch}subitem' assert list(list(list( xml.root)[0])[0])[0].tag == '{https://sub.ethz.ch}cat'
def _dae_mesh_importer(filename): """This is a very simple implementation of a DAE/Collada parser. It merges all solids of the DAE file into one mesh, because several other parts of the framework don't support multi-meshes per file.""" dae = XML.from_file(filename) meshes = [] for mesh_xml in dae.root.findall('.//mesh'): for triangle_set in mesh_xml.findall('triangles'): triangle_set_data = triangle_set.find('p').text.split() # Parse vertices vertices_input = triangle_set.find('input[@semantic="VERTEX"]') vertices_link = mesh_xml.find('vertices[@id="{}"]/input'.format(vertices_input.attrib['source'][1:])) positions = mesh_xml.find('source[@id="{}"]/float_array'.format(vertices_link.attrib['source'][1:])) positions = positions.text.split() vertices = list(map(float, positions[i:i + 3]) for i in range(0, len(positions), 3)) # Parse faces faces = list(map(int, triangle_set_data[::2])) # Ignore normals (ever second item is normal index) faces = list(faces[i:i + 3] for i in range(0, len(faces), 3)) mesh = Mesh.from_vertices_and_faces(vertices, faces) meshes.append(mesh) combined_mesh = meshes_join(meshes) # from compas.datastructures import mesh_transform # from compas.geometry import Frame # from compas.geometry import Transformation # former DAE files have yaxis and zaxis swapped # frame = Frame([0, 0, 0], [1, 0, 0], [0, 0, 1]) # T = Transformation.from_frame(frame) # mesh_transform(combined_mesh, T) return combined_mesh
def test_xml_to_string(sample_xml): xml = XML.from_string(sample_xml) strxml = xml.to_string('utf-8') assert strxml.startswith(b'<Tests>')
def test_xml_from_string(sample_xml): xml = XML.from_string(sample_xml) assert xml.root.tag == 'Tests'
def test_xml_from_file(sample_file): xml = XML.from_file(sample_file) assert xml.root.tag == 'Tests'
def from_srdf_string(cls, text, robot_model): """Create an instance of semantics based on an SRDF string.""" xml = XML.from_string(text) return cls.from_xml(xml, robot_model)
def from_srdf_file(cls, file, robot_model): """Create an instance of semantics based on an SRDF file path or file-like object.""" xml = XML.from_file(file) return cls.from_xml(xml, robot_model)
def test_no_default_namespace(): xml = XML.from_string( """<?xml version="1.0"?><main name="test-xml"></main>""") assert not xml.root.attrib.get('xmlns') assert xml.root.attrib['name'] == 'test-xml'
def from_srdf_file(cls, file, urdf_robot): xml = XML.from_file(file) return cls(xml.root, urdf_robot)
def _read_robot_name(self, robot_description): # TODO: Optimize this. We really don't need to parse the full URDF # only to read the robot's name (only used for local caching) xml = XML.from_string(robot_description) return xml.root.attrib['name']
def test_xml_from_string(basic_xml): xml = XML.from_string(basic_xml) assert xml.root.tag == 'Tests'
def test_xml_from_url(basic_file_url): xml = XML.from_file(basic_file_url) assert xml.root.tag == 'Tests'
def _dae_mesh_importer(filename, precision): """This is a very simple implementation of a DAE/Collada parser. Collada specification: https://www.khronos.org/files/collada_spec_1_5.pdf """ dae = XML.from_file(filename) meshes = [] visual_scenes = dae.root.find('library_visual_scenes') materials = dae.root.find('library_materials') effects = dae.root.find('library_effects') for geometry in dae.root.findall('library_geometries/geometry'): mesh_xml = geometry.find('mesh') mesh_id = geometry.attrib['id'] matrix_node = visual_scenes.find('visual_scene/node/instance_geometry[@url="#{}"]/../matrix'.format(mesh_id)) transform = None if matrix_node is not None: M = [float(i) for i in matrix_node.text.split()] # If it's the identity matrix, then ignore, we don't need to transform it if M != [1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1.]: M = M[0:4], M[4:8], M[8:12], M[12:16] transform = Transformation.from_matrix(M) # primitive elements can be any combination of: # lines, linestrips, polygons, polylist, triangles, trifans, tristrips # The current implementation only supports triangles and polylist of triangular meshes primitive_element_sets = [] primitive_element_sets.extend(mesh_xml.findall('triangles')) primitive_element_sets.extend(mesh_xml.findall('polylist')) if len(primitive_element_sets) == 0: raise Exception('No primitive elements found (currently only triangles and polylist are supported)') for primitive_element_set in primitive_element_sets: primitive_tag = primitive_element_set.tag primitive_set_data = primitive_element_set.find('p').text.split() # Try to retrieve mesh colors mesh_colors = {} if materials is not None and effects is not None: try: instance_effect = None material_id = primitive_element_set.attrib.get('material') primitive_count = int(primitive_element_set.attrib['count']) if material_id is not None: instance_effect = materials.find('material[@id="{}"]/instance_effect'.format(material_id)) if instance_effect is not None: instance_effect_id = instance_effect.attrib['url'][1:] colors = effects.findall('effect[@id="{}"]/profile_COMMON/technique/phong/*/color'.format(instance_effect_id)) for color_node in colors: rgba = [float(i) for i in color_node.text.split()] mesh_colors['mesh_color.{}'.format(color_node.attrib['sid'])] = rgba except Exception: LOGGER.exception('Exception while loading materials, all materials of mesh file %s will be ignored ', filename) # Parse vertices all_offsets = sorted([int(i.attrib['offset']) for i in primitive_element_set.findall('input[@offset]')]) if not all_offsets: raise Exception('Primitive element node does not contain offset information! Primitive tag={}'.format(primitive_tag)) vertices_input = primitive_element_set.find('input[@semantic="VERTEX"]') vertices_id = vertices_input.attrib['source'][1:] vertices_link = mesh_xml.find('vertices[@id="{}"]/input'.format(vertices_id)) positions = mesh_xml.find('source[@id="{}"]/float_array'.format(vertices_link.attrib['source'][1:])) positions = positions.text.split() vertices = [[float(p) for p in positions[i:i + 3]] for i in range(0, len(positions), 3)] # Parse faces # Every nth element is a vertex key, we ignore the rest based on the offsets defined # Usually, every second item is the normal, but there can be other items offset in there (vertex tangents, etc) skip_step = 1 + all_offsets[-1] if primitive_tag == 'triangles': vcount = [3] * primitive_count elif primitive_tag == 'polylist': vcount = [int(v) for v in primitive_element_set.find('vcount').text.split()] if len(vcount) != primitive_count: raise Exception('Primitive count does not match vertex per face count, vertex input id={}'.format(vertices_id)) fkeys = [int(f) for f in primitive_set_data[::skip_step]] faces = [] for i in range(primitive_count): a = i * vcount[i] b = a + vcount[i] faces.append(fkeys[a:b]) # Rebuild vertices and faces using the same logic that other importers # use remapping everything based on a selected precision index_key = OrderedDict() vertex = OrderedDict() for i, xyz in enumerate(vertices): key = geometric_key(xyz, precision) index_key[i] = key vertex[key] = xyz key_index = {key: index for index, key in enumerate(vertex)} index_index = {index: key_index[key] for index, key in iter(index_key.items())} vertices = [xyz for xyz in iter(vertex.values())] faces = [[index_index[index] for index in face] for face in faces] mesh = Mesh.from_vertices_and_faces(vertices, faces) if mesh_colors: mesh.attributes.update(mesh_colors) if transform: mesh_transform(mesh, transform) meshes.append(mesh) return meshes
def test_xml_to_pretty_string(sample_xml): xml = XML.from_string(sample_xml) prettyxml = xml.to_string(prettify=True) assert b"\n " in prettyxml
def from_srdf_string(cls, text, urdf_robot): xml = XML.from_string(text) return cls(xml.root, urdf_robot)
def read_robot_name_and_uris_from_urdf(self, robot_description): xml = XML.from_string(robot_description) robot_name = xml.root.attrib['name'] uris = [mesh.attrib['filename'] for mesh in xml.root.iter('mesh') if mesh.attrib['filename'] != ''] return robot_name, uris