import json
import numpy as np
import ddg
[docs]class HalfedgeEncoder(json.JSONEncoder):
def __init__(self, vertex_attrs=[], edge_attrs=[], face_attrs=[], **kwargs):
super(HalfedgeEncoder, self).__init__(**kwargs)
self.vertex_slots = ["edge"]
self.vertex_attrs = vertex_attrs
self.edge_slots = ["pre", "nex", "opp", "head", "face"]
self.edge_attrs = edge_attrs
self.face_slots = ["edge"]
self.face_attrs = face_attrs
[docs] def default(self, obj):
if isinstance(obj, ddg.halfedge.Surface):
obj.verts.add_attribute("json_idx")
obj.edges.add_attribute("json_idx")
obj.faces.add_attribute("json_idx")
for i, v in enumerate(obj.verts):
v.json_idx = i
for i, e in enumerate(obj.edges):
e.json_idx = i
for i, f in enumerate(obj.faces):
f.json_idx = i
obj_dict = dict()
obj_dict["verts"] = self._create_dicts(
obj, obj.verts, self.vertex_slots, self.vertex_attrs
)
obj_dict["edges"] = self._create_dicts(
obj, obj.edges, self.edge_slots, self.edge_attrs
)
obj_dict["faces"] = self._create_dicts(
obj, obj.faces, self.face_slots, self.face_attrs
)
return obj_dict
return json.JSONEncoder.default(self, obj)
@staticmethod
def _create_dicts(surface, nodes, slots, attrs):
nodes_dicts = []
for v in nodes:
node_dict = {}
for slot in slots:
node_dict[slot] = HalfedgeEncoder._get_index(surface, getattr(v, slot))
for attr in attrs:
node_attribute = getattr(nodes, attr)
value = node_attribute[v]
if isinstance(value, np.ndarray):
node_dict[attr] = tuple(value)
else:
node_dict[attr] = value
nodes_dicts.append(node_dict)
return nodes_dicts
@staticmethod
def _get_index(surface, value):
if isinstance(value, surface.verts):
return value.json_idx
if isinstance(value, surface.edges):
return value.json_idx
if isinstance(value, surface.faces):
return value.json_idx
if value is None:
return -1
return value
[docs]def to_json_string(
surface, vertex_attrs=[], edge_attrs=[], face_attrs=[], write_index=False
):
"""
Create a json string from a given halfedge surface. Additional
attributes to
be included in the json string need to be specified explicitly in the
respective
dictionaries. All the references to vertices, edges, and faces are
realized using
integer indices. The None face corresponds to `-1`
Parameters
----------
surface : halfedge surface
The surface to be converted to a json string
vertex_attrs : list of strings
vertex attributes to be contained in the json string in addition to
`edge`
edge_attrs
edge attributes to be contained in the json string in addition to
`pre`, `nex`,
`opp`, `head`, `face`
face_attrs
face attributes to be contained in the json string in addition to
`edge`
Returns
-------
string
"""
if write_index:
halfedge_encoder = HalfedgeEncoder(
vertex_attrs + ["json_idx"],
edge_attrs + ["json_idx"],
face_attrs + ["json_idx"],
)
else:
halfedge_encoder = HalfedgeEncoder(
vertex_attrs, edge_attrs, face_attrs, indent=2
)
return halfedge_encoder.encode(surface)
[docs]def write_json_file(surface, filename, vertex_attrs=[], edge_attrs=[], face_attrs=[]):
"""
Create a file in json format from a given halfedge surface. Additional
attributes
to be included in the json string need to be specified explicitly in the
respective
dictionaries. All the references to vertices, edges, and faces are
realized using
integer indices. The None face corresponds to `-1`
Parameters
----------
surface : halfedge surface
The surface to be converted to a json string
filename : string
file to be written
vertex_attrs : list of strings
vertex attributes to be contained in the json string in addition to
`edge`
edge_attrs
edge attributes to be contained in the json string in addition to
`pre`, `nex`,
`opp`, `head`, `face`
face_attrs
face attributes to be contained in the json string in addition to
`edge`
"""
with open(filename, "w") as outfile:
outfile.write(to_json_string(surface, vertex_attrs, edge_attrs, face_attrs))
[docs]def read_json_file(filename):
"""
Create a surface from a json file. All keys will be mapped to respective
attribute
of the vertices, edges, and faces.
Parameters
----------
filename: string
file containing a json representing a halfedge surface
Returns
-------
surface
Raises
------
ValueError
If required attributes are missing in the json string
"""
with open(filename, "r") as f:
d = json.load(f)
_validate_surface_dict(d)
return _dict_to_surf(d)
[docs]def parse_json_string(json_string):
"""
Create a surface from a json string. All keys will be mapped to
respective attribute
of the vertices, edges, and faces.
Parameters
----------
json_string : string
formatted string containing a halfedge surface
Returns
-------
surface
Raises
------
ValueError
If required attributes are missing in the json string
"""
json_dict = json.loads(json_string)
_validate_surface_dict(json_dict)
return _dict_to_surf(json_dict)
def _validate_surface_dict(json_dict):
_validate_node_dict(json_dict["verts"], ["edge"])
_validate_node_dict(json_dict["edges"], ["pre", "nex", "opp", "head", "face"])
_validate_node_dict(json_dict["faces"], ["edge"])
def _validate_node_dict(nodes, required_attributes):
for n in nodes:
missing_attributes = required_attributes - n.keys()
if len(missing_attributes) > 0:
raise ValueError(
"Halfedge surface dictionary does not contain required attributes"
f" {missing_attributes}."
)
def _dict_to_surf(d):
s = ddg.halfedge.Surface()
vertices = _create_nodes_with_attributes(d, "verts", s.verts)
edges = _create_nodes_with_attributes(d, "edges", s.edges)
faces = _create_nodes_with_attributes(d, "faces", s.faces)
_assign_node_attributes(zip(vertices, d["verts"]), s.verts, vertices, edges, faces)
_assign_node_attributes(zip(edges, d["edges"]), s.edges, vertices, edges, faces)
_assign_node_attributes(zip(faces, d["faces"]), s.faces, vertices, edges, faces)
return s
def _assign_node_attributes(pairs, nodes, vertices, edges, faces):
for node, json_node in pairs:
for k in json_node.keys():
if (k == "pre") or (k == "nex") or (k == "opp") or (k == "edge"):
setattr(node, k, edges[json_node[k]])
elif k == "face":
if json_node[k] < 0:
node.face = None
else:
node.face = faces[json_node[k]]
elif k == "head":
node.head = vertices[json_node[k]]
else:
node_attribute = getattr(nodes, k)
node_attribute[node] = (
json_node[k]
if not isinstance(json_node[k], list)
else np.array(json_node[k])
)
def _create_nodes_with_attributes(d, node_name, nodes):
new_nodes = [nodes() for vd in d[node_name]]
json_node = d[node_name][0]
for attribute in json_node.keys():
if not hasattr(nodes, attribute):
nodes.add_attribute(attribute)
return new_nodes
[docs]def surface_to_ifs_json(surface, vertex_attrs=["co"]):
encoder = SurfaceToIFSEncoder(vertex_attrs)
return encoder.encode(surface)
[docs]class SurfaceToIFSEncoder(json.JSONEncoder):
def __init__(self, vertex_attrs=["co"], **kwargs):
super(SurfaceToIFSEncoder, self).__init__(**kwargs)
self.vertex_attrs = vertex_attrs
[docs] def default(self, surface):
if isinstance(surface, ddg.halfedge.Surface):
surface_dict = {}
surface.verts.add_attribute("json_idx")
for i, v in enumerate(surface.verts):
v.json_idx = i
surface_dict["faces"] = []
for f in surface.faces:
vertex_index_list = tuple(
[
v.json_idx
for v in ddg.datastructures.halfedge.get.get_vertices(f)
]
)
surface_dict["faces"].append(vertex_index_list)
if len(self.vertex_attrs) != 0:
surface_dict["verts"] = []
for v in surface.verts:
vertex_dict = {}
for attribute in self.vertex_attrs:
v_attribute = getattr(surface.verts, attribute)
value = v_attribute[v]
vertex_dict[attribute] = value.tolist()
surface_dict["verts"].append(vertex_dict)
return surface_dict
return json.JSONEncoder.default(self, surface)