Skip to content

Commit

Permalink
Merge pull request openalea#22 from fredboudon/jsonrep
Browse files Browse the repository at this point in the history
Jsonrep
  • Loading branch information
fredboudon authored Nov 17, 2020
2 parents 14a2656 + 7146232 commit 15d9f03
Show file tree
Hide file tree
Showing 19 changed files with 465 additions and 24 deletions.
11 changes: 6 additions & 5 deletions conda/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ requirements:
- cgal-cpp
- qhull
- ann
- boost <=1.73
- expat # [linux]
- libxcb # [linux]
- pthread-stubs # [linux]
- {{ cdt('mesa-libgl-devel') }} # [linux]
- boost # [unix]
- boost <1.74 # [win] # v1.74 has a bug on windows.
- expat # [linux]
- libxcb # [linux]
- pthread-stubs # [linux]
- {{ cdt('mesa-libgl-devel') }} # [linux]
- libglu # [linux]
- xorg-libxfixes # [linux]

Expand Down
8 changes: 8 additions & 0 deletions src/cpp/plantgl/scenegraph/appearance/texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,14 @@ Texture2D::Texture2D(const ImageTexturePtr& image,
__BaseColor(basecolor) {
}

Texture2D::Texture2D(const ImageTexturePtr& image,
const Color4& basecolor):
Appearance(),
__Image(image),
__Transformation(DEFAULT_TRANSFORMATION),
__BaseColor(basecolor) {
}


Texture2D::Texture2D(const std::string& name,
const ImageTexturePtr& image,
Expand Down
3 changes: 3 additions & 0 deletions src/cpp/plantgl/scenegraph/appearance/texture.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ class SG_API Texture2D : public Appearance
const Texture2DTransformationPtr& transformation = DEFAULT_TRANSFORMATION,
const Color4& basecolor = DEFAULT_BASECOLOR);

Texture2D(const ImageTexturePtr& image,
const Color4& basecolor);

Texture2D(const std::string& name,
const ImageTexturePtr& image,
const Texture2DTransformationPtr& transformation = DEFAULT_TRANSFORMATION,
Expand Down
2 changes: 1 addition & 1 deletion src/cpp/plantgl/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
#define __plantgl_version_h__

/// PGL Version macro
#define PGL_VERSION 0x030600
#define PGL_VERSION 0x030700

#endif

Expand Down
193 changes: 193 additions & 0 deletions src/openalea/plantgl/algo/jsonrep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import openalea.plantgl.scenegraph as sg
import openalea.plantgl.math as mt
import openalea.plantgl.scenegraph.pglinspect as inspect
from openalea.plantgl.algo.pyalgo import *


class ToJsonRepConverter (PyAlgo):
def __init__(self):
PyAlgo.__init__(self)
self.clear()

def clear(self):
self.cache = set()

def pglobject(self, obj, *args):
res = dict( type = obj.__class__.__name__, id = obj.getObjectId())
return self.pglattributes(res, obj, *args)

def pglattributes(self, res, obj):
attributes = inspect.get_pgl_attributes(obj)

for att in attributes:
if inspect.is_pgl_attribute_to_default(obj, att):
continue
res[att] = self.convert(getattr(obj,att))
return res

def convert(self,obj):
if obj is None:
return None
elif hasattr(obj,'getObjectId') :
if obj.getObjectId() in self.cache :
return { 'type' : 'Instance', 'id' : obj.getObjectId() }
else:
self.cache.add(obj.getObjectId())
return self.apply(obj)
else:
return self.apply(obj)

@for_types((bool, int, float, str))
def simpletype(self, value):
return value

@for_types(sg.Color3)
def color3(self, value):
return [value.red,value.green,value.blue]

@for_types(sg.Color4)
def color4(self, value):
return [value.red,value.green,value.blue, value.alpha]

@for_types((mt.Vector2,mt.Vector3,mt.Vector4,sg.Index,sg.Index3,sg.Index4))
def vector(self, value):
return list(value)

@for_types(sg.Transform4)
def transform4(self, value):
return { 'type' : value.__class__.__name__ , 'data' : self.convert(value.getMatrix()) }

@for_types((sg.Point2Array,sg.Point3Array,sg.Point4Array,sg.Color3Array,sg.Color4Array,sg.IndexArray,sg.Index3Array,sg.Index4Array, sg.GeometryArray, sg.RealArray, sg.Transform4Array))
def array(self, value):
return { 'type' : value.__class__.__name__ , 'id' : value.getPglId() , 'data' : list(map(self.convert,value)) }

@for_types((sg.Point2Matrix,sg.Point3Matrix,sg.Point4Matrix,sg.RealArray2))
def array2(self, value):
return { 'type' : value.__class__.__name__ , 'id' : value.getPglId() , 'data' : [list(map(self.convert,row)) for row in value] }

@for_types((mt.Matrix2,mt.Matrix3,mt.Matrix4))
def matrix(self, value):
return { 'type' : value.__class__.__name__ , 'data' : value.data() }

@for_types(sg.SceneObject)
def sceneobject(self, value):
return self.pglobject(value)

@for_types(sg.Scene)
def scene(self, value):
return { 'type' : 'Scene', 'data' : list(map(self.convert,value)) }

@for_types(sg.QuantisedFunction)
def qfunction(self, qfunc):
return { 'type' : 'QuantisedFunction', 'clamped' : qfunc.clamped, 'sampling' : qfunc.sampling, 'data' : self.convert(qfunc.input) }


class FromJsonRepConverter(PyAlgo):
def __init__(self):
PyAlgo.__init__(self)
self.clear()
self.set_default_mapping(self.default)

def clear(self):
self.cache = dict()

@for_identifiers(('Matrix2','Matrix3','Matrix4'))
def matrix(self, jsonrep):
ptype = jsonrep['type']
constructor = mt.__dict__[ptype]
dim = int(ptype[-1])
values = jsonrep['data']
values = tuple([tuple(values[dim*i:dim*(i+1)]) for i in range(dim)])
return constructor(*values)

@for_identifiers('Scene')
def scene(self, jsonrep):
return sg.Scene(list(map(self.convert,jsonrep['data'])))

@for_identifiers('Instance')
def instance(self, jsonrep):
return self.cache[jsonrep.get('id') ]

@for_identifiers('QuantisedFunction')
def qfunction(self, jsonrep):
input = self.convert(jsonrep['data'])
res = sg.QuantisedFunction(input, clamped = jsonrep['clamped'], sampling = jsonrep['sampling'])
res.input = input
return res

def array(self, values):
return list(map(self.convert, values))

def default(self, jsonrep):
name = jsonrep.get('name')
attributes = [key for key in jsonrep if not key in ['type','name','id']]
values = {}
for att in attributes:
value = self.convert(jsonrep[att])
values[att] = value
if 'data' in values:
rawdata = values['data']
del values['data']
else:
rawdata = None
ptype = jsonrep['type']
try:
constructor = sg.__dict__[ptype]
except KeyError as ke:
constructor = mt.__dict__[ptype]
toremove = []
for param, pvalue in values.items():
if pvalue is None:
toremove.append(param)
for p in toremove:
del values[p]
try:
if rawdata:
res = constructor(rawdata, **values)
else:
res = constructor(**values)
except Exception as e:
print(jsonrep)
raise e
if name:
res.name = name
return res

def convert(self, jsonrep):
if type(jsonrep) != dict:
if type(jsonrep) == list:
return self.array(jsonrep)
else:
return jsonrep
res = self.apply(jsonrep, jsonrep['type'])
self.cache[jsonrep.get('id')] = res
return res


def to_json_rep(object):
fj = ToJsonRepConverter()
return fj.convert(object)

def from_json_rep(jsonrep):
fj = FromJsonRepConverter()
return fj.convert(jsonrep)



pattern =""" @for_types([sg.%s])
def %s(self, obj):
return self.pglobject(obj, %s)
"""


def generate_class_methods():
candidates = []
for classname,cls in sg.__dict__.items():
if inspect.is_sceneobject_subclass(cls) and not inspect.is_pgl_abstract(cls):
candidates.append((classname,cls))
candidates.sort(key = lambda v : v[0])
for classname,cls in candidates:
attributes = inspect.get_pgl_attributes(cls)
print(pattern % (classname, classname.lower(), ', '.join(map(repr,attributes))))

#generate_class_methods()
44 changes: 44 additions & 0 deletions src/openalea/plantgl/algo/pyalgo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import openalea.plantgl.scenegraph.pglinspect as inspect


def for_types(types):
if not type(types) in [tuple,list]:
types = (types,)
types = sum([inspect.get_all_subclasses(t) for t in types if inspect.is_sceneobject_subclass(t)],[])+[t for t in types if not inspect.is_sceneobject_subclass(t)]
types = tuple(types)
def for_given_types(f):
f.__for_identifiers__ = types
return f
return for_given_types

def for_identifiers(identifiers):
if not type(identifiers) in [tuple,list]:
identifiers = (identifiers,)
def for_given_types(f):
f.__for_identifiers__ = identifiers
return f
return for_given_types

class PyAlgo:
def __init__(self):
self.build_conversion_map()
self.__default_mapping = None

def build_conversion_map(self):
self.__conversion_map = { }
for funcname in dir(self):
func = getattr(self,funcname)
if hasattr(func,'__for_identifiers__'):
for t in func.__for_identifiers__:
self.add_to_conversion_map(t,func)

def add_to_conversion_map(self, identifier, func):
self.__conversion_map[identifier] = func

def set_default_mapping(self, func):
self.__default_mapping = func

def apply(self, value, identifier = None):
if identifier is None:
identifier = type(value)
return self.__conversion_map.get(identifier,self.__default_mapping)(value)
5 changes: 3 additions & 2 deletions src/openalea/plantgl/codec/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import asc
from . import gts
from . import obj
from . import ply
from . import json
#from . import asc
#from . import ply
33 changes: 33 additions & 0 deletions src/openalea/plantgl/codec/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import openalea.plantgl.scenegraph as sg
import openalea.plantgl.algo.jsonrep as jr
import json

class JsonCodec (sg.SceneCodec):
""" Json File Format """

def __init__(self):
"""
Initialisation of the codec info
"""
sg.SceneCodec.__init__(self,"JSON",sg.SceneCodec.Mode.ReadWrite)

def formats(self):
""" return formats """
return [ sg.SceneFormat("Json Codec",["json"],"The json format") ]

def read(self,fname):
""" read a json file """
with open(fname, 'r') as json_file:
json_schema = json.load(json_file)
return jr.from_json_rep(json_schema)

def write(self, fname, scene):
""" Write a JSON file from a plantGL scene graph.
This method will convert a PlantGL scene graph into an JSON file.
"""
with open(fname, 'w') as json_file:
json.dump(jr.to_json_rep(scene), json_file)

codec = JsonCodec()
sg.SceneFactory.get().registerCodec(codec)
6 changes: 4 additions & 2 deletions src/openalea/plantgl/codec/obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,11 +481,13 @@ def write(self,fname,scene):
""" Write an OBJ file from a plantGL scene graph.
This method will convert a PlantGL scene graph into an OBJ file.
It does not manage materials correctly yet.
:Examples:
import openalea.plantgl.scenegraph as sg
scene = sg.Scene()"""
scene = sg.Scene()
scene.write('scene.obj')
"""
print("Write "+fname)
d = alg.Discretizer()
f = open(fname,'w')
Expand Down
5 changes: 3 additions & 2 deletions src/openalea/plantgl/scenegraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def newFunc(*args, **kwargs):
warnings.warn("Call to deprecated function %s." % func.__name__,
category=DeprecationWarning)
return func(*args, **kwargs)
newFunc.__deprecated__ = True
newFunc.__name__ = func.__name__
newFunc.__doc__ = func.__doc__
newFunc.__dict__.update(func.__dict__)
Expand All @@ -31,7 +32,7 @@ def __extrusion_get_scale(extrusion):
def __extrusion_set_scale(extrusion,value):
extrusion.scaleList = value

Extrusion.scale = property(__extrusion_get_scale,__extrusion_set_scale)
Extrusion.scale = property(__extrusion_get_scale,__extrusion_set_scale, doc='DEPRECATED')

@deprecated
def __extrusion_get_orientation(extrusion):
Expand All @@ -41,7 +42,7 @@ def __extrusion_get_orientation(extrusion):
def __extrusion_set_orientation(extrusion,value):
extrusion.orientationList = value

Extrusion.orientation = property(__extrusion_get_orientation,__extrusion_set_orientation)
Extrusion.orientation = property(__extrusion_get_orientation,__extrusion_set_orientation, doc='DEPRECATED')


""" Copy functions for Curve2D types """
Expand Down
Loading

0 comments on commit 15d9f03

Please sign in to comment.