Source code for ddg.geometry.intersection

from collections.abc import Iterable
# Can't import Subspace and Quadric on their own due to circular imports
import ddg.geometry.quadrics as quadrics
import ddg.geometry.subspaces as subspaces

from ddg.abc import NonExact



def _resolve_intersection(intersection):
    COMBINATIONS = {
        (subspaces.Subspace,) : subspaces.meet,
        (quadrics.Quadric, subspaces.Subspace) : quadrics.intersect_quadric_subspace
    }

    #Maybe it would be a better idea to remember if we got an intersection
    #object to not built the same again if no combination was found
    if isinstance(intersection, Intersection):
        intersection = list(intersection.objects)
    if len(intersection) == 1:
        return intersection[0]

    for combination in COMBINATIONS.keys():
        intersect_fct = COMBINATIONS[combination]
        if len(combination) == 1:
            cls = combination[0]
            instances = [(idx, instance) for idx, instance in
                         enumerate(intersection) if isinstance(instance, cls)]
            instances.reverse()

            if len(instances) in (0,1):
                continue

            for idx, instance in instances:
                intersection.pop(idx)

            instances = list(zip(*instances))[1]
            intersection.append(intersect_fct(*instances))

            return _resolve_intersection(intersection)

        #Not used yet
        checked = []
        instances = ['']*len(intersection)
        used_indices = []

        contained = True

        for (clsidx, cls) in enumerate(combination):
            if cls in checked:
                idx = checked.index(cls)
                next_index = instances.index(idx)
                instances[next_index] = None
                used_indices.append(next_index)
            else:
                instances = [clsidx if (isinstance(intersection[i], cls) and
                             instances[i] != None) else
                             instances[i] for i in range(len(intersection))]
                if instances.count(clsidx) < combination.count(cls):
                    contained = False
                    break

                checked.append(cls)
                next_index = instances.index(clsidx)
                instances[next_index] = None
                used_indices.append(next_index)

        if contained == True:
            used_indices.sort()
            used_indices.reverse()
            used_objects = [intersection.pop(i) for i in used_indices]
            # sort according to the order given in the combination tuple since
            # order matters for some intersection functions
            used_objects_sorted = []
            for cls in combination:
                for object_ in used_objects:
                    if isinstance(object_, cls):
                        used_objects_sorted.append(object_)

            intersection.append(intersect_fct(*used_objects_sorted))

            return _resolve_intersection(intersection)

    return Intersection(*intersection)

def _resolve_join(join):
    COMBINATIONS = {
        (subspaces.Subspace,) : subspaces.join,
        (quadrics.Quadric, subspaces.Subspace) : quadrics.join_quadric_subspace
    }

    #Maybe it would be a better idea to remember if we got a join
    #object to not built the same again if no combination was found
    if isinstance(join, Join):
        join = list(join.objects)
    if len(join) == 1:
        return join[0]

    for combination in COMBINATIONS.keys():
        join_fct = COMBINATIONS[combination]
        if len(combination) == 1:
            cls = combination[0]
            instances = [(idx, instance) for idx, instance in
                         enumerate(join) if isinstance(instance, cls)]
            instances.reverse()

            if len(instances) in (0,1):
                continue

            for idx, instance in instances:
                join.pop(idx)

            instances = list(zip(*instances))[1]
            join.append(join_fct(*instances))

            return _resolve_join(join)

        #Not used yet
        checked = []
        instances = ['']*len(join)
        used_indices = []

        contained = True

        for (clsidx, cls) in enumerate(combination):
            if cls in checked:
                idx = checked.index(cls)
                next_index = instances.index(idx)
                instances[next_index] = None
                used_indices.append(next_index)
            else:
                instances = [clsidx if (isinstance(join[i], cls) and
                             instances[i] != None) else
                             instances[i] for i in range(len(join))]
                if instances.count(clsidx) < combination.count(cls):
                    contained = False
                    break

                checked.append(cls)
                next_index = instances.index(clsidx)
                instances[next_index] = None
                used_indices.append(next_index)

        if contained == True:
            used_indices.sort()
            used_indices.reverse()
            used_objects = [join.pop(i) for i in used_indices]

            # sort according to the order given in the combination tuple since
            # order matters for some join functions
            used_objects_sorted = []
            for cls in combination:
                for object_ in used_objects:
                    if isinstance(object_, cls):
                        used_objects_sorted.append(object_)

            join.append(join_fct(*used_objects_sorted))

            return _resolve_join(join)

    return Join(*join)

[docs]def intersect(*objects, resolve=True): """Intersection function Parameters ---------- *objects : Any Objects to intersect resolve : bool, default=False Whether or not the intersection should be resolved immediately Returns ------- ddg.geometry.intersection.Intersection or resolved Intersection Instance of Intersection containing obj1 and obj2 or the intersection of the class obtained by resolving the intersection See Also -------- join """ if resolve: return Intersection(*objects).resolve() return Intersection(*objects)
[docs]def meet(*objects, resolve=True): """Alias for intersect. See Also -------- intersect """ return intersect(*objects, resolve=resolve)
[docs]def join(*objects, resolve=True): """Join function Parameters ---------- *objects : Any Objects to join resolve : bool, default=False Whether or not the join should be resolved immediately Returns ------- ddg.geometry.intersection.Join or resolved Intersection Instance of Join containing obj1 and obj2 or the join of the class obtained by resolving the join See Also -------- intersect """ if resolve: return Join(*objects).resolve() return Join(*objects)
[docs]class Intersection(Iterable, NonExact): """Base class for Intersections. Parameters ---------- *obj : object Intersecting objects Attributes ---------- objects : list List of intersecting objects. types : set Set of types of intersecting objects. Methods ------- resolve Resolve the intersection. See Also -------- Join """ def __init__(self, *obj): self.objects = obj def __iter__(self): return self.objects.__iter__() def __contains__(self, p): return all(p in o for o in self.objects) @property def types(self): return set(type(o) for o in self.objects) @property def atol(self): tol_list = [obj.atol for obj in self.objects if hasattr(obj, 'atol')] return max(tol_list) @property def rtol(self): tol_list = [obj.rtol for obj in self.objects if hasattr(obj, 'rtol')] return max(tol_list)
[docs] def resolve(self): return _resolve_intersection(self)
[docs]class Join(Iterable): """Base class for joins. Parameters ---------- *obj : object Objects to join Attributes ---------- objects : list List of joined objects. Methods ------- resolve Resolve the join. See Also -------- Intersection """ def __init__(self, *obj): self.objects = obj def __iter__(self): return self.objects.__iter__() def __contains__(self, p): return any(p in obj for obj in self.objects)
[docs] def resolve(self): return _resolve_join(self)