Exemple #1
0
def Facing(heading):
	if isinstance(heading, VectorField):
		return Specifier('heading', DelayedArgument({'position'},
		                                            lambda self: heading[self.position]))
	else:
		heading = toHeading(heading, 'specifier "facing X" with X not a heading or vector field')
		return Specifier('heading', heading)
Exemple #2
0
def Facing(heading):
	"""The 'facing X' polymorphic specifier.

	Specifies 'heading', with dependencies depending on the form:
		facing <number> -- no dependencies;
		facing <field> -- depends on 'position'.
	"""
	if isinstance(heading, VectorField):
		return Specifier('heading', DelayedArgument({'position'},
													lambda self: heading[self.position]))
	else:
		heading = toHeading(heading, 'specifier "facing X" with X not a heading or vector field')
		return Specifier('heading', heading)
Exemple #3
0
def ApparentlyFacing(heading, fromPt=None):
	heading = toHeading(heading, 'specifier "apparently facing X" with X not a heading')
	if fromPt is None:
		fromPt = ego()
	fromPt = toVector(fromPt, 'specifier "apparently facing X from Y" with Y not a vector')
	value = lambda self: fromPt.angleTo(self.position) + heading
	return Specifier('heading', DelayedArgument({'position'}, value))
Exemple #4
0
def OffsetBy(offset):
    """The 'offset by <vector>' specifier.

	Specifies 'position', with no dependencies.
	"""
    offset = toVector(offset, 'specifier "offset by X" with X not a vector')
    pos = RelativeTo(offset, ego()).toVector()
    return Specifier('position', pos)
Exemple #5
0
def FacingToward(pos):
	"""The 'facing toward <vector>' specifier.

	Specifies 'heading', depending on 'position'.
	"""
	pos = toVector(pos, 'specifier "facing toward X" with X not a vector')
	return Specifier('heading', DelayedArgument({'position'},
												lambda self: self.position.angleTo(pos)))
Exemple #6
0
def In(region):
	# TODO fix type checking
	#region = toType(region, Region, 'specifier "in/on R" with R not a Region')
	if isinstance(region, Distribution):
		extras = {'heading'}	# TODO fix hack!!!
	else:
		extras = {} if region.orientation is None else {'heading'}
	return Specifier('position', Region.uniformPointIn(region), optionals=extras)
Exemple #7
0
def In(region):
	"""The 'in/on <region>' specifier.

	Specifies 'position', with no dependencies. Optionally specifies 'heading'
	if the given Region has a preferred orientation.
	"""
	region = toType(region, Region, 'specifier "in/on R" with R not a Region')
	extras = {'heading'} if alwaysProvidesOrientation(region) else {}
	return Specifier('position', Region.uniformPointIn(region), optionals=extras)
Exemple #8
0
def OffsetAlongSpec(direction, offset):
    """The 'offset along X by Y' polymorphic specifier.

	Specifies 'position', with no dependencies.

	Allowed forms:
		offset along <heading> by <vector>
		offset along <field> by <vector>
	"""
    return Specifier('position', OffsetAlong(ego(), direction, offset))
Exemple #9
0
def VisibleFrom(base):
	"""The 'visible from <Point>' specifier.

	Specifies 'position', with no dependencies.

	This uses the given object's 'visibleRegion' property, and so correctly
	handles the view regions of Points, OrientedPoints, and Objects.
	"""
	if not isinstance(base, Point):
		raise RuntimeParseError('specifier "visible from O" with O not a Point')
	return Specifier('position', Region.uniformPointIn(base.visibleRegion))
Exemple #10
0
def Beyond(pos, offset, fromPt=None):
	pos = toVector(pos, 'specifier "beyond X by Y" with X not a vector')
	dType = underlyingType(offset)
	if dType is float or dType is int:
		offset = Vector(0, offset)
	elif dType is not Vector:
		raise RuntimeParseError('specifier "beyond X by Y" with Y not a number or vector')
	if fromPt is None:
		fromPt = ego()
	fromPt = toVector(fromPt, 'specifier "beyond X by Y from Z" with Z not a vector')
	lineOfSight = fromPt.angleTo(pos)
	return Specifier('position', pos.offsetRotated(lineOfSight, offset))
Exemple #11
0
def ApparentlyFacing(heading, fromPt=None):
	"""The 'apparently facing <heading> [from <vector>]' specifier.

	Specifies 'heading', depending on 'position'.

	If the 'from <vector>' is omitted, the position of ego is used.
	"""
	heading = toHeading(heading, 'specifier "apparently facing X" with X not a heading')
	if fromPt is None:
		fromPt = ego()
	fromPt = toVector(fromPt, 'specifier "apparently facing X from Y" with Y not a vector')
	value = lambda self: fromPt.angleTo(self.position) + heading
	return Specifier('heading', DelayedArgument({'position'}, value))
Exemple #12
0
def leftSpecHelper(syntax, pos, dist, axis, toComponents, makeOffset):
	extras = set()
	if canCoerce(dist, float):
		dx, dy = toComponents(coerce(dist, float))
	elif canCoerce(dist, Vector):
		dx, dy = coerce(dist, Vector)
	else:
		raise RuntimeParseError(f'"{syntax} X by D" with D not a number or vector')
	if isinstance(pos, OrientedPoint):		# TODO too strict?
		val = lambda self: pos.relativize(makeOffset(self, dx, dy))
		new = DelayedArgument({axis}, val)
		extras.add('heading')
	else:
		pos = toVector(pos, f'specifier "{syntax} X" with X not a vector')
		val = lambda self: pos.offsetRotated(self.heading, makeOffset(self, dx, dy))
		new = DelayedArgument({axis, 'heading'}, val)
	return Specifier('position', new, optionals=extras)
Exemple #13
0
def Beyond(pos, offset, fromPt=None):
	"""The 'beyond X by Y [from Z]' polymorphic specifier.

	Specifies 'position', with no dependencies.

	Allowed forms:
		beyond <vector> by <number> [from <vector>]
		beyond <vector> by <vector> [from <vector>]

	If the 'from <vector>' is omitted, the position of ego is used.
	"""
	pos = toVector(pos, 'specifier "beyond X by Y" with X not a vector')
	dType = underlyingType(offset)
	if dType is float or dType is int:
		offset = Vector(0, offset)
	elif dType is not Vector:
		raise RuntimeParseError('specifier "beyond X by Y" with Y not a number or vector')
	if fromPt is None:
		fromPt = ego()
	fromPt = toVector(fromPt, 'specifier "beyond X by Y from Z" with Z not a vector')
	lineOfSight = fromPt.angleTo(pos)
	return Specifier('position', pos.offsetRotated(lineOfSight, offset))
Exemple #14
0
def Following(field, dist, fromPt=None):
	"""The 'following F [from X] for D' specifier.

	Specifies 'position', and optionally 'heading', with no dependencies.

	Allowed forms:
		following <field> [from <vector>] for <number>

	If the 'from <vector>' is omitted, the position of ego is used.
	"""
	if fromPt is None:
		fromPt = ego()
	else:
		dist, fromPt = fromPt, dist
	if not isinstance(field, VectorField):
		raise RuntimeParseError('"following F" specifier with F not a vector field')
	fromPt = toVector(fromPt, '"following F from X for D" with X not a vector')
	dist = toScalar(dist, '"following F for D" with D not a number')
	pos = field.followFrom(fromPt, dist)
	heading = field[pos]
	val = OrientedVector.make(pos, heading)
	return Specifier('position', val, optionals={'heading'})
Exemple #15
0
def At(pos):
    """The 'at <vector>' specifier.

	Specifies 'position', with no dependencies."""
    pos = toVector(pos, 'specifier "at X" with X not a vector')
    return Specifier('position', pos)
Exemple #16
0
def With(prop, val):
    """The 'with <property> <value>' specifier.

	Specifies the given property, with no dependencies.
	"""
    return Specifier(prop, val)
Exemple #17
0
def OffsetAlongSpec(direction, offset):
	return Specifier('position', OffsetAlong(ego(), direction, offset))
Exemple #18
0
def OffsetBy(offset):
	offset = toVector(offset, 'specifier "offset by X" with X not a vector')
	pos = RelativeTo(offset, ego()).toVector()
	return Specifier('position', pos)
Exemple #19
0
    def __init__(self, *args, **kwargs):
        # Validate specifiers
        name = type(self).__name__
        specifiers = list(args)
        for prop, val in kwargs.items():
            specifiers.append(Specifier(prop, val))
        properties = dict()
        optionals = collections.defaultdict(list)
        defs = self.defaults()
        for spec in specifiers:
            if not isinstance(spec, Specifier):
                raise RuntimeParseError(
                    f'argument {spec} to {name} is not a specifier')
            prop = spec.property
            if prop in properties:
                raise RuntimeParseError(
                    f'property "{prop}" of {name} specified twice')
            properties[prop] = spec
            for opt in spec.optionals:
                if opt in defs:  # do not apply optionals for properties this object lacks
                    optionals[opt].append(spec)

        # Decide which optionals to use
        optionalsForSpec = collections.defaultdict(set)
        for opt, specs in optionals.items():
            if opt in properties:
                continue  # optionals do not override a primary specification
            if len(specs) > 1:
                raise RuntimeParseError(
                    f'property "{opt}" of {name} specified twice (optionally)')
            assert len(specs) == 1
            spec = specs[0]
            properties[opt] = spec
            optionalsForSpec[spec].add(opt)

        # Add any default specifiers needed
        for prop in defs:
            if prop not in properties:
                spec = defs[prop]
                specifiers.append(spec)
                properties[prop] = spec

        # Topologically sort specifiers
        order = []
        seen, done = set(), set()

        def dfs(spec):
            if spec in done:
                return
            elif spec in seen:
                raise RuntimeParseError(
                    f'specifier for property {spec.property} '
                    'depends on itself')
            seen.add(spec)
            for dep in spec.requiredProperties:
                child = properties.get(dep)
                if child is None:
                    raise RuntimeParseError(
                        f'property {dep} required by '
                        f'specifier {spec} is not specified')
                else:
                    dfs(child)
            order.append(spec)
            done.add(spec)

        for spec in specifiers:
            dfs(spec)
        assert len(order) == len(specifiers)

        # Evaluate and apply specifiers
        for spec in order:
            spec.applyTo(self, optionalsForSpec[spec])

        # Set up dependencies
        deps = []
        for prop in properties:
            assert hasattr(self, prop)
            val = getattr(self, prop)
            if needsSampling(val):
                deps.append(val)
        super().__init__(deps)
        self.properties = set(properties)
Exemple #20
0
 def withProperties(cls, props):
     assert all(reqProp in props for reqProp in cls.defaults())
     assert all(not needsLazyEvaluation(val) for val in props.values())
     specs = (Specifier(prop, val) for prop, val in props.items())
     return cls(*specs)
Exemple #21
0
def At(pos):
	pos = toVector(pos, 'specifier "at X" with X not a vector')
	return Specifier('position', pos)
Exemple #22
0
def VisibleFrom(base):
	if not isinstance(base, Point):
		raise RuntimeParseError('specifier "visible from O" with O not a Point')
	return Specifier('position', Region.uniformPointIn(base.visibleRegion))
Exemple #23
0
def test_pickle_object():
    spec = Specifier(
        'blob', DelayedArgument(('width', ),
                                lambda context: 2 * context.width), {'width'})
    obj = Object(spec)
    tryPickling(obj)
Exemple #24
0
def With(prop, val):
	return Specifier(prop, val)
Exemple #25
0
    def __init__(self, *args, _internal=False, **kwargs):
        if _internal:  # Object is being constructed internally; use fast path
            assert not args
            for prop, value in kwargs.items():
                assert not needsLazyEvaluation(value), (prop, value)
                object.__setattr__(self, prop, value)
            super().__init__(kwargs.values())
            self.properties = set(kwargs.keys())
            return

        # Validate specifiers
        name = self.__class__.__name__
        specifiers = list(args)
        for prop, val in kwargs.items():  # kwargs supported for internal use
            specifiers.append(Specifier(prop, val, internal=True))
        properties = dict()
        optionals = collections.defaultdict(list)
        defs = self.__class__._defaults
        for spec in specifiers:
            assert isinstance(spec, Specifier), (name, spec)
            prop = spec.property
            if prop in properties:
                raise RuntimeParseError(
                    f'property "{prop}" of {name} specified twice')
            properties[prop] = spec
            for opt in spec.optionals:
                if opt in defs:  # do not apply optionals for properties this object lacks
                    optionals[opt].append(spec)

        # Decide which optionals to use
        optionalsForSpec = collections.defaultdict(set)
        for opt, specs in optionals.items():
            if opt in properties:
                continue  # optionals do not override a primary specification
            if len(specs) > 1:
                raise RuntimeParseError(
                    f'property "{opt}" of {name} specified twice (optionally)')
            assert len(specs) == 1
            spec = specs[0]
            properties[opt] = spec
            optionalsForSpec[spec].add(opt)

        # Add any default specifiers needed
        for prop in defs:
            if prop not in properties:
                spec = defs[prop]
                specifiers.append(spec)
                properties[prop] = spec

        # Topologically sort specifiers
        order = []
        seen, done = set(), set()

        def dfs(spec):
            if spec in done:
                return
            elif spec in seen:
                raise RuntimeParseError(
                    f'specifier for property {spec.property} '
                    'depends on itself')
            seen.add(spec)
            for dep in spec.requiredProperties:
                child = properties.get(dep)
                if child is None:
                    raise RuntimeParseError(
                        f'property {dep} required by '
                        f'specifier {spec} is not specified')
                else:
                    dfs(child)
            order.append(spec)
            done.add(spec)

        for spec in specifiers:
            dfs(spec)
        assert len(order) == len(specifiers)

        # Evaluate and apply specifiers
        self.properties = set()  # will be filled by calls to _specify below
        self._evaluated = DefaultIdentityDict(
        )  # temporary cache for lazily-evaluated values
        for spec in order:
            spec.applyTo(self, optionalsForSpec[spec])
        del self._evaluated

        # Set up dependencies
        deps = []
        for prop in properties:
            assert hasattr(self, prop)
            val = getattr(self, prop)
            deps.append(val)
        super().__init__(deps)

        # Possibly register this object
        self._register()
Exemple #26
0
def FacingToward(pos):
	pos = toVector(pos, 'specifier "facing toward X" with X not a vector')
	return Specifier('heading', DelayedArgument({'position'},
	                                            lambda self: self.position.angleTo(pos)))
Exemple #27
0
 def withProperties(cls, props):
     assert all(reqProp in props for reqProp in cls.defaults())
     specs = (Specifier(prop, val) for prop, val in props.items())
     return cls(*specs)