private void AddRelationshipsToUKS(Thing t1, Thing t2)
        {
            if (t1 == null || t2 == null)
            {
                return;
            }
            //DeletePreviousRelationships(t1, t2);
            var vals1 = uks.GetValues(t1);
            var vals2 = uks.GetValues(t2);

            foreach (var pair1 in vals1)
            {
                //if (!pair1.Key.Contains("Siz")) continue;
                var val2 = vals2[pair1.Key];
                //hue is special because it is an angle which wraps around so < & > are not really defined
                if (pair1.Key == "Hue+")
                {
                    Angle diff = (pair1.Value - val2) * 2 * Math.PI;
                    diff = (diff.ToDegrees() + 180) % 360 - 180;
                    diff = Angle.FromDegrees(diff);
                    if (Math.Abs(diff) < Angle.FromDegrees(10))
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing("=" + pair1.Key, "Relationship"));
                    }
                    else
                    {
                        t1.AddRelationship(t1, uks.GetOrAddThing("!" + pair1.Key, "Relationship"));
                    }
                }
                else if (pair1.Key.EndsWith("+"))
                {
                    //value comparison
                    if (Math.Abs(pair1.Value - val2) == 0) //TODO make this tolerance a parameter
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing("=" + pair1.Key, "Relationship"));
                    }
                    else if (pair1.Value > val2)
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing("<" + pair1.Key, "Relationship"));
                    }
                    else
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing(">" + pair1.Key, "Relationship"));
                    }
                }
                else
                {
                    //equality comparison
                    if (pair1.Value == val2)
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing("=" + pair1.Key.Replace("Saved", ""), "Relationship"));
                    }
                    else
                    {
                        t1.AddRelationship(t2, uks.GetOrAddThing("!" + pair1.Key.Replace("Saved", ""), "Relationship"));
                    }
                }
            }
        }
        //fill this method in with code which will execute
        //once for each cycle of the engine
        public override void Fire()
        {
            Init();  //be sure to leave this here
            ModuleView naSource = theNeuronArray.FindModuleByLabel("UKS");

            if (naSource == null)
            {
                return;
            }
            uks = (ModuleUKS)naSource.TheModule;

            Thing areaParent  = uks.GetOrAddThing("CurrentlyVisible", "Visual");
            Thing colorParent = uks.GetOrAddThing("Color", "Visual");

            foreach (Thing area in areaParent.Children)
            {
                var   values = uks.GetValues(area);
                float hue    = values["Hue+"];
                float sat    = values["Sat+"];
                float lum    = values["Lum+"];

                Thing theColor = null;
                foreach (Thing color in colorParent.Children)
                {
                    var   valuesc = uks.GetValues(color);
                    float huec    = valuesc["Hue+"];
                    float satc    = valuesc["Sat+"];
                    float lumc    = valuesc["Lum+"];

                    //TODO add better match algorithm
                    if (huec == hue && Abs(satc - sat) < 0.2f && Abs(lumc - lum) < 0.2f)
                    {
                        theColor = color;
                        break;
                    }
                }
                if (theColor == null)
                {
                    theColor = uks.AddThing("c*", colorParent);
                    uks.SetValue(theColor, hue, "Hue", 2);
                    uks.SetValue(theColor, sat, "Sat", 2);
                    uks.SetValue(theColor, lum, "Lum", 2);
                }
                area.AddReference(theColor);
            }

            //if you want the dlg to update, use the following code whenever any parameter changes
            // UpdateDialog();
        }
示例#3
0
        //Areas have absolute coordinates, Shapes are completely relative
        Thing CreateTempShapeFromArea(Thing theArea)
        {
            foreach (Thing t in theArea.Children)
            {
                if (t.Children.Count == 0)
                {
                    return(null);
                }
            }
            if (!theArea.Label.Contains("Area"))
            {
                return(null);
            }
            //this abstracts a shape from a specific area
            Thing theShape = uks.AddThing("TempShape", new Thing[] { }); //the resultant shape

            //Areas are randomly ordered. We want the Shape to be in sequence around the shape because that's easier to search

            //first find the top/left corner
            Thing topLeft = null;

            float greatestLength = uks.GetValues(theArea)["Siz+"];
            Angle ang            = uks.GetValues(theArea)["Ang+"];

            uks.SetValue(theShape, ang, "PrefAng", 2);

            for (int i = 0; i < theArea.Children.Count; i++)
            {
                Thing corner = theArea.Children[i];
                if (topLeft == null)
                {
                    topLeft = corner;
                }
                Point p1 = (Point)corner.References[0].T.Children[0].V;

                if (p1.Y < ((Point)topLeft.Children[0].V).Y ||
                    (p1.Y == ((Point)topLeft.Children[0].V).Y && p1.X < ((Point)topLeft.Children[0].V).X))
                {
                    topLeft = theArea.Children[i];
                }
            }


            //these both are for the area
            Thing nextAreaCorner = topLeft.References[0].T;
            Thing prevAreaCorner = topLeft;

            //these are for the shape
            Thing newShapeCorner   = uks.AddThing("Cnr*", theShape);
            Thing firstShapeCorner = newShapeCorner;

            while (nextAreaCorner != topLeft)
            {
                //which area reference points to the next corner (not the previous)
                int refIndex = 0;
                if (nextAreaCorner.References.Count > 1 && nextAreaCorner.References[0].T == prevAreaCorner)
                {
                    refIndex = 1;
                }
                Thing nextShapeCorner = AddAngleandRelationship(greatestLength, nextAreaCorner, newShapeCorner, refIndex, theShape);

                newShapeCorner = nextShapeCorner;

                prevAreaCorner = nextAreaCorner;
                nextAreaCorner = nextAreaCorner.References[refIndex].T; //which way do we go?
            }

            int refIndex1 = 0;

            if (nextAreaCorner.References.Count > 1 && nextAreaCorner.References[0].T == prevAreaCorner)
            {
                refIndex1 = 1;
            }
            AddAngleandRelationship(greatestLength, nextAreaCorner, newShapeCorner, refIndex1, theShape, firstShapeCorner);

            return(theShape);
        }
        public override bool Draw(bool checkDrawTimer)
        {
            if (!base.Draw(checkDrawTimer))
            {
                return(false);
            }
            //this has a timer so that no matter how often you might call draw, the dialog
            //only updates 10x per second

            ModuleAttention parent = (ModuleAttention)base.ParentModule;

            theCanvas.Children.Clear();
            Point windowSize = new Point(theCanvas.ActualWidth, theCanvas.ActualHeight);

            ModuleView naSource = MainWindow.theNeuronArray.FindModuleByLabel("UKS");

            if (naSource == null)
            {
                return(false);
            }
            uks = (ModuleUKS)naSource.TheModule;

            Thing mentalModel = uks.GetOrAddThing("MentalModel", "Visual");

            if (mentalModel == null || mentalModel.Children.Count == 0)
            {
                return(false);
            }

            Thing attn = uks.Labeled("ATTN");

            if (attn == null || attn.References.Count == 0)
            {
                return(false);
            }

            Thing    attnTarget = attn.GetReferenceWithAncestor(uks.Labeled("Visual"));
            var      values     = uks.GetValues(attnTarget);
            HSLColor c1         = new HSLColor(values["Hue+"], values["Sat+"], values["Lum+"]);
            Color    fillColor  = c1.ToColor();

            try
            {
                double largest = 0;
                foreach (Thing area in mentalModel.Children)
                {
                    var       areaValues  = uks.GetValues(area);
                    Thing     theShape    = area.Children[0];
                    var       shapeValues = uks.GetValues(theShape);
                    PointPlus pOffset     = new PointPlus(areaValues["CtrX+"] - shapeValues["CtrX+"], areaValues["CtrY+"] - shapeValues["CtrY+"]);
                    foreach (Thing corner in theShape.Children)
                    {
                        Point p = (Point)corner.Children[0].V;
                        p       = p + pOffset;
                        largest = Math.Max(largest, p.X);
                        largest = Math.Max(largest, p.Y);
                    }
                }

                largest += 10; //a little margin

                float scale = (float)Math.Min(windowSize.X, windowSize.Y) / (float)largest;
                if (scale == 0)
                {
                    return(false);
                }

                theCanvas.Children.Clear();
                foreach (Thing area in mentalModel.Children)
                {
                    PointCollection pts         = new PointCollection();
                    var             areaValues  = uks.GetValues(area);
                    Thing           theShape    = area.Children[0];
                    var             shapeValues = uks.GetValues(theShape);

                    //These corrections are needed because known objects are stored at the location and size when they were first seen
                    //now the viewed object will have a different size and location
                    PointPlus pAreaCtr   = new PointPlus(areaValues["CtrX+"], areaValues["CtrY+"]);
                    PointPlus pShapeCtr  = new PointPlus(shapeValues["CtrX+"], shapeValues["CtrY+"]);
                    float     areaSize   = areaValues["Siz+"];
                    float     shapeSize  = shapeValues["Siz+"];
                    Angle     areaAngle  = areaValues["Ang+"];
                    Angle     shapeAngle = shapeValues["Ang+"];
                    Angle     rotation   = areaAngle - shapeAngle;

                    foreach (Thing corner in theShape.Children)
                    {
                        PointPlus p = new PointPlus((Point)corner.Children[0].V);
                        p        = p - pShapeCtr;
                        p.Theta += rotation;
                        float ratio = areaSize / shapeSize;
                        p.X *= ratio;
                        p.Y *= ratio;
                        p    = p + pAreaCtr;

                        p.X *= scale;
                        p.Y *= scale;
                        pts.Add(p);
                    }
                    Polygon poly = new Polygon {
                        Points = pts, Stroke = new SolidColorBrush(Colors.AliceBlue)
                    };
                    poly.ToolTip = area.Label;
                    poly.Fill    = this.Background;
                    if (attnTarget == area)
                    {
                        poly.Fill         = new SolidColorBrush(fillColor);
                        poly.Stroke       = new SolidColorBrush(fillColor);
                        poly.Fill.Opacity = 1;
                    }
                    poly.MouseDown += Poly_MouseDown;
                    poly.SetValue(AttentionObjectProperty, area);
                    theCanvas.Children.Add(poly);
                }
            }
#pragma warning disable 168
            catch (Exception e) //sometimes useful for debugging
#pragma warning restore 168
            { }
            return(true);
        }
        //fill this method in with code which will execute
        //once for each cycle of the engine
        public override void Fire()
        {
            Init();  //be sure to leave this here

            ModuleView naSource = theNeuronArray.FindModuleByLabel("UKS");

            if (naSource == null)
            {
                return;
            }
            uks = (ModuleUKS)naSource.TheModule;

            Thing mentalModel            = uks.GetOrAddThing("MentalModel", "Visual");
            Thing currentlyVisibleParent = uks.GetOrAddThing("CurrentlyVisible", "Visual");
            Thing motionParent           = uks.GetOrAddThing("Motion", "Visual");



            //Things which disappeared in the last cycle need special treatment to remove from mental model & relationships but not the known object
            foreach (Thing t in motionParent.Children)
            {
                if (t.Label.Contains("Disappeared"))
                {
                    Thing thingToRemove = t.Children[0];
                    for (int i = thingToRemove.Children.Count - 1; i >= 0; i--)
                    {
                        Thing child = thingToRemove.Children[i];
                        thingToRemove.RemoveChild(child);
                    }
                    uks.DeleteThing(thingToRemove);
                }
            }

            uks.DeleteAllChilden(motionParent);

            //build little lists of things which moved or appeared so we can also be left with things which things which disappeared
            List <Thing> matchedThings  = new List <Thing>();
            List <Thing> movedThings    = new List <Thing>();
            List <Thing> movedThingsNew = new List <Thing>();
            List <Thing> newThings      = new List <Thing>();

            foreach (Thing visibleArea in currentlyVisibleParent.Children)
            {
                Thing matchedThing = null;
                foreach (Thing storedArea in mentalModel.Children)
                {
                    Debug.Assert(visibleArea.Children.Count == 1);
                    if (storedArea.Children.Count != 1)
                    {
                        continue;
                    }
                    if (storedArea.Children[0] == visibleArea.Children[0]) //are these instances of the same object type?
                    {
                        var   values     = uks.GetValues(visibleArea);
                        var   prevValues = uks.GetValues(storedArea);
                        Point c1         = new Point(uks.GetValue(visibleArea, "CtrX+"), uks.GetValue(visibleArea, "CtrY+"));
                        Point c2         = new Point(uks.GetValue(storedArea, "CtrX+"), uks.GetValue(storedArea, "CtrY+"));
                        float dist       = (float)(c1 - c2).Length;
                        if (dist < (uks.GetValue(visibleArea, "Siz+") + uks.GetValue(storedArea, "Siz+")) / 2)
                        {
                            //these are probably the same object...is motion detected?
                            var changes = uks.GetChangedValues(visibleArea, storedArea);
                            if (changes.ContainsKey("Siz+") ||
                                changes.ContainsKey("CtrX+") ||
                                changes.ContainsKey("CtrY+") ||
                                changes.ContainsKey("Ang+"))
                            {
                                Thing t1 = uks.AddThing("Moved", motionParent);
                                t1.AddChild(storedArea);
                                foreach (var change in changes)
                                {
                                    uks.SetValue(t1, change.Value, change.Key, 0);
                                }
                                movedThings.Add(storedArea);
                                movedThingsNew.Add(visibleArea);
                            }
                            matchedThing = storedArea;
                            goto objectMatch;
                        }
                    }
                }
                //there is no matching object, this just appeared!
                newThings.Add(visibleArea);
                Thing m = uks.AddThing("Appeared*", motionParent);
                m.AddChild(visibleArea);
                continue;

                objectMatch :;
                if (matchedThing != null)
                {
                    matchedThings.Add(matchedThing);
                }
            }


            for (int i = mentalModel.Children.Count - 1; i >= 0; i--)
            {
                Thing t = mentalModel.Children[i];
                if (!matchedThings.Contains(t))
                {
                    Thing m = uks.AddThing("Disappeared*", motionParent);
                    m.AddChild(t);
                    mentalModel.RemoveChild(t);
                    Thing attn = uks.Labeled("ATTN");
                    if (attn != null)
                    {
                        attn.RemoveReference(t);
                    }
                }
            }

            if (motionParent.Children.Count > 1)
            {
                //analyze motion
                //if only a few objects moved, they moved
                //if all objects moved in same direction, it's becaue POV turned
                //if all objects moved in directions radiating to or from a point, POV is moving toward or away from that point.
                //TODO if all objects moved in same direction but varying magnitudes, POV has moved sideways...further objects move less

                //calculate the motion vector for all associated objects
                List <Segment> motions = new List <Segment>();
                foreach (Thing t in motionParent.Children)
                {
                    if (t.Label.StartsWith("Moved")) //ignore appeared/disappeared
                    {
                        var       values = uks.GetValues(t);
                        PointPlus p      = new PointPlus(0, (float)0);
                        if (values.ContainsKey("CtrX++"))
                        {
                            p.X = values["CtrX++"];
                        }
                        if (values.ContainsKey("CtrY++"))
                        {
                            p.Y = values["CtrY++"];
                        }
                        Segment s = new Segment();
                        s.P1   = p; //This is a relative motion value
                        values = uks.GetValues(t.Children[0]);
                        p      = new PointPlus(0, (float)0);
                        if (values.ContainsKey("CtrX+"))
                        {
                            p.X = values["CtrX+"];
                        }
                        if (values.ContainsKey("CtrY+"))
                        {
                            p.Y = values["CtrY+"];
                        }
                        s.P2 = p; //This is an absolute position
                        motions.Add(s);
                    }
                }

                if (motions.Count > 1)
                {
                    //do most move the same?  ANGULAR POV MOTION
                    //TODO: use clustering to get most likely motions
                    PointPlus pMotion = new PointPlus(motions[0].P1);

                    int count = 0;
                    foreach (Segment s in motions)
                    {
                        if (s.P1.Near(pMotion, .1f))
                        {
                            count++;
                        }
                    }
                    if (count > motions.Count - 2) //only two objects can be exceptions
                    {
                        for (int i = 0; i < motions.Count; i++)
                        {
                            if (motions[i].P1.Near(pMotion, .1f))
                            {
                                motions.RemoveAt(i);
                                Thing t = motionParent.Children[i];
                                uks.DeleteAllChilden(t);
                                uks.DeleteThing(t);
                                i--;
                            }
                            else
                            {
                                //an object has its own motion too...subtract off the POV motion
                                Thing t = motionParent.Children[i];
                                float x = uks.GetValue(t, "CtrX+");
                                float y = uks.GetValue(t, "CtrY+");
                                x -= motions[i].P1.X;
                                y -= motions[i].P1.Y;
                                uks.SetValue(t, x, "CtrX", 3);
                                uks.SetValue(t, y, "CtrY", 3);
                            }
                        }
                        Thing POVMotion = uks.AddThing("POVMotion*", "Motion");
                        uks.SetValue(POVMotion, pMotion.X, "CtrX", 3);
                        uks.SetValue(POVMotion, pMotion.Y, "CtrY", 3);
                    }
                    else
                    { //this is not a rotation...is it a motion toward/away?
                      //TODO there are no exceptions allowed
                        foreach (var m in motions)
                        {
                            m.P1 += m.P2;
                        }
                        List <PointPlus> intersections = new List <PointPlus>();
                        for (int i = 0; i < motions.Count; i++)
                        {
                            for (int j = i + 1; j < motions.Count; j++)
                            {
                                Utils.FindIntersection(motions[i].P1, motions[i].P2, motions[j].P1, motions[j].P2, out Point intersection);
                                intersections.Add(intersection);
                            }
                        }

                        //testing clustering...
                        //TODO try all this with points of interest rather than centers
                        double[][] rawData = new double[intersections.Count][];
                        for (int i = 0; i < intersections.Count; i++)
                        {
                            rawData[i] = new double[2] {
                                intersections[i].X, intersections[i].Y
                            }
                        }
                        ;
                        int[] clusters = KMeansClustering.Cluster(rawData, 2);
                        //now ask if there is a single cluster with most of the data

                        PointPlus aveIntersection = new PointPlus();
                        for (int i = 0; i < intersections.Count; i++)
                        {
                            aveIntersection.X += intersections[i].X;
                            aveIntersection.Y += intersections[i].Y;
                        }
                        aveIntersection.X /= intersections.Count;
                        aveIntersection.Y /= intersections.Count;
                        float dist = 0;
                        float dir  = 0;
                        for (int i = 0; i < intersections.Count; i++)
                        {
                            dist += (intersections[i] - aveIntersection).R;
                            //moving toward or away?
                            dir += (motions[i].P1 - intersections[i]).R - (motions[i].P2 - intersections[i]).R;
                        }
                        dist /= intersections.Count;
                        dir  /= intersections.Count;
                        if (dist < 10) // ???
                        {
                            uks.DeleteAllChilden(motionParent);
                            Thing POVMotion = uks.AddThing("POVMotion*", "Motion");
                            uks.SetValue(POVMotion, aveIntersection.X, "CtrX", 3);
                            uks.SetValue(POVMotion, aveIntersection.Y, "CtrY", 3);
                            uks.SetValue(POVMotion, dir, "CtrZ", 3);
                        }
                    }
                }
            }

            //transfer currently visible objects to previously visible
            Debug.Assert(movedThings.Count == movedThingsNew.Count);
            for (int i = 0; i < movedThings.Count; i++)
            {
                uks.SetValue(movedThings[i], uks.GetValue(movedThingsNew[i], "CtrX+"), "CtrX", 0);
                uks.SetValue(movedThings[i], uks.GetValue(movedThingsNew[i], "CtrY+"), "CtrY", 0);
                uks.SetValue(movedThings[i], uks.GetValue(movedThingsNew[i], "Siz+"), "Siz", 0);
                uks.SetValue(movedThings[i], uks.GetValue(movedThingsNew[i], "Ang+"), "Ang", 0);
            }

            foreach (var t in newThings)
            {
                t.AddParent(mentalModel); //TODO, this may leave multiple things in the mental model with the same label
            }

            //This hack puts predictable labels on things in the mental model so we can select them more easily in the description
            //Area0-n from top->bottom, left->right
            List <sortable> vals = new List <sortable>();

            foreach (Thing storedArea in mentalModel.Children)
            {
                var values = uks.GetValues(storedArea);
                vals.Add(new sortable {
                    t = storedArea, x = values["CtrX+"], y = values["CtrY+"],
                });
            }
            vals = vals.OrderBy(w => w.y * 1000 + w.x).ToList();
            for (int i = 0; i < vals.Count; i++)
            {
                vals[i].t.Label = "Area" + i;
            }

            //for debugging:
            //if there is a new image, cancel the existing relationships
            ModuleImageFile mif = (ModuleImageFile)FindModule("ImageFile");

            if (mif == null)
            {
                return;
            }
            string curPath = mif.GetFilePath();

            if (curPath != prevPath)
            {
                foreach (Thing t in mentalModel.Children)
                {
                    var refs = t.GetRelationshipsByType(null, null);
                    foreach (Link ref1 in refs)
                    {
                        t.RemoveReference(ref1.T);
                    }
                }
                prevPath = curPath;
            }

            //if you want the dlg to update, use the following code whenever any parameter changes
            // UpdateDialog();
        }