def pruneContainment(scenario, verbosity): """Prune based on the requirement that individual Objects fit within their container. Specifically, if O is positioned uniformly in region B and has container C, then we can instead pick a position uniformly in their intersection. If we can also lower bound the radius of O, then we can first erode C by that distance. """ for obj in scenario.objects: base = matchInRegion(obj.position) if base is None: # match objects positioned uniformly in a Region continue basePoly = regions.toPolygon(base) if basePoly is None: # to prune, the Region must be polygonal continue container = scenario.containerOfObject(obj) containerPoly = regions.toPolygon(container) if containerPoly is None: # the object's container must also be polygonal return None minRadius, _ = supportInterval(obj.inradius) if minRadius is not None: # if we can lower bound the radius, erode the container containerPoly = containerPoly.buffer(-minRadius) elif base is container: continue newBasePoly = basePoly & containerPoly # restrict the base Region to the container if verbosity >= 1: percent = 100 * (1.0 - (newBasePoly.area / basePoly.area)) print( f' Region containment constraint pruned {percent:.1f}% of space.' ) newBase = regions.PolygonalRegion(polygon=newBasePoly, orientation=base.orientation) newPos = regions.Region.uniformPointIn(newBase) obj.position.conditionTo(newPos)
def visibilityBound(obj, target): """Upper bound the distance from an Object to another it can see.""" # Upper bound on visible distance is a sum of several terms: # 1. obj.visibleDistance _, maxVisibleDistance = supportInterval(obj.visibleDistance) if maxVisibleDistance is None: return None # 2. distance from obj's center to its camera _, maxCameraX = supportInterval(obj.cameraOffset.x) _, maxCameraY = supportInterval(obj.cameraOffset.y) if maxCameraX is None or maxCameraY is None: return None maxVisibleDistance += math.hypot(maxCameraX, maxCameraY) # 3. radius of target _, maxRadius = supportInterval(target.radius) if maxRadius is None: return None maxVisibleDistance += maxRadius return maxVisibleDistance
def maxDistanceBetween(scenario, obj, target): # TODO also infer from requirements ego = scenario.egoObject if not ((obj is ego and target.requireVisible) or (target is ego and obj.requireVisible)): return None # Upper bound on visible distance is a sum of several terms: # 1. obj.visibleDistance _, maxVisibleDistance = supportInterval(obj.visibleDistance) if maxVisibleDistance is None: return None # 2. distance from obj's center to its camera _, maxCameraX = supportInterval(obj.cameraOffset.x) _, maxCameraY = supportInterval(obj.cameraOffset.y) if maxCameraX is None or maxCameraY is None: return None maxVisibleDistance += math.hypot(maxCameraX, maxCameraY) # 3. radius of target _, maxRadius = supportInterval(target.radius) if maxRadius is None: return None maxVisibleDistance += maxRadius return maxVisibleDistance
def matchPolygonalField(heading, position): if (isMethodCall(heading, VectorField.__getitem__) and isinstance(heading.object, PolygonalVectorField) and heading.arguments == (position.toVector(), )): return heading.object, 0, 0 elif isinstance(heading, OperatorDistribution) and heading.operator in ('__add__', '__radd__'): field, lower, upper = matchPolygonalField(heading.object, position) if field is not None: assert len(heading.operands) == 1 offset = heading.operands[0] ol, oh = supportInterval(offset) if ol is not None and oh is not None: return field, lower + ol, upper + oh return None, 0, 0
def matchPolygonalField(heading, position): """Match headings defined by a PolygonalVectorField at the given position. Matches headings exactly equal to a PolygonalVectorField, or offset by a bounded disturbance. Returns a triplet consisting of the matched field if any, together with lower/upper bounds on the disturbance. """ if (isMethodCall(heading, VectorField.__getitem__) and isinstance(heading.object, PolygonalVectorField) and heading.arguments == (position.toVector(),)): return heading.object, 0, 0 elif isinstance(heading, OperatorDistribution) and heading.operator in ('__add__', '__radd__'): field, lower, upper = matchPolygonalField(heading.object, position) if field is not None: assert len(heading.operands) == 1 offset = heading.operands[0] ol, oh = supportInterval(offset) if ol is not None and oh is not None: return field, lower + ol, upper + oh return None, 0, 0