Processing a Square Dance Call

Every square dance call is represented by an instance of a subtype of SquareDanceCall. Every instance has a rolefield (or a method on [restricted_to](@ref)) which can be used to restrict the call to only some dancers, for exampleOriginalHeadsorCenters. Some calls might have additional fields that inform a handedness (e.g. [PullBy](@ref)) or a count ([SquareThru`](@ref)).

The function do_call is the entry point for performing a square dance call. It is passed the current knowledge base (a SDRKnowledgeBase) and the call to be performed. The knowledge base already knows the current location and facing direction of each dancer, and has concluded all of the formations they are in.

do_call creates a CallSchedule and schedules the call to that schedule. do_call then calls do_schedule, passing it the schedule and the knowledge base.

do_schedule dequeues calls from the schedule. It calls [get_call_options] to identify formations in the knowledge base that match the call's role and the result of calling can_do_from. do_schedule calls expand_parts with the call and the appropriate formation. If there is an expansion, those parts are placed in the schedule. Otherwise perform is called to perform the call.

There is a method of can_do_from for each combination of square dance call and formation. can_do_from returns a preference number that imposes a preference order on each combination of call and formation. A preference value of 0 indicates that the call can not be performed from that formation (or that it is not yet implemented). A positive preference value is returned if the call can be performed from that formation. A higher preference value is preferred over a lower one. For example, UTurnBack from Couple is preferable to UTurnBack from a single dancer (represented as a DancerState) because Couple further informs correct turning direction. A call like AndRoll need only be implemented for a single dancer (DancerState) because the dancer's rotational flow can be determined from there (via the previous field chain).

get_call_options is pretty complicated. Consider a right handed tidal wave (RHWaveOfEight). it includes 7 two dancer formations: four RHMiniWaves and three LHMiniWaves. If there is only one CanDoCall concerning a dancer after the preference sorting, then that CanDoCall will be included in the results of get_call_options. Any CanDoCall whose formation includes a dancer that is already included in the result set is discarded. get_call_options loops over these two operations: adding to the result set and eliminating overlaps, until all CanDoCall objects have been considered. get_call_options finally returns those results – a vector of CanDoCall objects that concern disjoint formations.

Once do_schedule has processed all of the schedule entries for a given time value, it calls `breatheto spread dancers apart if any overlap.doschedulethen makes sure the dancers are all synchronized and updates the knowledge base (actually, it creates and populates a new one because the Rete package doesn't support retraction). If the schedule is not yet empty thendoschedule` continues as above.