<commit_before>from queue import Empty, Queue
from threading import Thread, Event
import logging

from .checks import Check, IntervalCheck, ScheduledCheck
from .notify import Notifier
from .notify import TwilioNotifier
from .notify import SMTPNotifier
from .notify import Message
from .notify import levels
from .notify import TelegramNotifier
from .notify import ConsoleNotifier
from .notify import HTTPNotifier

log = logging.getLogger(__name__)

class Custos:
    '''
    The custos class holds checks and notifiers and sticks them together.

    '''
    def __init__(self, checks, notifiers):
        self.queue = Queue()
        self.notifiers = notifiers
        self.checks = checks
        for check in self.checks:
            check.queue = self.queue
        self.stop_event = Event()

    def start(self):
        for check in self.checks:
            check.start()

        log.info('%s running', self.__class__.__name__)

    def run(self):
        self.start()
        while not self.stop_event.is_set():
            try:
                message = self.queue.get(block=True, timeout=1)
            except Empty:
                continue

            for notifier in self.notifiers:
                try:
                    notifier.handle_message(message)
                except:
                    log.exception(
                        '%s failed to handle message',
                        notifier.__class__.__name__
                    )

    def stop(self):
        for check in self.checks:
            check.stop()
        self.stop_event.set()
        log.info('%s stopped', self.__class__.__name__)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.stop()
<commit_msg>catch keyboard interrupt<commit_after>from queue import Empty, Queue
from threading import Thread, Event
import logging

from .checks import Check, IntervalCheck, ScheduledCheck
from .notify import Notifier
from .notify import TwilioNotifier
from .notify import SMTPNotifier
from .notify import Message
from .notify import levels
from .notify import TelegramNotifier
from .notify import ConsoleNotifier
from .notify import HTTPNotifier

log = logging.getLogger(__name__)

class Custos:
    '''
    The custos class holds checks and notifiers and sticks them together.

    '''
    def __init__(self, checks, notifiers):
        self.queue = Queue()
        self.notifiers = notifiers
        self.checks = checks
        for check in self.checks:
            check.queue = self.queue
        self.stop_event = Event()

    def start(self):
        for check in self.checks:
            check.start()

        log.info('%s running', self.__class__.__name__)

    def run(self):
        self.start()
        while not self.stop_event.is_set():
            try:
                message = self.queue.get(block=True, timeout=1)
                print(message.text)
            except Empty:
                continue

            for notifier in self.notifiers:
                try:
                    notifier.handle_message(message)
                except (KeyboardInterrupt, SystemExit):
                    raise
                except:
                    log.exception(
                        '%s failed to handle message',
                        notifier.__class__.__name__
                    )

    def stop(self):
        for check in self.checks:
            check.stop()
        self.stop_event.set()
        log.info('%s stopped', self.__class__.__name__)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.stop()
<|endoftext|>


<commit_before>import os.path
import math
import xml.etree.ElementTree as etree

from Compliant import StructuralAPI

import SofaPython.Tools
import SofaPython.units
from SofaPython import Quaternion

from SofaPython.Tools import listToStr as concat

def parseUnits(xmlModel):
    """ set SofaPython.units.local_* to units specified in <units />
    """
    if xmlModel.find("units") is not None:
        message = "units set to:"
        xmlUnits = xmlModel.find("units")
        for unit in xmlUnits.attrib:
            exec("SofaPython.units.local_{0} = SofaPython.units.{0}_{1}".format(unit,xmlUnits.attrib[unit]))
            message+= " "+unit+":"+xmlUnits.attrib[unit]
        print message

class Mesh:
    class Group:
        pass
    pass

class Deformable:

    def __init__(self, node, name):
        self.node = node.createChild( name )
        self.dofs=None

    def setMesh(self, position, meshPath):
        r = Quaternion.to_euler(position[3:])  * 180.0 / math.pi
        self.meshLoader = SofaPython.Tools.meshLoader(self.node, meshPath, translation=concat(position[:3]) , rotation=concat(r))
        self.topology = self.node.createObject('MeshTopology', name='topology', src="@"+self.meshLoader.name )
        self.dofs = self.node.createObject("MechanicalObject", template = "Vec3d", name="dofs", src="@"+self.meshLoader.name)

    def addVisual(self):
        return Deformable.VisualModel(self.node)

    class VisualModel:
        def __init__(self, node ):
            self.node = node.createChild("visual")
            self.model = self.node.createObject('VisualModel', name="model")
            self.mapping = self.node.createObject('IdentityMapping', name="mapping")

def parseMesh(xmlModel):
    """ parse meshes and their attribute
    """
    meshes=dict()
    for m in xmlModel.iter("mesh"):
        if not m.find("source") is None:
            if m.attrib["id"] in meshes:
                print "WARNING: Compliant.sml.parseMesh: mesh id {0} already defined".format(m.attrib["id"])
            mesh=Mesh()
            mesh.format = m.find("source").attrib["format"]
            mesh.source = m.find("source").text

            mesh.group=dict()
            mesh.data=dict()
            for g in m.iter("group"):
                mesh.group[g.attrib["id"]] = Mesh.Group()
                mesh.group[g.attrib["id"]].index = SofaPython.Tools.strToListInt(g.find("index").text)
                mesh.group[g.attrib["id"]].data = dict()
                for d in g.iter("data"):
                    mesh.group[g.attrib["id"]].data[d.attrib["name"]]=SofaPython.Tools.strToListFloat(d.text)

            meshes[m.attrib["id"]] = mesh

    return meshes

class Scene:
    """ Builds a (sub)scene from a sml file using compliant formulation

    <rigid>: if <density> is given, inertia is computed from mesh, else <mass> must be given
    """

    class Param:
        pass

    def __init__(self, filename, name=None):
        self.filename=filename
        self.name=name
        self.param=self.Param()
        # param to tune scene creation
        self.param.showRigid=False
        self.param.showRigidScale=0.5 # SI unit (m)
        self.param.showOffset=False
        self.param.showOffsetScale=0.1 # SI unit (m)
        self.nodes = dict() # to store special nodes
        self.rigids = dict()
        self.joints = dict()
        self.deformable = dict()

    def insertRigids(self, modelXml, parentNode):
        for r in modelXml.iter("rigid"):
            name = r.attrib["id"]
            if r.find("name") is not None:
                name = r.find("name").text
            print "rigid:", name

            if r.attrib["id"] in self.rigids:
                print "ERROR: Compliant.sml.scene: rigid defined twice, id:", r.attrib["id"]
                return

            rigid = StructuralAPI.RigidBody(parentNode, name)
            self.rigids[r.attrib["id"]] = rigid

            meshfile = None
            if not r.find("mesh") is None:
                meshfile = os.path.join(self.sceneDir, self.meshes[r.find("mesh").attrib["id"]].source)
            offset=SofaPython.Tools.strToListFloat(r.find("position").text)

            if r.find("density") is not None:
                rigid.setFromMesh(meshfile, density=float(r.find("density").text), offset=offset)
            else:
                mass=1.
                if not r.find("mass") is None:
                    mass = float(r.find("mass").text)
                rigid.setManually(offset=offset,mass=mass)
            rigid.dofs.showObject = self.param.showRigid
            rigid.dofs.showObjectScale = SofaPython.units.length_from_SI(self.param.showRigidScale)
            # visual
            if not meshfile is None:
                rigid.addVisualModel(meshfile)
                rigid.addCollisionMesh(meshfile)

    def insertJoints(self, modelXml):

        for j in modelXml.iter("jointGeneric"):
            name = j.attrib["id"]
            if j.find("name") is not None:
                name = j.find("name").text
            print "joint:", name

            if j.attrib["id"] in self.joints:
                logging.error("ERROR: Compliant.sml.scene: joint defined twice, id:", j.attrib["id"])
                return

            frames=list()
            for o in j.iter("object"):
                if not o.find("offset") is None:
                    frames.append(self.addOffset("offset_{0}".format(name), o.attrib["id"], o.find("offset")))
                else:
                    frame.append(self.rigids[o.attrib["id"]])

            if len(frames) != 2:
                logging.error("ERROR: Compliant.sml.scene: generic joint expect two objects, {0} specified".format(len(frames)))

            # dofs
            mask = [1] * 6
            for dof in j.iter("dof"):
                mask[int(dof.attrib["index"])]=0
                #TODO limits !

            joint = StructuralAPI.GenericRigidJoint(name, frames[0].node, frames[1].node, mask)
            self.joints[j.attrib["id"]] = joint

    def insertDeformables(self, modelXml, parentNode):
        for d in modelXml.iter("deformable"):
            name = d.attrib["id"]
            if d.find("name") is not None:
                name = d.find("name").text
            print "deformable:", name

            position = SofaPython.Tools.strToListFloat(d.find("position").text)
            mesh = self.meshes[d.find("mesh").attrib["id"]]
            deformable=Deformable(parentNode,name)
            self.nodes["bones"].addChild(deformable.node)
            deformable.setMesh(position, os.path.join(os.path.dirname(self.filename),mesh.source))
            deformable.addVisual()

            indices=dict()
            weights=dict()
            for s in d.iter("skinning"):
                if not s.attrib["rigid"] in self.rigids:
                    print "ERROR: Compliant.sml.Scene: skinning for deformable {0}: rigid {1} is not defined".format(name, s.attrib["rigid"])
                    continue
                currentBone = self.rigids[s.attrib["rigid"]].boneIndex
                if not (s.attrib["group"] in mesh.group and s.attrib["weight"] in mesh.group[s.attrib["group"]].data):
                    print "ERROR: Compliant.sml.Scene: skinning for deformable {0}: group {1} - weight {2} is not defined".format(name, s.attrib["group"], s.attrib["weight"])
                    continue
                group = mesh.group[s.attrib["group"]]
                weight = group.data[s.attrib["weight"]]
                for index,weight in zip(group.index, weight):
                    if not index in indices:
                        indices[index]=list()
                        weights[index]=list()
                    indices[index].append(currentBone)
                    weights[index].append(weight)

            if len(indices)>0:
                deformable.node.createObject("LinearMapping", template="Rigid3d,Vec3d", name="skinning", input="@"+self.nodes["bones"].getPathName(), indices=concat(indices.values()), weights=concat(weights.values()))

            self.deformable[d.attrib["id"]]=deformable

    def createScene(self, parentNode):
        if not os.path.exists(self.filename):
            print "ERROR: Compliant.sml.Scene: file {0} not found".format(filename)
            return None

        parentNode.createObject('RequiredPlugin', name = 'Flexible' )
        parentNode.createObject('RequiredPlugin', name = 'Compliant' )

        with open(self.filename,'r') as f:
            # relative path with respect to the scene file
            self.sceneDir = os.path.dirname(self.filename)

            # TODO automatic DTD validation could go here, not available in python builtin ElementTree module
            modelXml = etree.parse(f).getroot()
            if self.name is None:
                self.name = modelXml.attrib["name"]
            self.node=parentNode.createChild(self.name)
            print "model:", self.name

            # units
            parseUnits(modelXml)

            # meshes
            self.meshes = parseMesh(modelXml)

            # rigids
            self.insertRigids(modelXml, self.node)

            # joints
            self.insertJoints(modelXml)

            # all rigids (bones) must be gathered in a single node
            self.nodes["bones"] = self.node.createChild("bones")
            bones = self.nodes["bones"].createObject("MechanicalObject", template = "Rigid3d", name="dofs")
            input=""
            indexPairs=""
            for i,r in enumerate(self.rigids.values()):
                r.node.addChild(self.nodes["bones"])
                input += '@'+r.node.getPathName()+" "
                indexPairs += str(i) + " 0 "
                r.boneIndex=i
            self.nodes["bones"].createObject('SubsetMultiMapping', template = "Rigid3d,Rigid3d", name="mapping", input = input , output = '@./', indexPairs=indexPairs, applyRestPosition=True )

            #deformable
            self.insertDeformables(modelXml, parentNode)

    def addOffset(self, name, rigidId, xmlOffset):
        """ add xml defined offset to rigid
        """
        if rigidId not in self.rigids:
            print "ERROR: Compliant.sml.Scene: rigid {0} is unknown".format(rigidId)
            return None

        if xmlOffset is None:
            # just return rigid frame
            return self.rigids[rigidId]

        if xmlOffset.attrib["type"] == "absolute":
            offset = self.rigids[rigidId].addAbsoluteOffset(name, SofaPython.Tools.strToListFloat(xmlOffset.text))
        else:
            offset = self.rigids[rigidId].addOffset(name, SofaPython.Tools.strToListFloat(xmlOffset.text))
        offset.dofs.showObject = self.param.showOffset
        offset.dofs.showObjectScale = SofaPython.units.length_from_SI(self.param.showOffsetScale)
        return offset

<commit_msg>Compliant.sml [ADD] support following data types: int, float, string<commit_after>import os.path
import math
import xml.etree.ElementTree as etree

from Compliant import StructuralAPI

import SofaPython.Tools
import SofaPython.units
from SofaPython import Quaternion

from SofaPython.Tools import listToStr as concat

def parseUnits(xmlModel):
    """ set SofaPython.units.local_* to units specified in <units />
    """
    if xmlModel.find("units") is not None:
        message = "units set to:"
        xmlUnits = xmlModel.find("units")
        for unit in xmlUnits.attrib:
            exec("SofaPython.units.local_{0} = SofaPython.units.{0}_{1}".format(unit,xmlUnits.attrib[unit]))
            message+= " "+unit+":"+xmlUnits.attrib[unit]
        print message

class Mesh:
    class Group:
        pass
    pass

class Deformable:

    def __init__(self, node, name):
        self.node = node.createChild( name )
        self.dofs=None

    def setMesh(self, position, meshPath):
        r = Quaternion.to_euler(position[3:])  * 180.0 / math.pi
        self.meshLoader = SofaPython.Tools.meshLoader(self.node, meshPath, translation=concat(position[:3]) , rotation=concat(r))
        self.topology = self.node.createObject('MeshTopology', name='topology', src="@"+self.meshLoader.name )
        self.dofs = self.node.createObject("MechanicalObject", template = "Vec3d", name="dofs", src="@"+self.meshLoader.name)

    def addVisual(self):
        return Deformable.VisualModel(self.node)

    class VisualModel:
        def __init__(self, node ):
            self.node = node.createChild("visual")
            self.model = self.node.createObject('VisualModel', name="model")
            self.mapping = self.node.createObject('IdentityMapping', name="mapping")

def parseData(xmlData):
    """ return the list of data in xmlData
    """
    if xmlData.attrib["type"]=="float":
        return SofaPython.Tools.strToListFloat(xmlData.text)
    elif xmlData.attrib["type"]=="int":
        return SofaPython.Tools.strToListInt(xmlData.text)
    elif xmlData.attrib["type"]=="string":
        return xmlData.text.split()

def parseMesh(xmlModel):
    """ parse meshes and their attribute
    """
    meshes=dict()
    for m in xmlModel.iter("mesh"):
        if not m.find("source") is None:
            if m.attrib["id"] in meshes:
                print "WARNING: Compliant.sml.parseMesh: mesh id {0} already defined".format(m.attrib["id"])
            mesh=Mesh()
            mesh.format = m.find("source").attrib["format"]
            mesh.source = m.find("source").text

            mesh.group=dict()
            mesh.data=dict()
            for g in m.iter("group"):
                mesh.group[g.attrib["id"]] = Mesh.Group()
                mesh.group[g.attrib["id"]].index = SofaPython.Tools.strToListInt(g.find("index").text)
                mesh.group[g.attrib["id"]].data = dict()
                for d in g.iter("data"):
                    mesh.group[g.attrib["id"]].data[d.attrib["name"]]=parseData(d)

            meshes[m.attrib["id"]] = mesh

    return meshes

class Scene:
    """ Builds a (sub)scene from a sml file using compliant formulation

    <rigid>: if <density> is given, inertia is computed from mesh, else <mass> must be given
    """

    class Param:
        pass

    def __init__(self, filename, name=None):
        self.filename=filename
        self.name=name
        self.param=self.Param()
        # param to tune scene creation
        self.param.showRigid=False
        self.param.showRigidScale=0.5 # SI unit (m)
        self.param.showOffset=False
        self.param.showOffsetScale=0.1 # SI unit (m)
        self.nodes = dict() # to store special nodes
        self.rigids = dict()
        self.joints = dict()
        self.deformable = dict()

    def insertRigids(self, modelXml, parentNode):
        for r in modelXml.iter("rigid"):
            name = r.attrib["id"]
            if r.find("name") is not None:
                name = r.find("name").text
            print "rigid:", name

            if r.attrib["id"] in self.rigids:
                print "ERROR: Compliant.sml.scene: rigid defined twice, id:", r.attrib["id"]
                return

            rigid = StructuralAPI.RigidBody(parentNode, name)
            self.rigids[r.attrib["id"]] = rigid

            meshfile = None
            if not r.find("mesh") is None:
                meshfile = os.path.join(self.sceneDir, self.meshes[r.find("mesh").attrib["id"]].source)
            offset=SofaPython.Tools.strToListFloat(r.find("position").text)

            if r.find("density") is not None:
                rigid.setFromMesh(meshfile, density=float(r.find("density").text), offset=offset)
            else:
                mass=1.
                if not r.find("mass") is None:
                    mass = float(r.find("mass").text)
                rigid.setManually(offset=offset,mass=mass)
            rigid.dofs.showObject = self.param.showRigid
            rigid.dofs.showObjectScale = SofaPython.units.length_from_SI(self.param.showRigidScale)
            # visual
            if not meshfile is None:
                rigid.addVisualModel(meshfile)
                rigid.addCollisionMesh(meshfile)

    def insertJoints(self, modelXml):

        for j in modelXml.iter("jointGeneric"):
            name = j.attrib["id"]
            if j.find("name") is not None:
                name = j.find("name").text
            print "joint:", name

            if j.attrib["id"] in self.joints:
                logging.error("ERROR: Compliant.sml.scene: joint defined twice, id:", j.attrib["id"])
                return

            frames=list()
            for o in j.iter("object"):
                if not o.find("offset") is None:
                    frames.append(self.addOffset("offset_{0}".format(name), o.attrib["id"], o.find("offset")))
                else:
                    frame.append(self.rigids[o.attrib["id"]])

            if len(frames) != 2:
                logging.error("ERROR: Compliant.sml.scene: generic joint expect two objects, {0} specified".format(len(frames)))

            # dofs
            mask = [1] * 6
            for dof in j.iter("dof"):
                mask[int(dof.attrib["index"])]=0
                #TODO limits !

            joint = StructuralAPI.GenericRigidJoint(name, frames[0].node, frames[1].node, mask)
            self.joints[j.attrib["id"]] = joint

    def insertDeformables(self, modelXml, parentNode):
        for d in modelXml.iter("deformable"):
            name = d.attrib["id"]
            if d.find("name") is not None:
                name = d.find("name").text
            print "deformable:", name

            position = SofaPython.Tools.strToListFloat(d.find("position").text)
            mesh = self.meshes[d.find("mesh").attrib["id"]]
            deformable=Deformable(parentNode,name)
            self.nodes["bones"].addChild(deformable.node)
            deformable.setMesh(position, os.path.join(os.path.dirname(self.filename),mesh.source))
            deformable.addVisual()

            indices=dict()
            weights=dict()
            for s in d.iter("skinning"):
                if not s.attrib["rigid"] in self.rigids:
                    print "ERROR: Compliant.sml.Scene: skinning for deformable {0}: rigid {1} is not defined".format(name, s.attrib["rigid"])
                    continue
                currentBone = self.rigids[s.attrib["rigid"]].boneIndex
                if not (s.attrib["group"] in mesh.group and s.attrib["weight"] in mesh.group[s.attrib["group"]].data):
                    print "ERROR: Compliant.sml.Scene: skinning for deformable {0}: group {1} - weight {2} is not defined".format(name, s.attrib["group"], s.attrib["weight"])
                    continue
                group = mesh.group[s.attrib["group"]]
                weight = group.data[s.attrib["weight"]]
                for index,weight in zip(group.index, weight):
                    if not index in indices:
                        indices[index]=list()
                        weights[index]=list()
                    indices[index].append(currentBone)
                    weights[index].append(weight)

            if len(indices)>0:
                deformable.node.createObject("LinearMapping", template="Rigid3d,Vec3d", name="skinning", input="@"+self.nodes["bones"].getPathName(), indices=concat(indices.values()), weights=concat(weights.values()))

            self.deformable[d.attrib["id"]]=deformable

    def createScene(self, parentNode):
        if not os.path.exists(self.filename):
            print "ERROR: Compliant.sml.Scene: file {0} not found".format(filename)
            return None

        parentNode.createObject('RequiredPlugin', name = 'Flexible' )
        parentNode.createObject('RequiredPlugin', name = 'Compliant' )

        with open(self.filename,'r') as f:
            # relative path with respect to the scene file
            self.sceneDir = os.path.dirname(self.filename)

            # TODO automatic DTD validation could go here, not available in python builtin ElementTree module
            modelXml = etree.parse(f).getroot()
            if self.name is None:
                self.name = modelXml.attrib["name"]
            self.node=parentNode.createChild(self.name)
            print "model:", self.name

            # units
            parseUnits(modelXml)

            # meshes
            self.meshes = parseMesh(modelXml)

            # rigids
            self.insertRigids(modelXml, self.node)

            # joints
            self.insertJoints(modelXml)

            # all rigids (bones) must be gathered in a single node
            self.nodes["bones"] = self.node.createChild("bones")
            bones = self.nodes["bones"].createObject("MechanicalObject", template = "Rigid3d", name="dofs")
            input=""
            indexPairs=""
            for i,r in enumerate(self.rigids.values()):
                r.node.addChild(self.nodes["bones"])
                input += '@'+r.node.getPathName()+" "
                indexPairs += str(i) + " 0 "
                r.boneIndex=i
            self.nodes["bones"].createObject('SubsetMultiMapping', template = "Rigid3d,Rigid3d", name="mapping", input = input , output = '@./', indexPairs=indexPairs, applyRestPosition=True )

            #deformable
            self.insertDeformables(modelXml, parentNode)

    def addOffset(self, name, rigidId, xmlOffset):
        """ add xml defined offset to rigid
        """
        if rigidId not in self.rigids:
            print "ERROR: Compliant.sml.Scene: rigid {0} is unknown".format(rigidId)
            return None

        if xmlOffset is None:
            # just return rigid frame
            return self.rigids[rigidId]

        if xmlOffset.attrib["type"] == "absolute":
            offset = self.rigids[rigidId].addAbsoluteOffset(name, SofaPython.Tools.strToListFloat(xmlOffset.text))
        else:
            offset = self.rigids[rigidId].addOffset(name, SofaPython.Tools.strToListFloat(xmlOffset.text))
        offset.dofs.showObject = self.param.showOffset
        offset.dofs.showObjectScale = SofaPython.units.length_from_SI(self.param.showOffsetScale)
        return offset

<|endoftext|>


<commit_before>import os.path
import math
import xml.etree.ElementTree as etree
import sys

from Compliant import StructuralAPI

import SofaPython.Tools
import SofaPython.units
from SofaPython import Quaternion
from SofaPython.Tools import listToStr as concat
import SofaPython.mass
import SofaPython.sml
import Compliant.StructuralAPI
import Flexible.API
import Flexible.sml

import Sofa

printLog = True

def insertRigid(parentNode, rigidModel, density, param=None):
    """ create a StructuralAPI.RigidBody from the rigidModel, compute rigidMass from
        1) mass, com and inertia if present
        2) mesh if possible
        3) default to a unit sphere TODO: is it relevant to do so ?
    """

    if printLog:
        Sofa.msg_info("Compliant.sml","insertRigid "+rigidModel.name)
        for mesh in rigidModel.mesh :
            if rigidModel.meshAttributes[mesh.id].collision is True:
                Sofa.msg_info("Compliant.sml","     collision mesh: "+mesh.name)
        sys.stdout.flush()

    rigid = StructuralAPI.RigidBody(parentNode, rigidModel.name)

    # check mesh formats are supported by generateRigid
    meshFormatSupported = True
    for mesh in rigidModel.mesh :
        meshFormatSupported &= mesh.format=="obj" or mesh.format=="vtk"

    if not rigidModel.mass is None and not rigidModel.com is None and not rigidModel.inertia is None:
        # all inertial data is present, let's use it
        massinfo = SofaPython.mass.RigidMassInfo()
        massinfo.mass = rigidModel.mass # TODO: convert units ?
        massinfo.com = rigidModel.com # TODO: convert units ?

        if len(rigidModel.inertia)==3 and not rigidModel.inertia_rotation is None:
            massinfo.diagonal_inertia = rigidModel.inertia
            massinfo.inertia_rotation = rigidModel.inertia_rotation
        else:
            massinfo.setFromInertia(rigidModel.inertia[0], rigidModel.inertia[1], rigidModel.inertia[2], # Ixx, Ixy, Ixz
                                    rigidModel.inertia[3], rigidModel.inertia[4], # Iyy, Iyz
                                    rigidModel.inertia[5] ) # Izz
        rigid.setFromRigidInfo(massinfo, offset=rigidModel.position , inertia_forces = False )    # TODO: handle inertia_forces ?
    elif len(rigidModel.mesh)!=0 and meshFormatSupported:
        # get inertia from meshes and density
        massinfo = SofaPython.sml.getSolidRigidMassInfo(rigidModel, density)
        rigid.setFromRigidInfo(massinfo, offset=rigidModel.position , inertia_forces = False )    # TODO: handle inertia_forces ?

        #if not rigidModel.mass is None :
            ## no density but a mesh let's normalise computed mass with specified mass
            #mass= SofaPython.units.mass_from_SI(rigidModel.mass)
            #inertia = []
            #for inert,m in zip(rigid.mass.inertia, rigid.mass.mass):
                #for i in inert:
                    #inertia.append( i/m[0]*mass)
            #rigid.mass.inertia = concat(inertia)
            #rigid.mass.mass = mass
    else:
        # no mesh, get mass/inertia if present, default to a unit sphere
        Sofa.msg_warning("Compliant.sml","insertRigid: using default rigidMass")
        mass = rigidModel.mass  if not rigidModel.mass is None else SofaPython.units.mass_from_SI(1.)
        inertia = [1,1,1]
        t = rigidModel.position
        if not rigidModel.com is None:
            t[0] += rigidModel.com[0]
            t[1] += rigidModel.com[1]
            t[2] += rigidModel.com[2]
        rigid.setManually(t, mass, inertia)

    if not param is None:
        rigid.dofs.showObject = param.showRigid
        rigid.dofs.showObjectScale = SofaPython.units.length_from_SI(param.showRigidScale)

    # walk around to handle multiple meshes
    # @todo: handle them in StructuralAPI ?
    rigid.collisions=dict()
    rigid.visuals=dict()
    for mesh in rigidModel.mesh :
        if rigidModel.meshAttributes[mesh.id].collision is True:
            rigid.collisions[mesh.id] = rigid.addCollisionMesh(mesh.source,name_suffix='_'+mesh.name)
            if rigidModel.meshAttributes[mesh.id].visual is True:
                rigid.visuals[mesh.id] = rigid.collisions[mesh.id].addVisualModel()
        elif rigidModel.meshAttributes[mesh.id].visual is True:
            rigid.visuals[mesh.id] = rigid.addVisualModel(mesh.source,name_suffix='_'+mesh.name)

    return rigid

def insertJoint(jointModel, rigids, param):
    """ create a StructuralAPI.GenericRigidJoint from the jointModel """

    frames=list()
    for i,offset in enumerate(jointModel.offsets):
        if not jointModel.solids[i].id in rigids:
            Sofa.msg_warning("Compliant.sml","insertJoint "+jointModel.name+" failed: "+jointModel.solids[i].id+" is not a rigid body")
            return None
        rigid = rigids[jointModel.solids[i].id] # shortcut
        if not offset is None:
            if offset.isAbsolute():
                frames.append( rigid.addAbsoluteOffset(offset.name, offset.value))
            else:
                frames.append( rigid.addOffset(offset.name, offset.value) )
            if not param is None:
                frames[-1].dofs.showObject = param.showOffset
                frames[-1].dofs.showObjectScale = SofaPython.units.length_from_SI(param.showOffsetScale)
        else:
            frames.append(rigid)

    if printLog:
        Sofa.msg_info("Compliant.sml","insertJoint "+jointModel.name)

    mask = [1]*6
    limits=[] # mask for limited dofs
    isLimited = True # does the joint have valid limits?
    for d in jointModel.dofs:
        if isLimited:
            if d.min==None or d.max==None:
                isLimited = False # as soon as a limit is not defined, the limits cannot work
            else:
                limits.append(d.min)
                limits.append(d.max)
        mask[d.index] = 0

    joint = StructuralAPI.GenericRigidJoint(jointModel.name, frames[0].node, frames[1].node, mask,
                 compliance=SofaPython.sml.getValueByTag(param.jointComplianceByTag, jointModel.tags),
                 isCompliance=SofaPython.sml.getValueByTag(param.jointIsComplianceByTag, jointModel.tags))
    if isLimited:
        joint.addLimits(limits)
    return joint

class SceneArticulatedRigid(SofaPython.sml.BaseScene):
    """ Builds a (sub)scene from a model using compliant formulation
    [tag] rigid are simulated as RigidBody, more tags can be added to param.rigidTags
    Compliant joints are setup between the rigids """

    def __init__(self, parentNode, model):
        SofaPython.sml.BaseScene.__init__(self, parentNode, model)

        self.rigids = dict()
        self.joints = dict()
        self.meshExporters = list()

        # the set of tags simulated as rigids
        self.param.rigidTags={"rigid"}

        self.param.geometricStiffness=0 # TODO doc on the possible values !
        # for tagged joints, values come from these dictionnaries if they contain one of the tag
        self.param.jointIsComplianceByTag=dict()
        self.param.jointComplianceByTag=dict()

        # default joint is set up using isCompliance=True and self.param.jointCompliance value
        self.param.jointIsComplianceByTag["default"]=True
        self.param.jointComplianceByTag["default"]=0 # TODO add 2 default values: for translation and rotation dofs ?

        self.param.showRigid=False
        self.param.showRigidScale=0.5 # SI unit (m)
        self.param.showOffset=False
        self.param.showOffsetScale=0.1 # SI unit (m)

    def insertMergeRigid(self, mergeNodeName="dofRigid", tag="rigid", rigidIndexById=None ):
        """ Merge all the rigids in a single MechanicalObject using a SubsetMultiMapping
        optionnaly give a tag to select the rigids which are merged
        return the created node"""
        mergeNode = None
        currentRigidIndex=0
        input=""
        indexPairs=""
        if tag in self.model.solidsByTag:
            for solid in self.model.solidsByTag[tag]:
                if not solid.id in self.rigids:
                    Sofa.msg_warning("Compliant.sml","SceneArticulatedRigid.insertMergeRigid: "+solid.name+" is not a rigid")
                    continue
                rigid = self.rigids[solid.id]
                if mergeNode is None:
                    mergeNode = rigid.node.createChild(mergeNodeName)
                else:
                    rigid.node.addChild(mergeNode)
                input += '@'+rigid.node.getPathName()+" "
                indexPairs += str(currentRigidIndex) + " 0 "
                if not rigidIndexById is None:
                    rigidIndexById[solid.id]=currentRigidIndex
                currentRigidIndex+=1
        if input:
            mergeNode.createObject("MechanicalObject", template = "Rigid3", name="dofs")
            mergeNode.createObject('SubsetMultiMapping', template = "Rigid3,Rigid3", name="mapping", input = input , output = '@./', indexPairs=indexPairs, applyRestPosition=True )
        return mergeNode

    def addMeshExporters(self, dir, ExportAtEnd=False):
        """ add obj Exporters for each visual model of the scene
        """
        if not os.path.exists(dir):
            os.makedirs(dir)
        for rigid in self.rigids.values():
            for mid,visual in rigid.visuals.iteritems():
                filename = os.path.join(dir, os.path.basename(self.model.meshes[mid].source))
                e = visual.node.createObject('ObjExporter', name='ObjExporter', filename=filename, printLog=True, ExportAtEnd=ExportAtEnd)
                self.meshExporters.append(e)

    def exportMeshes(self):
        for e in self.meshExporters:
            e.writeOBJ()

    def createScene(self):
        self.node.createObject('RequiredPlugin', name = 'Flexible' )
        self.node.createObject('RequiredPlugin', name = 'Compliant' )

        Compliant.StructuralAPI.geometric_stiffness = self.param.geometricStiffness

        # rigids
        for tag in self.param.rigidTags:
            if tag in self.model.solidsByTag:
                for rigidModel in self.model.solidsByTag[tag]:
                    self.rigids[rigidModel.id] = insertRigid(self.node, rigidModel, self.material.density(self.getMaterial(rigidModel.id)) , self.param)

        # joints
        for jointModel in self.model.genericJoints.values():
            self.joints[jointModel.id] = insertJoint(jointModel, self.rigids, self.param)

class SceneSkinning(SceneArticulatedRigid) :
    """ Build a (sub-)scene based on SceneArticulatedRigid, add solids with skinning using the defined armature
    [tag] armature are simulated as RigidBody and used as bones for skinning
    """

    def __init__(self, parentNode, model):
        SceneArticulatedRigid.__init__(self, parentNode, model)
        self.deformables = dict()
        self.skinningArmatureBoneIndexById = dict() # keep track of bone armature index in the armature merge node

    def createScene(self):

        self.model.setTagFromTag("armature", "rigid")

        SceneArticulatedRigid.createScene(self)

        if "armature" in self.model.solidsByTag:
            # insert node containing all bones of the armature
            self.nodes["armature"] = self.insertMergeRigid(mergeNodeName="armature", tag="armature", rigidIndexById=self.skinningArmatureBoneIndexById)
            for solidModel in self.model.solids.values():
                print solidModel.name, len(solidModel.skinnings)
                if len(solidModel.skinnings)>0: # ignore solid if it has no skinning
                    # for each mesh create a Flexible.API.Deformable
                    for mesh in solidModel.mesh:
                        # take care only of visual meshes with skinning
                        if solidModel.meshAttributes[mesh.id].visual:
                            deformable = Flexible.API.Deformable(self.nodes["armature"], solidModel.name+"_"+mesh.name)
                            deformable.loadMesh(mesh.source)
                            deformable.addMechanicalObject()
                            (indices, weights) = Flexible.sml.getSolidSkinningIndicesAndWeights(solidModel, self.skinningArmatureBoneIndexById)
                            deformable.addSkinning(self.nodes["armature"], indices.values(), weights.values())
                            deformable.addVisual()
                            self.deformables[mesh.id] = deformable

#                        self.deformables[solidModel.id]=Flexible.API.Deformable(self.nodes["armature"], solidModel.name)
#                    self.deformables[solidModel.id]=Flexible.sml.insertDeformableWithSkinning(self.node, solidModel, self.nodes["armature"].getPathName(), bonesId)

<commit_msg>[Compliant.sml] more robust<commit_after>import os.path
import math
import xml.etree.ElementTree as etree
import sys

from Compliant import StructuralAPI

import SofaPython.Tools
import SofaPython.units
from SofaPython import Quaternion
from SofaPython.Tools import listToStr as concat
import SofaPython.mass
import SofaPython.sml
import Compliant.StructuralAPI
import Flexible.API
import Flexible.sml

import Sofa

printLog = True

def insertRigid(parentNode, rigidModel, density, param=None):
    """ create a StructuralAPI.RigidBody from the rigidModel, compute rigidMass from
        1) mass, com and inertia if present
        2) mesh if possible
        3) default to a unit sphere TODO: is it relevant to do so ?
    """

    if printLog:
        Sofa.msg_info("Compliant.sml","insertRigid "+rigidModel.name)
        for mesh in rigidModel.mesh :
            if rigidModel.meshAttributes[mesh.id].collision is True:
                Sofa.msg_info("Compliant.sml","     collision mesh: "+mesh.name)
        sys.stdout.flush()

    rigid = StructuralAPI.RigidBody(parentNode, rigidModel.name)

    # check mesh formats are supported by generateRigid
    meshFormatSupported = True
    for mesh in rigidModel.mesh :
        meshFormatSupported &= mesh.format=="obj" or mesh.format=="vtk"

    if not rigidModel.mass is None and not rigidModel.com is None and not rigidModel.inertia is None:
        # all inertial data is present, let's use it
        massinfo = SofaPython.mass.RigidMassInfo()
        massinfo.mass = rigidModel.mass # TODO: convert units ?
        massinfo.com = rigidModel.com # TODO: convert units ?

        if len(rigidModel.inertia)==3 and not rigidModel.inertia_rotation is None:
            massinfo.diagonal_inertia = rigidModel.inertia
            massinfo.inertia_rotation = rigidModel.inertia_rotation
        else:
            massinfo.setFromInertia(rigidModel.inertia[0], rigidModel.inertia[1], rigidModel.inertia[2], # Ixx, Ixy, Ixz
                                    rigidModel.inertia[3], rigidModel.inertia[4], # Iyy, Iyz
                                    rigidModel.inertia[5] ) # Izz
        rigid.setFromRigidInfo(massinfo, offset=rigidModel.position , inertia_forces = False )    # TODO: handle inertia_forces ?
    elif len(rigidModel.mesh)!=0 and meshFormatSupported:
        # get inertia from meshes and density
        massinfo = SofaPython.sml.getSolidRigidMassInfo(rigidModel, density)
        rigid.setFromRigidInfo(massinfo, offset=rigidModel.position , inertia_forces = False )    # TODO: handle inertia_forces ?

        #if not rigidModel.mass is None :
            ## no density but a mesh let's normalise computed mass with specified mass
            #mass= SofaPython.units.mass_from_SI(rigidModel.mass)
            #inertia = []
            #for inert,m in zip(rigid.mass.inertia, rigid.mass.mass):
                #for i in inert:
                    #inertia.append( i/m[0]*mass)
            #rigid.mass.inertia = concat(inertia)
            #rigid.mass.mass = mass
    else:
        # no mesh, get mass/inertia if present, default to a unit sphere
        Sofa.msg_warning("Compliant.sml","insertRigid: using default rigidMass")
        mass = rigidModel.mass  if not rigidModel.mass is None else SofaPython.units.mass_from_SI(1.)
        inertia = [1,1,1]
        t = rigidModel.position
        if not rigidModel.com is None:
            t[0] += rigidModel.com[0]
            t[1] += rigidModel.com[1]
            t[2] += rigidModel.com[2]
        rigid.setManually(t, mass, inertia)

    if not param is None:
        rigid.dofs.showObject = param.showRigid
        rigid.dofs.showObjectScale = SofaPython.units.length_from_SI(param.showRigidScale)

    # walk around to handle multiple meshes
    # @todo: handle them in StructuralAPI ?
    rigid.collisions=dict()
    rigid.visuals=dict()
    for mesh in rigidModel.mesh :
        if rigidModel.meshAttributes[mesh.id].collision is True:
            rigid.collisions[mesh.id] = rigid.addCollisionMesh(mesh.source,name_suffix='_'+mesh.name)
            if rigidModel.meshAttributes[mesh.id].visual is True:
                rigid.visuals[mesh.id] = rigid.collisions[mesh.id].addVisualModel()
        elif rigidModel.meshAttributes[mesh.id].visual is True:
            rigid.visuals[mesh.id] = rigid.addVisualModel(mesh.source,name_suffix='_'+mesh.name)

    return rigid

def insertJoint(jointModel, rigids, param):
    """ create a StructuralAPI.GenericRigidJoint from the jointModel """

    frames=list()
    for i,offset in enumerate(jointModel.offsets):
        if not jointModel.solids[i].id in rigids:
            Sofa.msg_warning("Compliant.sml","insertJoint "+jointModel.name+" failed: "+jointModel.solids[i].id+" is not a rigid body")
            return None
        rigid = rigids[jointModel.solids[i].id] # shortcut
        if rigid is None:
            Sofa.msg_warning("Compliant.sml", "in joint {0}, solid {1} is missing, ignored".format(jointModel.name, jointModel.solids[i].id))
            return
        if not offset is None:
            if offset.isAbsolute():
                frames.append( rigid.addAbsoluteOffset(offset.name, offset.value))
            else:
                frames.append( rigid.addOffset(offset.name, offset.value) )
            if not param is None:
                frames[-1].dofs.showObject = param.showOffset
                frames[-1].dofs.showObjectScale = SofaPython.units.length_from_SI(param.showOffsetScale)
        else:
            frames.append(rigid)

    if printLog:
        Sofa.msg_info("Compliant.sml","insertJoint "+jointModel.name)

    mask = [1]*6
    limits=[] # mask for limited dofs
    isLimited = True # does the joint have valid limits?
    for d in jointModel.dofs:
        if isLimited:
            if d.min==None or d.max==None:
                isLimited = False # as soon as a limit is not defined, the limits cannot work
            else:
                limits.append(d.min)
                limits.append(d.max)
        mask[d.index] = 0

    joint = StructuralAPI.GenericRigidJoint(jointModel.name, frames[0].node, frames[1].node, mask,
                 compliance=SofaPython.sml.getValueByTag(param.jointComplianceByTag, jointModel.tags),
                 isCompliance=SofaPython.sml.getValueByTag(param.jointIsComplianceByTag, jointModel.tags))
    if isLimited:
        joint.addLimits(limits)
    return joint

class SceneArticulatedRigid(SofaPython.sml.BaseScene):
    """ Builds a (sub)scene from a model using compliant formulation
    [tag] rigid are simulated as RigidBody, more tags can be added to param.rigidTags
    Compliant joints are setup between the rigids """

    def __init__(self, parentNode, model):
        SofaPython.sml.BaseScene.__init__(self, parentNode, model)

        self.rigids = dict()
        self.joints = dict()
        self.meshExporters = list()

        # the set of tags simulated as rigids
        self.param.rigidTags={"rigid"}

        self.param.geometricStiffness=0 # TODO doc on the possible values !
        # for tagged joints, values come from these dictionnaries if they contain one of the tag
        self.param.jointIsComplianceByTag=dict()
        self.param.jointComplianceByTag=dict()

        # default joint is set up using isCompliance=True and self.param.jointCompliance value
        self.param.jointIsComplianceByTag["default"]=True
        self.param.jointComplianceByTag["default"]=0 # TODO add 2 default values: for translation and rotation dofs ?

        self.param.showRigid=False
        self.param.showRigidScale=0.5 # SI unit (m)
        self.param.showOffset=False
        self.param.showOffsetScale=0.1 # SI unit (m)

    def insertMergeRigid(self, mergeNodeName="dofRigid", tag="rigid", rigidIndexById=None ):
        """ Merge all the rigids in a single MechanicalObject using a SubsetMultiMapping
        optionnaly give a tag to select the rigids which are merged
        return the created node"""
        mergeNode = None
        currentRigidIndex=0
        input=""
        indexPairs=""
        if tag in self.model.solidsByTag:
            for solid in self.model.solidsByTag[tag]:
                if not solid.id in self.rigids:
                    Sofa.msg_warning("Compliant.sml","SceneArticulatedRigid.insertMergeRigid: "+solid.name+" is not a rigid")
                    continue
                rigid = self.rigids[solid.id]
                if mergeNode is None:
                    mergeNode = rigid.node.createChild(mergeNodeName)
                else:
                    rigid.node.addChild(mergeNode)
                input += '@'+rigid.node.getPathName()+" "
                indexPairs += str(currentRigidIndex) + " 0 "
                if not rigidIndexById is None:
                    rigidIndexById[solid.id]=currentRigidIndex
                currentRigidIndex+=1
        if input:
            mergeNode.createObject("MechanicalObject", template = "Rigid3", name="dofs")
            mergeNode.createObject('SubsetMultiMapping', template = "Rigid3,Rigid3", name="mapping", input = input , output = '@./', indexPairs=indexPairs, applyRestPosition=True )
        return mergeNode

    def addMeshExporters(self, dir, ExportAtEnd=False):
        """ add obj Exporters for each visual model of the scene
        """
        if not os.path.exists(dir):
            os.makedirs(dir)
        for rigid in self.rigids.values():
            for mid,visual in rigid.visuals.iteritems():
                filename = os.path.join(dir, os.path.basename(self.model.meshes[mid].source))
                e = visual.node.createObject('ObjExporter', name='ObjExporter', filename=filename, printLog=True, ExportAtEnd=ExportAtEnd)
                self.meshExporters.append(e)

    def exportMeshes(self):
        for e in self.meshExporters:
            e.writeOBJ()

    def createScene(self):
        self.node.createObject('RequiredPlugin', name = 'Flexible' )
        self.node.createObject('RequiredPlugin', name = 'Compliant' )

        Compliant.StructuralAPI.geometric_stiffness = self.param.geometricStiffness

        # rigids
        for tag in self.param.rigidTags:
            if tag in self.model.solidsByTag:
                for rigidModel in self.model.solidsByTag[tag]:
                    self.rigids[rigidModel.id] = insertRigid(self.node, rigidModel, self.material.density(self.getMaterial(rigidModel.id)) , self.param)

        # joints
        for jointModel in self.model.genericJoints.values():
            self.joints[jointModel.id] = insertJoint(jointModel, self.rigids, self.param)

class SceneSkinning(SceneArticulatedRigid) :
    """ Build a (sub-)scene based on SceneArticulatedRigid, add solids with skinning using the defined armature
    [tag] armature are simulated as RigidBody and used as bones for skinning
    """

    def __init__(self, parentNode, model):
        SceneArticulatedRigid.__init__(self, parentNode, model)
        self.deformables = dict()
        self.skinningArmatureBoneIndexById = dict() # keep track of bone armature index in the armature merge node

    def createScene(self):

        self.model.setTagFromTag("armature", "rigid")

        SceneArticulatedRigid.createScene(self)

        if "armature" in self.model.solidsByTag:
            # insert node containing all bones of the armature
            self.nodes["armature"] = self.insertMergeRigid(mergeNodeName="armature", tag="armature", rigidIndexById=self.skinningArmatureBoneIndexById)
            for solidModel in self.model.solids.values():
                print solidModel.name, len(solidModel.skinnings)
                if len(solidModel.skinnings)>0: # ignore solid if it has no skinning
                    # for each mesh create a Flexible.API.Deformable
                    for mesh in solidModel.mesh:
                        # take care only of visual meshes with skinning
                        if solidModel.meshAttributes[mesh.id].visual:
                            deformable = Flexible.API.Deformable(self.nodes["armature"], solidModel.name+"_"+mesh.name)
                            deformable.loadMesh(mesh.source)
                            deformable.addMechanicalObject()
                            (indices, weights) = Flexible.sml.getSolidSkinningIndicesAndWeights(solidModel, self.skinningArmatureBoneIndexById)
                            deformable.addSkinning(self.nodes["armature"], indices.values(), weights.values())
                            deformable.addVisual()
                            self.deformables[mesh.id] = deformable

#                        self.deformables[solidModel.id]=Flexible.API.Deformable(self.nodes["armature"], solidModel.name)
#                    self.deformables[solidModel.id]=Flexible.sml.insertDeformableWithSkinning(self.node, solidModel, self.nodes["armature"].getPathName(), bonesId)

<|endoftext|>


<commit_before>#!/usr/bin/pyton

#    Copyright 2011 Lockheed Martin

'''
Created on Mar 8, 2011

Base model class for learning registry data model

@author: jpoyau
'''

from base_model import uuid4, createBaseModel, ModelParser, defaultCouchServer, \
appConfig, couchdb
from pylons import *
from lr.lib import helpers as h
import datetime, logging
import pprint
log = logging.getLogger(__name__)

SPEC_RESOURCE_DATA = appConfig['spec.models.resource_data']
DB_RESOURCE_DATA = appConfig['couchdb.db.resourcedata']

_DESIGN_DOC = '_design/'
_DESIGN_DOC_FILTERS = 'filters'
_DEFAULT_FILTER = 'defaultFilter'
# Javascript filter function code that will be added the design document of the
# resource_data database. This function produce the same result that on
# pylons filter function does for consistency.
filterFunction = """
    function (doc, req)
    {
        design_doc = "_design/";

        // Don't send the design document.
        if ( !doc || (doc.doc_ID && doc.doc_ID.indexOf(design_doc) != -1) ||
            (!doc.doc_type || doc.doc_type != 'resource_data'))
         {
            return false;
        }
        // If there is no query parameters return send the document.
        if (!req){
            return true;
        }
        // Don't do anything if dealing with custom filter

        // Check to see the query parameter is valid  node filter description. if not
        // we can filter anything out so send it.
        if(("custom_filter" in req) && req.custom_filter == true){
            return true
        }
        // If there is no filter in the parameter just send the document.
        if (!req.filter){
            return true;
        }
        match = false;
        for(i in req.filter){
            filter_key = req.filter[i].filter_key;
            filter_value = req.filter[i].filter_value;
            doc_value = null
            regex_key = new RegExp(filter_key)

            for (var k in doc){
                if (filter_key.match(regex_key)){
                    doc_value = doc[filter_key];
                    break;
                }
            }

            if (!filter_key || !filter_value || !doc_value){
                    continue;
            }
            regex_value = new RegExp(filter_value);
            if (JSON.stringify(doc[filter_key]).match(regex_value)){
                match = true;
                break;
            }
        }
        // If include_exclude  is T if the filters describe what documents to accept
        // all others are rejected F if the filters describe what documents to reject
        // all others are accepted optional, T if not present
        if(!('include_exclude' in req) || req.include_exclude ){
            if(match){
                return true;
            }
            else{return false;}
        }
        if(match){
            return false;
        }
        else{return true;}
    }
"""

def updateDesignFilters(db, filtersUpdate):
    if db is None or isinstance(db, couchdb.Database) == False or \
      filtersUpdate is None or isinstance(filtersUpdate, dict) ==False:
          log.debug("Cannot update design document for db: '"+str(db)+
                            "' with update '"+str(filtersUpdate))
          return
    designId = _DESIGN_DOC + db.name
    designUpdate = {}
    designDoc = db.get(designId)

    if designDoc is not  None and designDoc:
        designUpdate.update(designDoc)
        #Make sure the filter is different before trying the update it.
        if (_DESIGN_DOC_FILTERS in designDoc and
            designDoc[_DESIGN_DOC_FILTERS][_DEFAULT_FILTER] != filterFunction):
            designUpdate[_DESIGN_DOC_FILTERS].update(filtersUpdate)
    else:
        designUpdate[_DESIGN_DOC_FILTERS]=filtersUpdate

    db[designId] = designUpdate

BaseModel = createBaseModel(SPEC_RESOURCE_DATA, DB_RESOURCE_DATA)
class ResourceDataModel(BaseModel):
    _TIME_STAMPS = ['create_timestamp', 'update_timestamp', 'node_timestamp']
    _DOC_ID = 'doc_ID'

   #Make the filter is updated in the design document.
    # Add Filter function the design document that will be used to filter on replication.
    designFilter = {_DEFAULT_FILTER: filterFunction}
    updateDesignFilters(BaseModel._defaultDB, designFilter)
    def __init__(self,  data=None):

        super(ResourceDataModel, self).__init__(data)

        # Set the timestamps by default on creation use utc.
        timeStamp = h. nowToISO8601Zformat()

        # Set the timestap on creation if they are not set.
        for stamp in self._TIME_STAMPS:
            if stamp not in self._specData.keys():
                self.__setattr__(stamp, timeStamp)

        # Set the doc is none is provided.
        if self._DOC_ID not in self._specData.keys():
            doc_id = uuid4().hex
            self.__setattr__(self._DOC_ID, doc_id)

    def _postValidation(self):

    def save(self, doc_id=None, db=None):

        # Remove update_time and node_time field
        # that should not really be part of the document.  They change base
        # on the node So that would mess up replication
        node_timestamp = self._specData['node_timestamp']
        update_timestamp = self._specData['update_timestamp']
        del self._specData['node_timestamp']
        del self._specData['update_timestamp']
        log.info(pprint.pformat(self.__dict__))
        try:
            result = BaseModel.save(self, self.doc_ID, db)
        except Exception as ex:
            log.exception(ex)
        finally:
            self._specData['node_timestamp'] =  node_timestamp
            self._specData['update_timestamp'] =  update_timestamp
        return result

<commit_msg>Fix some bugs.<commit_after>#!/usr/bin/pyton

#    Copyright 2011 Lockheed Martin

'''
Created on Mar 8, 2011

Base model class for learning registry data model

@author: jpoyau
'''

from base_model import uuid4, createBaseModel, ModelParser, defaultCouchServer, \
appConfig, couchdb
from pylons import *
from lr.lib import helpers as h
import datetime, logging
import pprint
log = logging.getLogger(__name__)

SPEC_RESOURCE_DATA = appConfig['spec.models.resource_data']
DB_RESOURCE_DATA = appConfig['couchdb.db.resourcedata']

_DESIGN_DOC = '_design/'
_DESIGN_DOC_FILTERS = 'filters'
_DEFAULT_FILTER = 'defaultFilter'
# Javascript filter function code that will be added the design document of the
# resource_data database. This function produce the same result that on
# pylons filter function does for consistency.
filterFunction = """
    function (doc, req)
    {
        design_doc = "_design/";

        // Don't send the design document.
        if ( !doc || (doc.doc_ID && doc.doc_ID.indexOf(design_doc) != -1) ||
            (!doc.doc_type || doc.doc_type != 'resource_data'))
         {
            return false;
        }
        // If there is no query parameters return send the document.
        if (!req){
            return true;
        }
        // Don't do anything if dealing with custom filter

        // Check to see the query parameter is valid  node filter description. if not
        // we can filter anything out so send it.
        if(("custom_filter" in req) && req.custom_filter == true){
            return true
        }
        // If there is no filter in the parameter just send the document.
        if (!req.filter){
            return true;
        }
        match = false;
        for(i in req.filter){
            filter_key = req.filter[i].filter_key;
            filter_value = req.filter[i].filter_value;
            doc_value = null
            regex_key = new RegExp(filter_key)

            for (var k in doc){
                if (filter_key.match(regex_key)){
                    doc_value = doc[filter_key];
                    break;
                }
            }

            if (!filter_key || !filter_value || !doc_value){
                    continue;
            }
            regex_value = new RegExp(filter_value);
            if (JSON.stringify(doc[filter_key]).match(regex_value)){
                match = true;
                break;
            }
        }
        // If include_exclude  is T if the filters describe what documents to accept
        // all others are rejected F if the filters describe what documents to reject
        // all others are accepted optional, T if not present
        if(!('include_exclude' in req) || req.include_exclude ){
            if(match){
                return true;
            }
            else{return false;}
        }
        if(match){
            return false;
        }
        else{return true;}
    }
"""

def updateDesignFilters(db, filtersUpdate):
    if db is None or isinstance(db, couchdb.Database) == False or \
      filtersUpdate is None or isinstance(filtersUpdate, dict) ==False:
          log.debug("Cannot update design document for db: '"+str(db)+
                            "' with update '"+str(filtersUpdate))
          return
    designId = _DESIGN_DOC + db.name
    designUpdate = {}
    designDoc = db.get(designId)

    if designDoc is not  None and designDoc:
        designUpdate.update(designDoc)
        #Make sure the filter is different before trying the update it.
        if (_DESIGN_DOC_FILTERS in designDoc and
            designDoc[_DESIGN_DOC_FILTERS][_DEFAULT_FILTER] != filterFunction):
            designUpdate[_DESIGN_DOC_FILTERS].update(filtersUpdate)
    else:
        designUpdate[_DESIGN_DOC_FILTERS]=filtersUpdate

    db[designId] = designUpdate

BaseModel = createBaseModel(SPEC_RESOURCE_DATA, DB_RESOURCE_DATA)
class ResourceDataModel(BaseModel):
    _TIME_STAMPS = ['create_timestamp', 'update_timestamp', 'node_timestamp']
    _DOC_ID = 'doc_ID'

   #Make the filter is updated in the design document.
    # Add Filter function the design document that will be used to filter on replication.
    designFilter = {_DEFAULT_FILTER: filterFunction}
    updateDesignFilters(BaseModel._defaultDB, designFilter)
    def __init__(self,  data=None):

        super(ResourceDataModel, self).__init__(data)

        # Set the timestamps by default on creation use utc.
        timeStamp = h. nowToISO8601Zformat()

        # Set the timestap on creation if they are not set.
        for stamp in self._TIME_STAMPS:
            if stamp not in self._specData.keys():
                self.__setattr__(stamp, timeStamp)

        # Set the doc is none is provided.
        if self._DOC_ID not in self._specData.keys():
            doc_id = uuid4().hex
            self.__setattr__(self._DOC_ID, doc_id)
    def _preValidation(self):
        """Set the node and update  timestamp if there are not set to pas validation.
        """
        if 'node_timestamp' not in self._specData.keys():
            self._specData['node_timestamp'] = self.create_timestamp
        if 'update_timestamp' not in self._specData.keys():
            self._specData['update_timestamp'] = self.create_timestamp

    def _postValidation(self):
        """Use the post validation to remove update_time and node_time field
        that should not really be part of the document.  They change base on the node
        so that would mess up replication"""
        del self._specData['node_timestamp']
        del self._specData['update_timestamp']

    def save(self, doc_id=None, db=None):
        return BaseModel.save(self, self.doc_ID, db)

<|endoftext|>


<commit_before>import logging

from flask_login import current_user

from app import app, Roles
from app.repository import role_repository

_logger = logging.getLogger(__name__)

def user_has_role(user, *roles):
    return all(role in user.roles for role in roles)

def find_all_roles():
    return [(choice, choice.name) for choice in Roles]

def find_all_roles_by_group_id(group_id):
    return role_repository.find_all_roles_by_group_id(group_id)

@app.before_request
def load_user_roles():
    current_user.roles = role_repository.load_user_roles(current_user.id)

def set_roles_for_group(group_id, new_roles):
    """Update a groups roles, uses set theory to determine what changed."""
    current_roles = set(find_all_roles_by_group_id(group_id))
    new_roles = set(new_roles)

    removed_roles = current_roles - new_roles
    added_roles = new_roles - current_roles

    if removed_roles:
        role_repository.delete_roles_by_group(group_id, removed_roles)
    if added_roles:
        role_repository.insert_roles_by_group(group_id, added_roles)
<commit_msg>do not process user roles on static requests<commit_after>import logging

from flask_login import current_user

from app import app, Roles
from app.repository import role_repository
from flask import request

_logger = logging.getLogger(__name__)

def user_has_role(user, *roles):
    return all(role in user.roles for role in roles)

def find_all_roles():
    return [(choice, choice.name) for choice in Roles]

def find_all_roles_by_group_id(group_id):
    return role_repository.find_all_roles_by_group_id(group_id)

@app.before_request
def load_user_roles():
    if request.endpoint and not request.endpoint.startswith('static'):
        current_user.roles = role_repository.load_user_roles(current_user.id)

def set_roles_for_group(group_id, new_roles):
    """Update a groups roles, uses set theory to determine what changed."""
    current_roles = set(find_all_roles_by_group_id(group_id))
    new_roles = set(new_roles)

    removed_roles = current_roles - new_roles
    added_roles = new_roles - current_roles

    if removed_roles:
        role_repository.delete_roles_by_group(group_id, removed_roles)
    if added_roles:
        role_repository.insert_roles_by_group(group_id, added_roles)
<|endoftext|>


<commit_before>import weakref
from fontParts.base.errors import FontPartsError
from fontParts.base.base import BaseDict, dynamicProperty
from fontParts.base import normalizers

class BaseGroups(BaseDict):

    """
    A Groups object. This object normally created as part of a :class:`BaseFont`. An
    orphan Groups object can be created like this::

        >>> groups = RGroups()

    This object behaves like a Python dictionary. Most of the dictionary functionality
    comes from :class:`BaseDict`, look at that object for the required environment
    implementation details.

    Groups uses :func:`normalizers.normalizeGroupKey` to normalize the key of the
    ``dict``, and :func:`normalizers.normalizeGroupValue` to normalize the the value
    of the ``dict``.
    """

    keyNormalizer = normalizers.normalizeGroupKey
    valueNormalizer = normalizers.normalizeGroupValue

    # -------
    # Parents
    # -------

    def getParent(self):
        """
        Return the Groups' parent :class:`fontParts.base.BaseFont`.

        This is a backwards compatibility method.
        """
        return self.font

    # Font

    _font = None

    font = dynamicProperty("font", "The Groups' parent :class:`BaseFont`.")

    def _get_font(self):
        if self._font is None:
            return None
        return self._font()

    def _set_font(self, font):
        assert self._font is None or self._font() == font
        if font is not None:
            font = weakref.ref(font)
        self._font = font

    # ---------
    # Searching
    # ---------

    def findGlyph(self, glyphName):
        """
        Returns a ``list`` of the group or groups associated with **glyphName**.
        **glyphName** will be an :ref:`type-string`. If no group is found to contain
        **glyphName** an empty ``list`` will be returned. ::

            >>> font.groups.findGlyph("A")
            ["A_accented"]
        """
        glyphName = normalizers.normalizeGlyphName(glyphName)
        keys = self._findGlyph(glyphName)
        keys = [self.keyNormalizer(key) for key in keys]
        return keys

    def _findGlyph(self, glyphName):
        """
        This is the environment implementation of
        :meth:`BaseGroups.findGlyph`. **glyphName** will be
        an :ref:`type-string`.

        Subclasses may override this method.
        """
        found = []
        for key, groupList in self.items():
            if glyphName in groupList:
                found.append(key)
        return found

    # ---------------------
    # RoboFab Compatibility
    # ---------------------

    def remove(self, groupName):
        """
        Removes a group from the Groups. **groupName** will be
        a :ref:`type-string` that is the group name to
        be removed.

        This is a backwards compatibility method.
        """
        del self[groupName]

    def asDict(self):
        """
        Return the Groups as a ``dict``.

        This is a backwards compatibility method.
        """
        d = {}
        for k, v in self.items():
            d[k] = v
        return d

    # -------------------
    # Inherited Functions
    # -------------------

    def __contains__(self, groupName):
        """
        Tests to see if a group name is in the Groups.
        **groupName** will be a :ref:`type-string`.
        This returns a ``bool`` indicating if the **groupName**
        is in the Groups. ::

            >>> "myGroup" in font.groups
            True
        """
        return super(BaseGroups, self).__contains__(groupName)

    def __delitem__(self, groupName):
        """
        Removes **groupName** from the Groups. **groupName** is a :ref:`type-string`.::

            >>> del font.groups["myGroup"]
        """
        super(BaseGroups, self).__delitem__(groupName)

    def __getitem__(self, groupName):
        """
        Returns the contents of the named group. **groupName** is a :ref:`type-string`.
        The returned value will be a ``list`` of the group contents.::

            >>> font.groups["myGroup"]
            ["A", "B", "C"]

        It is important to understand that any changes to the returned group contents
        will not be reflected in the Groups object. If one wants to make a change to
        the group contents, one should do the following::

            >>> group = font.groups["myGroup"]
            >>> group.remove("A")
            >>> font.groups["myGroup"] = group
        """
        return super(BaseGroups, self).__getitem__(groupName)

    def __iter__(self):
        """
        Iterates through the Groups, giving the key for each iteration. The order that
        the Groups will iterate though is not fixed nor is it ordered.::

            >>> for groupName in font.groups:
            >>>     print groupName
            "myGroup"
            "myGroup3"
            "myGroup2"
        """
        return super(BaseGroups, self).__iter__()

    def __len__(self):
        """
        Returns the number of groups in Groups as an ``int``.::

            >>> len(font.groups)
            5
        """
        return super(BaseGroups, self).__len__()

    def __setitem__(self, groupName, glyphNames):
        """
        Sets the **groupName** to the list of **glyphNames**. **groupName** is the
        group name as a :ref:`type-string` and **glyphNames** is a ``list`` of glyph
        names as :ref:`type-string`.

            >>> font.groups["myGroup"] = ["A", "B", "C"]
        """
        super(BaseGroups, self).__setitem__(groupName, glyphNames)

    def clear(self):
        """
        Removes all group information from Groups,
        resetting the Groups to an empty dictionary. ::

            >>> font.groups.clear()
        """
        super(BaseGroups, self).clear()

    def get(self, groupName, default=None):
        """
        Returns the contents of the named group.
        **groupName** is a :ref:`type-string`, and the returned values will either
        be ``list`` of group contents or ``None`` if no group was found. ::

            >>> font.groups["myGroup"]
            ["A", "B", "C"]

        It is important to understand that any changes to the returned group contents
        will not be reflected in the Groups object. If one wants to make a change to
        the group contents, one should do the following::

            >>> group = font.groups["myGroup"]
            >>> group.remove("A")
            >>> font.groups["myGroup"] = group
        """
        return super(BaseGroups, self).get(groupName, default)

    def items(self):
        """
        Returns a list of ``tuple`` of each group name and group members. Group names are
        :ref:`type-string` and group members are a ``list`` of :ref:`type-string`.
        The intial list will be unordered.

            >>> font.groups.items()
            [("myGroup", ["A", "B", "C"]), ("myGroup2", ["D", "E", "F"])]
        """
        return super(BaseGroups, self).items()

    def keys(self):
        """
        Returns a ``list`` of all the group names in Groups. This list will be
        unordered.::

            >>> font.groups.keys()
            ["myGroup4", "myGroup1", "myGroup5"]
        """
        return super(BaseGroups, self).keys()

    def pop(self, groupName, default=None):
        """
        Removes the **groupName** from the Groups and returns the ``list`` of group
        members. If no group is found, **default** is returned. **groupName** is a
        :ref:`type-string`. This must return either **default** or a ``list`` of
        glyph names as :ref:`type-string`.

            >>> font.groups.pop("myGroup")
            ["A", "B", "C"]
        """
        return super(BaseGroups, self).pop(groupName, default)

    def update(self, otherGroups):
        """
        Updates the Groups based on **otherGroups**. *otherGroups** is a ``dict`` of
        groups information. If a group from **otherGroups** is in Groups, the group
        members will be replaced by the group members from **otherGroups**. If a group
        from **otherGroups** is not in the Groups, it is added to the groups. If Groups
        contain a group name that is not in *otherGroups**, it is not changed.

            >>> font.groups.update(newGroups)
        """
        super(BaseGroups, self).update(otherGroups)

    def values(self):
        """
        Returns a ``list`` of each named group's members. This will be a list of lists,
        the group members will be a ``list`` of :ref:`type-string`. The intial list will
        be unordered.

            >>> font.groups.items()
            [["A", "B", "C"], ["D", "E", "F"]]
        """
        return super(BaseGroups, self).values()
<commit_msg>keyNormalizer missing .__func__ call<commit_after>import weakref
from fontParts.base.errors import FontPartsError
from fontParts.base.base import BaseDict, dynamicProperty
from fontParts.base import normalizers

class BaseGroups(BaseDict):

    """
    A Groups object. This object normally created as part of a :class:`BaseFont`. An
    orphan Groups object can be created like this::

        >>> groups = RGroups()

    This object behaves like a Python dictionary. Most of the dictionary functionality
    comes from :class:`BaseDict`, look at that object for the required environment
    implementation details.

    Groups uses :func:`normalizers.normalizeGroupKey` to normalize the key of the
    ``dict``, and :func:`normalizers.normalizeGroupValue` to normalize the the value
    of the ``dict``.
    """

    keyNormalizer = normalizers.normalizeGroupKey
    valueNormalizer = normalizers.normalizeGroupValue

    # -------
    # Parents
    # -------

    def getParent(self):
        """
        Return the Groups' parent :class:`fontParts.base.BaseFont`.

        This is a backwards compatibility method.
        """
        return self.font

    # Font

    _font = None

    font = dynamicProperty("font", "The Groups' parent :class:`BaseFont`.")

    def _get_font(self):
        if self._font is None:
            return None
        return self._font()

    def _set_font(self, font):
        assert self._font is None or self._font() == font
        if font is not None:
            font = weakref.ref(font)
        self._font = font

    # ---------
    # Searching
    # ---------

    def findGlyph(self, glyphName):
        """
        Returns a ``list`` of the group or groups associated with **glyphName**.
        **glyphName** will be an :ref:`type-string`. If no group is found to contain
        **glyphName** an empty ``list`` will be returned. ::

            >>> font.groups.findGlyph("A")
            ["A_accented"]
        """
        glyphName = normalizers.normalizeGlyphName(glyphName)
        groupNames = self._findGlyph(glyphName)
        groupNames = [self.keyNormalizer.__func__(groupName) for groupName in groupNames]
        return groupNames

    def _findGlyph(self, glyphName):
        """
        This is the environment implementation of
        :meth:`BaseGroups.findGlyph`. **glyphName** will be
        an :ref:`type-string`.

        Subclasses may override this method.
        """
        found = []
        for key, groupList in self.items():
            if glyphName in groupList:
                found.append(key)
        return found

    # ---------------------
    # RoboFab Compatibility
    # ---------------------

    def remove(self, groupName):
        """
        Removes a group from the Groups. **groupName** will be
        a :ref:`type-string` that is the group name to
        be removed.

        This is a backwards compatibility method.
        """
        del self[groupName]

    def asDict(self):
        """
        Return the Groups as a ``dict``.

        This is a backwards compatibility method.
        """
        d = {}
        for k, v in self.items():
            d[k] = v
        return d

    # -------------------
    # Inherited Functions
    # -------------------

    def __contains__(self, groupName):
        """
        Tests to see if a group name is in the Groups.
        **groupName** will be a :ref:`type-string`.
        This returns a ``bool`` indicating if the **groupName**
        is in the Groups. ::

            >>> "myGroup" in font.groups
            True
        """
        return super(BaseGroups, self).__contains__(groupName)

    def __delitem__(self, groupName):
        """
        Removes **groupName** from the Groups. **groupName** is a :ref:`type-string`.::

            >>> del font.groups["myGroup"]
        """
        super(BaseGroups, self).__delitem__(groupName)

    def __getitem__(self, groupName):
        """
        Returns the contents of the named group. **groupName** is a :ref:`type-string`.
        The returned value will be a ``list`` of the group contents.::

            >>> font.groups["myGroup"]
            ["A", "B", "C"]

        It is important to understand that any changes to the returned group contents
        will not be reflected in the Groups object. If one wants to make a change to
        the group contents, one should do the following::

            >>> group = font.groups["myGroup"]
            >>> group.remove("A")
            >>> font.groups["myGroup"] = group
        """
        return super(BaseGroups, self).__getitem__(groupName)

    def __iter__(self):
        """
        Iterates through the Groups, giving the key for each iteration. The order that
        the Groups will iterate though is not fixed nor is it ordered.::

            >>> for groupName in font.groups:
            >>>     print groupName
            "myGroup"
            "myGroup3"
            "myGroup2"
        """
        return super(BaseGroups, self).__iter__()

    def __len__(self):
        """
        Returns the number of groups in Groups as an ``int``.::

            >>> len(font.groups)
            5
        """
        return super(BaseGroups, self).__len__()

    def __setitem__(self, groupName, glyphNames):
        """
        Sets the **groupName** to the list of **glyphNames**. **groupName** is the
        group name as a :ref:`type-string` and **glyphNames** is a ``list`` of glyph
        names as :ref:`type-string`.

            >>> font.groups["myGroup"] = ["A", "B", "C"]
        """
        super(BaseGroups, self).__setitem__(groupName, glyphNames)

    def clear(self):
        """
        Removes all group information from Groups,
        resetting the Groups to an empty dictionary. ::

            >>> font.groups.clear()
        """
        super(BaseGroups, self).clear()

    def get(self, groupName, default=None):
        """
        Returns the contents of the named group.
        **groupName** is a :ref:`type-string`, and the returned values will either
        be ``list`` of group contents or ``None`` if no group was found. ::

            >>> font.groups["myGroup"]
            ["A", "B", "C"]

        It is important to understand that any changes to the returned group contents
        will not be reflected in the Groups object. If one wants to make a change to
        the group contents, one should do the following::

            >>> group = font.groups["myGroup"]
            >>> group.remove("A")
            >>> font.groups["myGroup"] = group
        """
        return super(BaseGroups, self).get(groupName, default)

    def items(self):
        """
        Returns a list of ``tuple`` of each group name and group members. Group names are
        :ref:`type-string` and group members are a ``list`` of :ref:`type-string`.
        The intial list will be unordered.

            >>> font.groups.items()
            [("myGroup", ["A", "B", "C"]), ("myGroup2", ["D", "E", "F"])]
        """
        return super(BaseGroups, self).items()

    def keys(self):
        """
        Returns a ``list`` of all the group names in Groups. This list will be
        unordered.::

            >>> font.groups.keys()
            ["myGroup4", "myGroup1", "myGroup5"]
        """
        return super(BaseGroups, self).keys()

    def pop(self, groupName, default=None):
        """
        Removes the **groupName** from the Groups and returns the ``list`` of group
        members. If no group is found, **default** is returned. **groupName** is a
        :ref:`type-string`. This must return either **default** or a ``list`` of
        glyph names as :ref:`type-string`.

            >>> font.groups.pop("myGroup")
            ["A", "B", "C"]
        """
        return super(BaseGroups, self).pop(groupName, default)

    def update(self, otherGroups):
        """
        Updates the Groups based on **otherGroups**. *otherGroups** is a ``dict`` of
        groups information. If a group from **otherGroups** is in Groups, the group
        members will be replaced by the group members from **otherGroups**. If a group
        from **otherGroups** is not in the Groups, it is added to the groups. If Groups
        contain a group name that is not in *otherGroups**, it is not changed.

            >>> font.groups.update(newGroups)
        """
        super(BaseGroups, self).update(otherGroups)

    def values(self):
        """
        Returns a ``list`` of each named group's members. This will be a list of lists,
        the group members will be a ``list`` of :ref:`type-string`. The intial list will
        be unordered.

            >>> font.groups.items()
            [["A", "B", "C"], ["D", "E", "F"]]
        """
        return super(BaseGroups, self).values()
<|endoftext|>


<commit_before>"""Visualize DesignSpaceDocument and resulting VariationModel."""

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.varLib.models import VariationModel, supportScalar
from fontTools.designspaceLib import DesignSpaceDocument
from mpl_toolkits.mplot3d import axes3d
from matplotlib import pyplot
from itertools import cycle
import math
import logging
import sys

log = logging.getLogger(__name__)

def stops(support, count=20):
	a,b,c = support

	return [a + (b - a) * i / count for i in range(count)] + \
	       [b + (c - b) * i / count for i in range(count)] + \
	       [c]

def plotLocations(locations, fig, names=None, **kwargs):

	assert len(locations[0].keys()) == 2

	if names is None:
		names = ['']

	n = len(locations)
	cols = math.ceil(n**.5)
	rows = math.ceil(n / cols)

	model = VariationModel(locations)
	names = [names[model.reverseMapping[i]] for i in range(len(names))]

	ax1, ax2 = sorted(locations[0].keys())
	for i, (support,color, name) in enumerate(zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))):

		axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d')
		axis3D.set_title(name)
		axis3D.set_xlabel(ax1)
		axis3D.set_ylabel(ax2)

		Xs = support.get(ax1, (-1.,0.,+1.))
		Ys = support.get(ax2, (-1.,0.,+1.))
		for x in stops(Xs):
			X, Y, Z = [], [], []
			for y in Ys:
				z = supportScalar({ax1:x, ax2:y}, support)
				X.append(x)
				Y.append(y)
				Z.append(z)
			axis3D.plot_wireframe(X, Y, Z, color=color, **kwargs)
		for y in stops(Ys):
			X, Y, Z = [], [], []
			for x in Xs:
				z = supportScalar({ax1:x, ax2:y}, support)
				X.append(x)
				Y.append(y)
				Z.append(z)
			axis3D.plot_wireframe(X, Y, Z, color=color, **kwargs)

def plotDocument(doc, fig, **kwargs):
	doc.normalize()
	locations = [s.location for s in doc.sources]
	names = [s.name for s in doc.sources]
	plotLocations(locations, fig, names, **kwargs)

def main(args=None):
	from fontTools import configLogger

	if args is None:
		args = sys.argv[1:]

	# configure the library logger (for >= WARNING)
	configLogger()
	# comment this out to enable debug messages from logger
	# log.setLevel(logging.DEBUG)

	if len(args) < 1:
		print("usage: fonttools varLib.plot source.designspace", file=sys.stderr)
		print("  or")
		print("usage: fonttools varLib.plot location1 location2 ...", file=sys.stderr)
		sys.exit(1)

	fig = pyplot.figure()

	if len(args) == 1 and args[0].endswith('.designspace'):
		doc = DesignSpaceDocument()
		doc.read(args[0])
		plotDocument(doc, fig)
	else:
		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
		plotLocations(locs, fig)

	pyplot.show()

if __name__ == '__main__':
	import sys
	sys.exit(main())
<commit_msg>[varLib.plot] Set axis limits to -1,+1<commit_after>"""Visualize DesignSpaceDocument and resulting VariationModel."""

from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.varLib.models import VariationModel, supportScalar
from fontTools.designspaceLib import DesignSpaceDocument
from mpl_toolkits.mplot3d import axes3d
from matplotlib import pyplot
from itertools import cycle
import math
import logging
import sys

log = logging.getLogger(__name__)

def stops(support, count=20):
	a,b,c = support

	return [a + (b - a) * i / count for i in range(count)] + \
	       [b + (c - b) * i / count for i in range(count)] + \
	       [c]

def plotLocations(locations, fig, names=None, **kwargs):

	assert len(locations[0].keys()) == 2

	if names is None:
		names = ['']

	n = len(locations)
	cols = math.ceil(n**.5)
	rows = math.ceil(n / cols)

	model = VariationModel(locations)
	names = [names[model.reverseMapping[i]] for i in range(len(names))]

	ax1, ax2 = sorted(locations[0].keys())
	for i, (support,color, name) in enumerate(zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))):

		axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d')
		axis3D.set_title(name)
		axis3D.set_xlabel(ax1)
		axis3D.set_ylabel(ax2)
		pyplot.xlim(-1.,+1.)
		pyplot.ylim(-1.,+1.)

		Xs = support.get(ax1, (-1.,0.,+1.))
		Ys = support.get(ax2, (-1.,0.,+1.))
		for x in stops(Xs):
			X, Y, Z = [], [], []
			for y in Ys:
				z = supportScalar({ax1:x, ax2:y}, support)
				X.append(x)
				Y.append(y)
				Z.append(z)
			axis3D.plot_wireframe(X, Y, Z, color=color, **kwargs)
		for y in stops(Ys):
			X, Y, Z = [], [], []
			for x in Xs:
				z = supportScalar({ax1:x, ax2:y}, support)
				X.append(x)
				Y.append(y)
				Z.append(z)
			axis3D.plot_wireframe(X, Y, Z, color=color, **kwargs)

def plotDocument(doc, fig, **kwargs):
	doc.normalize()
	locations = [s.location for s in doc.sources]
	names = [s.name for s in doc.sources]
	plotLocations(locations, fig, names, **kwargs)

def main(args=None):
	from fontTools import configLogger

	if args is None:
		args = sys.argv[1:]

	# configure the library logger (for >= WARNING)
	configLogger()
	# comment this out to enable debug messages from logger
	# log.setLevel(logging.DEBUG)

	if len(args) < 1:
		print("usage: fonttools varLib.plot source.designspace", file=sys.stderr)
		print("  or")
		print("usage: fonttools varLib.plot location1 location2 ...", file=sys.stderr)
		sys.exit(1)

	fig = pyplot.figure()

	if len(args) == 1 and args[0].endswith('.designspace'):
		doc = DesignSpaceDocument()
		doc.read(args[0])
		plotDocument(doc, fig)
	else:
		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
		plotLocations(locs, fig)

	pyplot.show()

if __name__ == '__main__':
	import sys
	sys.exit(main())
<|endoftext|>


<commit_before>from __future__ import division, print_function
import numpy as np
from collections import namedtuple
from itertools import izip
from pambox.intelligibility_models.mrsepsm import MrSepsm

class BinauralMrSepsm(MrSepsm):
    """Binaural implementation of the sEPSM model. """

    def __init__(self, fs=22050, cf=MrSepsm._default_center_cf,
                 modf=MrSepsm._default_modf,
                 downsamp_factor=10,
                 noise_floor=0.001, snr_env_limit=0.001):
        """@todo: to be defined1. """
        MrSepsm.__init__(self, fs, cf, modf, downsamp_factor, noise_floor,
                         snr_env_limit)

    def _mr_better_ear(self, res):
        """Calculate the multi-resolution better-ear SNRenv.

        Args:
            (self, res)

        Returns:

        """
        # The mr-SNRenv is not a numpy matrix but a N_s x N_m list of N_x long
        # numpy arrays, where N_s is the number of bands above threshold and N_m is
        #  the number of modulation channels, which is constant.

    def predict(self, clean, mixture, noise):
        """Predict intelligibility.

        :clean: @todo
        :mixture: @todo
        :noise: @todo
        :returns: @todo

        """
        binaural_res = [MrSepsm.predict(self, c, m, n)
                        for c, m, n in izip(clean, mixture, noise)]
        Ears = namedtuple('Ears', ['left', 'right'])
        ears_res = Ears(*binaural_res)

        # Better ear (BE)
        be_matrix = self._better_ear(binaural_res)
        be_mask = self._get_bands_above_threshold(binaural_res)
        be_snr_env = MrSepsm._optimal_combination(self, be_matrix, be_mask)

        res = namedtuple('Results', ['be_snr_env', 'ears', 'be_matrix',
                                     'be_above_thres'])
        res.be_snr_env = be_snr_env
        res.be_matrix = be_matrix
        res.be_above_thres = be_mask
        res.ears = ears_res
        return res  # Results for each ear's sEPSM model.

    def _better_ear(self, res):
        """Return the better-ear SNRenv for bands above threshold only.

        :param res: Results from Sepsm.predict()
        :type res: namedtuple
        :returns: List of 3 ndarray, for clean speech, mixture, and noise which
            contains the better ear SNRenv.

        """
        be_snr_env = np.zeros_like(res[0].snr_env_matrix)
        for ii in range(2):
            idx = res[ii].bands_above_thres_idx
            be_snr_env[idx] = \
                np.maximum(be_snr_env[idx], res[ii].snr_env_matrix[idx])
        return be_snr_env

    def _get_bands_above_threshold(self, res):
        """Return a mask for bands above threshold in at least one ear.

        :param res: Results return by the Sepsm.predict() function.
        :type res: structured array.
        :returns: ndarray of booleans -- The mask, it has the same dimensions
            as res.snr_env_matrix.

        """
        at = {}
        for ii in range(2):
            at[ii] = np.zeros((len(self.cf), len(self.modf)))
            at[ii][res[ii].bands_above_thres_idx] = 1
        all_above_thres = np.logical_or(at[0], at[1])
        return all_above_thres
<commit_msg>Use dictionary for model output.<commit_after>from __future__ import division, print_function
import numpy as np
from collections import namedtuple
from itertools import izip
from pambox.intelligibility_models.mrsepsm import MrSepsm

class BinauralMrSepsm(MrSepsm):
    """Binaural implementation of the sEPSM model. """

    def __init__(self, fs=22050, cf=MrSepsm._default_center_cf,
                 modf=MrSepsm._default_modf,
                 downsamp_factor=10,
                 noise_floor=0.001, snr_env_limit=0.001):
        """@todo: to be defined1. """
        MrSepsm.__init__(self, fs, cf, modf, downsamp_factor, noise_floor,
                         snr_env_limit)

    def _mr_better_ear(self, res):
        """Calculate the multi-resolution better-ear SNRenv.

        Args:
            (self, res)

        Returns:

        """
        # The mr-SNRenv is not a numpy matrix but a N_s x N_m list of N_x long
        # numpy arrays, where N_s is the number of bands above threshold and N_m is
        #  the number of modulation channels, which is constant.

    def predict(self, clean, mixture, noise):
        """Predict intelligibility.

        :clean: @todo
        :mixture: @todo
        :noise: @todo
        :returns: @todo

        """
        binaural_res = [MrSepsm.predict(self, c, m, n)
                        for c, m, n in izip(clean, mixture, noise)]
        Ears = namedtuple('Ears', ['left', 'right'])
        ears_res = Ears(*binaural_res)

        # Better ear (BE)
        be_matrix = self._better_ear(binaural_res)
        be_mask = self._get_bands_above_threshold(binaural_res)
        be_snr_env = MrSepsm._optimal_combination(self, be_matrix, be_mask)

        res = {
            'be_snr_env': be_snr_env,
            'be_matrix': be_matrix,
            'be_above_thres': be_mask,
            'ears': ears_res
        }
        return res  # Results for each ear's sEPSM model.

    def _better_ear(self, res):
        """Return the better-ear SNRenv for bands above threshold only.

        :param res: Results from Sepsm.predict()
        :type res: namedtuple
        :returns: List of 3 ndarray, for clean speech, mixture, and noise which
            contains the better ear SNRenv.

        """
        be_snr_env = np.zeros_like(res[0]['snr_env_matrix'])
        for ii in range(2):
            idx = res[ii]['bands_above_thres_idx']
            be_snr_env[idx] = \
                np.maximum(be_snr_env[idx], res[ii]['snr_env_matrix'][idx])
        return be_snr_env

    def _get_bands_above_threshold(self, res):
        """Return a mask for bands above threshold in at least one ear.

        :param res: Results return by the Sepsm.predict() function.
        :type res: structured array.
        :returns: ndarray of booleans -- The mask, it has the same dimensions
            as res.snr_env_matrix.

        """
        at = {}
        for ii in range(2):
            at[ii] = np.zeros((len(self.cf), len(self.modf)))
            at[ii][res[ii]['bands_above_thres_idx']] = 1
        all_above_thres = np.logical_or(at[0], at[1])
        return all_above_thres
<|endoftext|>