//  Touching a dancer shows and hides its path
        public void doTouch(double x, double y)
        {
            //  Convert x and y to dance floor coords
            var range = Math.Min(ActualWidth, ActualHeight);
            var s     = range / 13.0;
            var px    = -(y - ActualHeight / 2) / s;
            var py    = -(x - ActualWidth / 2) / s;
            var pv    = Vector.Create(px, py);
            //  Compare with dancer locations
            var    bestdist = 0.5;
            Dancer bestd    = null;

            foreach (var d in dancers)
            {
                if (!d.hidden)
                {
                    var dp     = d.location;
                    var distsq = (pv - dp).LengthSquared();
                    if (distsq < bestdist)
                    {
                        bestd    = d;
                        bestdist = distsq;
                    }
                }
            }
            if (bestd != null)
            {
                bestd.showPath = !bestd.showPath;
                canvas.Invalidate();
            }
        }
Пример #2
0
 public Dancer(Dancer from) : this(from.number, from.number_couple, from.gender, from.fillColor, from.tx,
                                   GeometryMaker.makeOne(from.geom.geometry(), 0), new List <Movement>())
 {
     clonedFrom = from;
     //  For the sequencer, copy dancer data
     data.active = from.data.active;
 }
Пример #3
0
 public Handhold(Dancer dancer1, Dancer dancer2, int hold1, int hold2,
                 double angle1, double angle2, double distance, double score)
 {
     this.dancer1  = dancer1;
     this.dancer2  = dancer2;
     this.hold1    = hold1;
     this.hold2    = hold2;
     this.angle1   = angle1;
     this.angle2   = angle2;
     this.distance = distance;
     this.score    = score;
 }
Пример #4
0
        //  Return dancer that is facing the front of this dancer
        public Dancer dancerFacing(Dancer d)
        {
            var d2 = dancerInFront(d);

            if (d2 != null)
            {
                var d3 = dancerInFront(d2);
                if (d3 == d)
                {
                    return(d2);
                }
            }
            return(null);
        }
Пример #5
0
 //  Return dancer directly to the left of given dancer
 public Dancer dancerToLeft(Dancer d)
 {
     return(dancerClosest(d, isLeft(d)));
 }
Пример #6
0
 //  Return dancer directly to the right of given dancer
 public Dancer dancerToRight(Dancer d)
 {
     return(dancerClosest(d, isRight(d)));
 }
Пример #7
0
 //  Return true if this dancer is in tandem with another dancer
 public bool isInTandem(Dancer d)
 {
     return(d.data.trailer ? dancerInFront(d).data.leader :
            d.data.leader ? dancerInBack(d).data.trailer :
            false);
 }
Пример #8
0
 //  Return true if this dancer is in a wave or mini-wave
 public bool isInWave(Dancer d)
 {
     return(d.data.partner != null &&
            angle(d, d.data.partner) == angle(d.data.partner, d));
 }
Пример #9
0
 //  Return all the dancers in front, in order
 public IEnumerable <Dancer> dancersInFront(Dancer d)
 {
     return(dancersInOrder(d, isInFront(d)));
 }
Пример #10
0
 //  Return dancers that are in between two other dancers
 public IEnumerable <Dancer> inBetween(Dancer d1, Dancer d2)
 {
     return(dancers.Where(d =>
                          d != d1 && d != d2 && (distance(d, d1) + distance(d2)).isApprox(distance(d1, d2))
                          ));
 }
Пример #11
0
 //  Angle of dancer to the origin
 static public double angle(Dancer d)
 {
     return(new Vector2().preConcatenate(d.tx.Inverse()).Angle());
 }
Пример #12
0
 //  Distance between two dancers
 static public double distance(Dancer d1, Dancer d2)
 {
     return((d1.location - d2.location).Length());
 }
Пример #13
0
 int dancerRelation(Dancer d1, Dancer d2)
 {
     //  TODO fuzzy cases
     return(angleBin(angle(d1, d2)));
 }
Пример #14
0
 //  Angle of d2 as viewed from d1
 //  If angle is 0 then d2 is in front of d1
 //  Angle returned is in the range -pi to pi
 static public double angle(Dancer d1, Dancer d2)
 {
     return(d2.location.concatenate(d1.tx.Inverse()).Angle());
 }
        public void resetAnimation()
        {
            if (tam != null)
            {
                leadin  = interactiveDancer < 0 ? 2 : 3;
                leadout = interactiveDancer < 0 ? 2 : 1;
                // if (isRunnning)
                //  doneCallback();
                isRunning = false;
                beats     = 0.0;

                var tlist     = tam.SelectNodes("formation");
                var formation = tlist.Length > 0
          ? tlist.First()                                //  formation defined in animation
          : tam.hasAttr("formation")
          ? TamUtils.getFormation(tam.attr("formation")) // formation reference to formations.xml
          : tam;                                         //  formation passed in for sequencer
                var flist = formation.SelectNodes("dancer");
                dancers = new Dancer[flist.Length * (int)geometry];

                //  Except for the phantoms, these are the standard colors
                //  used for teaching callers
                var dancerColor = geometry == GeometryType.HEXAGON ?
                                  new Color[] { Colors.Red, Colors.ForestGreen, Colors.Magenta,
                                                Colors.Blue, Colors.Yellow, Colors.Cyan,
                                                Colors.LightGray, Colors.LightGray, Colors.LightGray, Colors.LightGray }
          :
                new Color[] { Colors.Red, ColorUtilities.ColorFromHex(0xff00c000), Colors.Blue, Colors.Yellow,
                              Colors.LightGray, Colors.LightGray, Colors.LightGray, Colors.LightGray };
                //  Get numbers for dancers and couples
                //  This fetches any custom numbers that might be defined in
                //  the animation to match a Callerlab or Ceder Chest illustration
                var paths   = tam.SelectNodes("path");
                var numbers = geometry == GeometryType.HEXAGON ?
                              new string[] { "A", "E", "I",
                                             "B", "F", "J",
                                             "C", "G", "K",
                                             "D", "H", "L",
                                             "u", "v", "w", "x", "y", "z" }
        : geometry == GeometryType.BIGON || paths.Length == 0 ?
                new string[] { "1", "2", "3", "4", "5", "6", "7", "8" }
        : TamUtils.getNumbers(tam);
                var couples = geometry == GeometryType.HEXAGON ?
                              new string[] { "1", "3", "5", "1", "3", "5",
                                             "2", "4", "6", "2", "4", "6",
                                             "7", "8", "7", "8", "7", "8" }
          : geometry == GeometryType.BIGON ?
                new string[] { "1", "2", "3", "4", "5", "6", "7", "8" }
          : paths.Length == 0 ?
                new string[] { "1", "3", "1", "3", "2", "4", "2", "4" }
          : TamUtils.getCouples(tam);
                var geoms = GeometryMaker.makeAll(geometry);

                //  Select a random dancer of the correct gender for the interactive dancer
                var icount = -1;
                var im     = Matrix3x2.Identity;
                if (interactiveDancer > 0)
                {
                    var rand     = new Random();
                    var selector = interactiveDancer == (int)Gender.BOY
            ? "dancer[@gender='boy']" : "dancer[@gender='girl']";
                    var glist = formation.SelectNodes(selector);
                    icount = rand.Next(glist.Count);
                    //  If the animations starts with "Heads" or "Sides"
                    //  then select the first dancer.
                    //  Otherwise the formation could rotate 90 degrees
                    //  which would be confusing
                    var title = tam.attr("title");
                    if (title.Contains("Heads") || title.Contains("Sides"))
                    {
                        icount = 0;
                    }
                    //  Find the angle the interactive dancer faces at start
                    //  We want to rotate the formation so that direction is up
                    var iangle = glist.Item((uint)icount).attr("angle").toDouble();
                    im     = Matrix.CreateRotation(-iangle.toRadians()) * im;
                    icount = icount * geoms.Count() + 1;
                }

                //  Create the dancers and set their starting positions
                int dnum = 0;
                for (var i = 0; i < flist.Length; i++)
                {
                    var fd       = flist.ElementAt(i);
                    var x        = fd.attr("x").toDouble();
                    var y        = fd.attr("y").toDouble();
                    var angle    = fd.attr("angle").toDouble();
                    var gender   = fd.attr("gender");
                    var g        = gender == "boy" ? Gender.BOY : gender == "girl" ? Gender.GIRL : Gender.PHANTOM;
                    var movelist = paths.Length > i?TamUtils.translatePath(paths.ElementAt(i))
                                       : new List <Movement>();

                    //  Each dancer listed in the formation corresponds to
                    //  one, two, or three real dancers depending on the geometry
                    foreach (Geometry geom in geoms)
                    {
                        var m    = Matrix3x2.Identity * Matrix.CreateRotation(angle.toRadians()) * Matrix.CreateTranslation(x, y) * im;
                        var nstr = g == Gender.PHANTOM ? " " : numbers[dnum];
                        var cstr = g == Gender.PHANTOM ? " " : couples[dnum];
                        var c    = g == Gender.PHANTOM ? Colors.LightGray : dancerColor[int.Parse(cstr) - 1];
                        //  add one dancer
                        //icount -= 1;
                        if ((int)g == interactiveDancer && --icount == 0)
                        {
                            idancer       = new InteractiveDancer(nstr, cstr, g, c, m, geom.clone(), movelist);
                            dancers[dnum] = idancer;
                        }
                        else
                        {
                            dancers[dnum]        = new Dancer(nstr, cstr, g, c, m, geom.clone(), movelist);
                            dancers[dnum].hidden = g == Gender.PHANTOM && !showPhantoms;
                        }
                        beats = Math.Max(dancers[dnum].beats + leadout, beats);
                        dnum++;
                    }
                } // All dancers added

                //  Initialize other stuff
                parts     = tam.attr("parts") + tam.attr("fractions");
                hasParts  = tam.attr("parts").Length > 0;
                isRunning = false;
                beat      = -leadin;
                prevbeat  = -leadin;
                partbeats = partsValues();
                //  force a redraw
                canvas.Invalidate();
                //  ready callback
                Callouts.animationReady();
            }
        }
Пример #16
0
        public static Handhold Create(Dancer d1, Dancer d2, int geometry)
        {
            if (!d1.hidden && !d2.hidden)
            {
                //  Turn off grips if not specified in current movement
                if ((d1.hands & Hands.GRIPRIGHT) != Hands.GRIPRIGHT)
                {
                    d1.rightgrip = null;
                }
                if ((d1.hands & Hands.GRIPLEFT) != Hands.GRIPLEFT)
                {
                    d1.leftgrip = null;
                }
                if ((d2.hands & Hands.GRIPRIGHT) != Hands.GRIPRIGHT)
                {
                    d2.rightgrip = null;
                }
                if ((d2.hands & Hands.GRIPLEFT) != Hands.GRIPLEFT)
                {
                    d2.leftgrip = null;
                }

                //  Check distance
                var x1       = d1.tx.M31;
                var y1       = d1.tx.M32;
                var x2       = d2.tx.M31;
                var y2       = d2.tx.M32;
                var dx       = x2 - x1;
                var dy       = y2 - y1;
                var dfactor1 = 0.1; // for distance up to 2.0
                var dfactor2 = 2.0; // for distance past 2.0
                var cutover  = geometry == GeometryType.HEXAGON ? 2.5
          : geometry == GeometryType.BIGON ? 3.7 : 2.0;
                var d        = Math.Sqrt(dx * dx + dy * dy);
                var dfactor0 = geometry == GeometryType.HEXAGON ? 1.15 : 1.0;
                var d0       = d * dfactor0;
                var score1   = d0 > cutover ? (d0 - cutover) * dfactor2 + 2 * dfactor1 : d0 * dfactor1;
                var score2   = score1;
                //  Angle between dancers
                var a0 = Math.Atan2(dy, dx);
                //  Angle each dancer is facing
                var a1 = Math.Atan2(d1.tx.M12, d1.tx.M22);
                var a2 = Math.Atan2(d2.tx.M12, d2.tx.M22);
                //  For each dancer, try left and right hands
                int    h1       = 0;
                int    h2       = 0;
                double ah1      = 0;
                double ah2      = 0;
                double afactor1 = 0.2;
                double afactor2 = geometry == GeometryType.BIGON ? 0.6 : 1.0;

                //  Dancer 1
                var a      = Math.Abs(Math.IEEERemainder(Math.Abs(a1 - a0 + Math.PI * 3.0 / 2.0), Math.PI * 2.0));
                var ascore = a > Math.PI / 6.0 ? (a - Math.PI / 6.0) * afactor2 + Math.PI / 6.0 * afactor1 : a * afactor1;
                if (score1 + ascore < 1 && (d1.hands & Hands.RIGHTHAND) != 0 &&
                    d1.rightgrip == null || d1.rightgrip == d2)
                {
                    score1 = d1.rightgrip == d2 ? 0 : score1 + ascore;
                    h1     = Hands.RIGHTHAND;
                    ah1    = a1 - a0 + Math.PI * 3.0 / 2.0;
                }
                else
                {
                    a      = Math.Abs(Math.IEEERemainder(Math.Abs(a1 - a0 + Math.PI / 2.0), Math.PI * 2.0));
                    ascore = (a > Math.PI / 6.0) ? (a - Math.PI / 6.0) * afactor2 + Math.PI / 6.0 * afactor1 : a * afactor1;
                    if (score1 + ascore < 1.0 && (d1.hands & Hands.LEFTHAND) != 0 &&
                        d1.leftgrip == null || d1.leftgrip == d2)
                    {
                        score1 = d1.leftgrip == d2 ? 0.0 : score1 + ascore;
                        h1     = Hands.LEFTHAND;
                        ah1    = a1 - a0 + Math.PI / 2.0;
                    }
                    else
                    {
                        score1 = 10.0;
                    }
                }

                //  Dancer 2
                a      = Math.Abs(Math.IEEERemainder(Math.Abs(a2 - a0 + Math.PI / 2.0), Math.PI * 2));
                ascore = a > Math.PI / 6 ? (a - Math.PI / 6) * afactor2 + Math.PI / 6 * afactor1 : a * afactor1;
                if (score2 + ascore < 1 && (d2.hands & Hands.RIGHTHAND) != 0 &&
                    d2.rightgrip == null || d2.rightgrip == d1)
                {
                    score2 = d2.rightgrip == d1 ? 0 : score2 + ascore;
                    h2     = Hands.RIGHTHAND;
                    ah2    = a2 - a0 + Math.PI / 2;
                }
                else
                {
                    a      = Math.Abs(Math.IEEERemainder(Math.Abs(a2 - a0 + Math.PI * 3.0 / 2.0), Math.PI * 2));
                    ascore = (a > Math.PI / 6) ? (a - Math.PI / 6) * afactor2 + Math.PI / 6 * afactor1 : a * afactor1;
                    if (score2 + ascore < 1.0 && (d2.hands & Hands.LEFTHAND) != 0 &&
                        d2.leftgrip == null || d2.leftgrip == d1)
                    {
                        score2 = d2.leftgrip == d1 ? 0.0 : score2 + ascore;
                        h2     = Hands.LEFTHAND;
                        ah2    = a2 - a0 + Math.PI * 3.0 / 2.0;
                    }
                    else
                    {
                        score2 = 10.0;
                    }
                }

                //  Generate return value
                if (d1.rightgrip == d2 && d2.rightgrip == d1)
                {
                    return(new Handhold(d1, d2, Hands.RIGHTHAND, Hands.RIGHTHAND, ah1, ah2, d, 0.0));
                }
                else if (d1.rightgrip == d2 && d2.leftgrip == d1)
                {
                    return(new Handhold(d1, d2, Hands.RIGHTHAND, Hands.LEFTHAND, ah1, ah2, d, 0.0));
                }
                else if (d1.leftgrip == d2 && d2.rightgrip == d1)
                {
                    return(new Handhold(d1, d2, Hands.LEFTHAND, Hands.RIGHTHAND, ah1, ah2, d, 0.0));
                }
                else if (d1.leftgrip == d2 && d2.leftgrip == d1)
                {
                    return(new Handhold(d1, d2, Hands.LEFTHAND, Hands.LEFTHAND, ah1, ah2, d, 0.0));
                }
                else if (score1 > 1.0 || score2 > 1.0 || score1 + score2 > 1.2)
                {
                    return(null);
                }
                else
                {
                    return(new Handhold(d1, d2, h1, h2, ah1, ah2, d, score1 + score2));
                }
            }
            return(null); // hidden dancer
        }
Пример #17
0
        static public bool isFacingOut(Dancer d)
        {
            var a = Math.Abs(angle(d));

            return(!a.isApprox(Math.PI / 2) && a > Math.PI / 2);
        }
Пример #18
0
 //  Distance of one dancer to the origin
 static public double distance(Dancer d)
 {
     return(d.location.Length());
 }
Пример #19
0
 //  Return all the dancers to the left, in order
 public IEnumerable <Dancer> dancersToLeft(Dancer d)
 {
     return(dancersInOrder(d, isLeft(d)));
 }
Пример #20
0
 //  Return all dancers, ordered by distance, that satisfies a conditional
 public IEnumerable <Dancer> dancersInOrder(Dancer d, Func <Dancer, bool> f)
 {
     return(dancers.Where(f).OrderBy(d2 => distance(d, d2)));
 }
Пример #21
0
 //  Return all the dancers in back, in order
 public IEnumerable <Dancer> dancersInBack(Dancer d)
 {
     return(dancersInOrder(d, isInBack(d)));
 }
Пример #22
0
 //  Return closest dancer that satisfies a given conditional
 public Dancer dancerClosest(Dancer d, Func <Dancer, bool> f)
 {
     return(dancersInOrder(d, f).FirstOrDefault());
 }
Пример #23
0
        //  Return true if this dancer is part of a couple facing same direction
        public bool isInCouple(Dancer d)
        {
            var d2 = d.data.partner;

            return(d2 != null && d.tx.Angle().angleEquals(d2.tx.Angle()));
        }
Пример #24
0
 //  Return dancer directly in front of given dancer
 public Dancer dancerInFront(Dancer d)
 {
     return(dancerClosest(d, isInFront(d)));
 }
Пример #25
0
        public void analyze()
        {
            dancers.ForEach(d => {
                d.animateToEnd();
                d.data.beau       = false;
                d.data.belle      = false;
                d.data.leader     = false;
                d.data.trailer    = false;
                d.data.partner    = null;
                d.data.center     = false;
                d.data.end        = false;
                d.data.verycenter = false;
            });
            bool istidal = false;

            dancers.ForEach(d1 => {
                Dancer bestleft  = null;
                Dancer bestright = null;
                int leftcount    = 0;
                int rightcount   = 0;
                int frontcount   = 0;
                int backcount    = 0;
                dancers.Where(d2 => d2 != d1).ForEach(d2 => {
                    //  Count dancers to the left and right,
                    //  and find the closest on each side
                    if (isRight(d1)(d2))
                    {
                        rightcount++;
                        if (bestright == null || distance(d1, d2) < distance(d1, bestright))
                        {
                            bestright = d2;
                        }
                    }
                    else if (isLeft(d1)(d2))
                    {
                        leftcount++;
                        if (bestleft == null || distance(d1, d2) < distance(d1, bestleft))
                        {
                            bestleft = d2;
                        }
                    }
                    //  Also count dancers in front and in back
                    else if (isInFront(d1)(d2))
                    {
                        frontcount++;
                    }
                    else if (isInBack(d1)(d2))
                    {
                        backcount++;
                    }
                });
                //  Use the results of the counts to assign belle/beau/leader/trailer
                //  and partner
                if (leftcount % 2 == 1 && rightcount % 2 == 0 && distance(d1, bestleft) < 3)
                {
                    d1.data.partner = bestleft;
                    d1.data.belle   = true;
                }
                else if (rightcount % 2 == 1 && leftcount % 2 == 0 && distance(d1, bestright) < 3)
                {
                    d1.data.partner = bestright;
                    d1.data.beau    = true;
                }
                if (frontcount % 2 == 0 && backcount % 2 == 1)
                {
                    d1.data.leader = true;
                }
                else if (frontcount % 2 == 1 && backcount % 2 == 0)
                {
                    d1.data.trailer = true;
                }
                //  Assign ends
                if (rightcount == 0 && leftcount > 1)
                {
                    d1.data.end = true;
                }
                else if (leftcount == 0 && rightcount > 1)
                {
                    d1.data.end = true;
                }
                //  The very centers of a tidal wave are ends
                //  Remember this special case for assigning centers later
                if (rightcount == 3 && leftcount == 4 ||
                    rightcount == 4 && leftcount == 3)
                {
                    d1.data.end = true;
                    istidal     = true;
                }
            });
            //  Analyze for centers and very centers
            //  Sort dancers by distance from center
            var dorder = dancers.OrderBy(d => d.location.Length()).ToArray();

            //  The 2 dancers closest to the center
            //  are centers (4 dancers) or very centers (8 dancers)
            if (!((double)dorder[1].location.Length()).isApprox((double)dorder[2].location.Length()))
            {
                if (dancers.Count == 4)
                {
                    dorder[0].data.center = true;
                    dorder[1].data.center = true;
                }
                else
                {
                    dorder[0].data.verycenter = true;
                    dorder[1].data.verycenter = true;
                }
            }
            // If tidal, then the next 4 dancers are centers
            if (istidal)
            {
                Enumerable.Range(2, 4).ForEach(i => dorder[i].data.center = true);
            }
            //  Otherwise, if there are 4 dancers closer to the center than the other 4,
            //  they are the centers
            else if (dancers.Count > 4 && !distance(dorder[3]).isApprox(distance(dorder[4])))
            {
                Enumerable.Range(0, 4).ForEach(i => dorder[i].data.center = true);
            }
        }
Пример #26
0
 //  Return dancer directly in back of given dancer
 public Dancer dancerInBack(Dancer d)
 {
     return(dancerClosest(d, isInBack(d)));
 }