I have a problem. I have created this code to check how many shared edges my triangle formation has:
public class TriangleRegistryList
{
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public int ShapeNum { get; set; }
public TriangleRegistryList()
{
this.AdjacentShapeNumbers = new List<int>();
}
public List<int> AdjacentShapeNumbers { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
}
public bool IsAdjacentTo(TriangleRegistryList other)
{
var isAdjacentTo =
GetPoints().Intersect(other.GetPoints()).Count() >= 2;
if (isAdjacentTo)
{
if(other.ShapeNum != 0)
{
AdjacentShapeNumbers.Add(other.ShapeNum);
}
}
return isAdjacentTo;
}
}
public static class EnumerableExtensions
{
public static IEnumerable<(T first, T second)> GetPairs<T>(this IEnumerable<T> list)
{
return list.SelectMany((value, index) => list.Skip(index + 1),
(first, second) => (first, second));
}
}
And then I create the list like this:
triangles = new List<TriangleRegistryList>();
triangles.Add(new TriangleRegistryList
{
x1 = (float)405,
y1 = (float)701.4806,
x2 = (float)675,
y2 = (float)701.4806,
x3 = (float)540,
y3 = (float)935.3074
});
triangles.Add(new TriangleRegistryList
{
x1 = (float)135,
y1 = (float)701.4806,
x2 = (float)405,
y2 = (float)701.4806,
x3 = (float)270,
y3 = (float)935.3074
});
triangles.Add(new TriangleRegistryList
{
x1 = (float)270,
y1 = (float)935.3074,
x2 = (float)540,
y2 = (float)935.3074,
x3 = (float)405,
y3 = (float)701.4806
});
And finally I call the method to compare all the triangle sides with eachother.
int sharedEdges = triangles.GetPairs().Where(t => t.first.IsAdjacentTo(t.second)).Count();
This works great, but now I want to compare 1 triangle to the list, so I can check how many shared edges 1 triangle has with the formation. I already created this loop, but I don't know how to continue:
foreach (TriangleRegistryList triangle in triangles)
{
int sharedEdges = triangles.GetPairs().Where(t => t.first.IsAdjacentTo(t.second)).Count();
}
How can I only compare 1 triangle with the list to see how many shared edges 1 triangle has with the formation?
I would separate the concept of Triangle out completely. Trying to manage triangle point properties and maintain an adjacent ledger on the same class is going to complicate things as your code scales.
Here's a very rough design of how you could get started in this direction:
public class Triangle
{
public PointF Point1 { get; }
public PointF Point2 { get; }
public PointF Point3 { get; }
public IEnumerable<PointF> Points => new List<PointF> { Point1, Point2, Point3 };
public Triangle(PointF point1, PointF point2, PointF point3)
{
this.Point1 = point1;
this.Point2 = point2;
this.Point3 = point3;
}
public Triangle(float x1, float y1, float x2, float y2, float x3, float y3)
: this(new PointF(x1, y1), new PointF(x2, y2), new PointF(x3, y3)) { }
public bool IsAdjacentTo(Triangle other) => this.Points.Intersect(other.Points).Count() > 1;
}
public class TriangleRegistryList
{
public IList<Triangle> Triangles { get; }
public Dictionary<Triangle, List<Triangle>> AdjacentMap { get; }
public TriangleRegistryList(IEnumerable<Triangle> triangles)
{
this.Triangles = new List<Triangle>(triangles);
this.AdjacentMap = GetAdjacentMap();
}
private Dictionary<Triangle, List<Triangle>> GetAdjacentMap()
{
return Triangles.ToDictionary(t => t, a => Triangles.Where(b => b.IsAdjacentTo(a)).ToList());
}
}
This is not tested by any means, but you see the compartmentalizing of each respective class.
Related
I am using the AreaSeries in Oxyplot to graph a range between two points and would like the Tracker to display both of them when hovered. I achieved making the Tracker display custom properties with other series by creating a custom class for the DataPoints and changing the TrackerFormat accordingly. Here is an example:
public class CommentPoint : IScatterPointProvider
{
public CommentPoint(double x, double y, string text)
{
X = x; Y = y; Text = text;
}
public double X, Y;
public string Text { get; set; }
public ScatterPoint GetScatterPoint()
{
return new ScatterPoint(X, Y);
}
}
And the TrackerFormat:
Series.TrackerFormatString = "{2}\n{Text}";
However, this only works when adding an array of the new points as the ItemSource of the series. With Line/ScatterSeries it's easy, since they require just a list of points. But an AreaSeries requres 2 points to be graphed.
I tried adding a 2d array of these custom points as the ItemSource:
public class AreaPoint : IDataPointProvider
{
public AreaPoint(double x, double y, double y2)
{
X = x; Y1 = y; Y2 = y2;
}
public double X { get; set; }
public double Y1 { get; set; }
public double Y2 { get; set; }
public DataPoint GetDataPoint()
{
return new DataPoint(X, Y1);
}
}
So basically:
AreaPoint[,] points = new AreaPoint[2,amount.Count]
AreaPoint[0,i] = new AreaPoint(x,y,y2);
....
Series.ItemSource = points;
That doesn't work and I recieved an error "Is not a one-dimentional array".
Next I tried adding the points directly via the GetDataPoint method:
Series.Points.Add(new AreaPoint(x,y,y2).GetDataPoint());
Series.Points2.Add(new AreaPoint(x2,y2,y3).GetDataPoint());
Series.TrackerFormatString = "{2}\n{4}\n{Y2}";
This works in that the AreaSeries is being plotted correctly, but the Tracker won't display my custom properties.
So I suppose my question is, how can I add a custom ItemSource to an AreaSeries?
You can just add X2, Y2 fields to your custom point class and set DataField properties in your AreaSeries.
public class AreaPoint : IDataPointProvider
{
public AreaPoint(double x, double y, double x2, double y2, string customValue)
{
X = x;
Y = y;
X2 = x2;
Y2 = y2;
CustomValue = customValue;
}
public double X { get; set; }
public double Y { get; set; }
public double X2 { get; set; }
public double Y2 { get; set; }
public string CustomValue { get; set; }
}
...
series = new AreaSeries();
series.DataFieldX = "X";
series.DataFieldY = "Y";
series.DataFieldX2 = "X2";
series.DataFieldY2 = "Y2";
series.TrackerFormatString = "{0}\n{1}: {2}\n{3}: {4}" +
"\nMy custom value: {CustomValue}"
I have a problem. I want to compare 2 coordinates with each other, but the coordinates doesn't have to be exactly the same. It's allowed to have a maximum difference of 0,1. So I created this code:
public class HexagonRegistryList
{
public int HexagonNum { get; set; }
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public float x4 { get; set; }
public float y4 { get; set; }
public float x5 { get; set; }
public float y5 { get; set; }
public float x6 { get; set; }
public float y6 { get; set; }
public int ShapeNum { get; set; }
public HexagonRegistryList()
{
this.AdjacentShapeNumbers = new List<int>();
}
public List<int> AdjacentShapeNumbers { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
yield return (x4, y4);
yield return (x5, y5);
yield return (x6, y6);
}
public struct PointComparer : IEqualityComparer<(float x, float y)>
{
public bool Equals((float x, float y) p1, (float x, float y) p2)
{
return Math.Abs(p1.x - p2.x) < 0.1f && Math.Abs(p1.y - p2.y) < 0.1f;
}
public int GetHashCode((float x, float y) obj)
{
return obj.GetHashCode();
}
}
public bool IsAdjacentTo(HexagonRegistryList other)
{
//var isAdjacentTo = GetPoints().Intersect(other.GetPoints()).Count() >= 2;
var isAdjacentTo = GetPoints().Intersect(other.GetPoints(), new PointComparer()).Count() >= 2;
if (isAdjacentTo)
{
if (other.ShapeNum != 0)
{
AdjacentShapeNumbers.Add(other.ShapeNum);
}
}
return isAdjacentTo;
}
}
Now when it gets at this line: var isAdjacentTo = GetPoints().Intersect(other.GetPoints(), new PointComparer()).Count() >= 2; It returns false, what is supposed to be true. Here are the coordinates:
x1 = 607.5
y1 = 935.3075
x2 = 607.5
y2 = 935.3074
As you can see the y coordinates have a difference of 0.0001, but that shouldn't be a problem with my code. But for some reason it says that these coordinates don't match!
I am comparing hexagon sides with each other, so here is the main call I do:
var sharedEdges = hexagons.GetPairs().Where(t => hexagon.IsAdjacentTo(hexagons[i]));
I am also using this class for the comparing:
public static class EnumerableExtensions
{
public static IEnumerable<(T first, T second)> GetPairs<T>(this IEnumerable<T> list)
{
return list.SelectMany((value, index) => list.Skip(index + 1),
(first, second) => (first, second));
}
}
hexagons is a List and hexagon is just 1 hexagon in the list.
What am I doing wrong?
I suspect the problem is that Intersect is effectively building a hash set to compare for equality. Your two "nearly equal" coordinates won't have the same hash code, so it won't even call Equals. You could implement GetHashCode by always returning 0, but fundamentally you've still got a big problem: your equality comparer can't implement IEqualityComparer<T> as intended. An equality comparer should obey these rules for Equals:
Reflexive: Equals(x, x) should return true - that's fine
Symmetric: Equals(x, y) should return Equals(y, x) - that's fine too
Transitive: if Equals(x, y) and Equals(y, z) return true, then Equals(x, z) should return true too - and that's not fine.
If you have three points, say:
a=(0, 0)
b=(0.09, 0)
c=(0.18, 0) then you're saying that a and b are close, and b and c are close, but a and c are not close
Asking for proximity isn't the same as asking for equality - and you want the former.
I realize this answer doesn't show you what you should do - but it's showing you where you're going wrong. I think you'll need to change your approach entirely. It's not entirely clear to me what you want to achieve in your method, but you might want to consider finding the distances between points instead of treating them as equal.
I have a problem. I created this class to find all the shared edges of a hexagon:
public class HexagonRegistryList
{
public int HexagonNum { get; set; }
public float x1 { get; set; }
public float y1 { get; set; }
public float x2 { get; set; }
public float y2 { get; set; }
public float x3 { get; set; }
public float y3 { get; set; }
public float x4 { get; set; }
public float y4 { get; set; }
public float x5 { get; set; }
public float y5 { get; set; }
public float x6 { get; set; }
public float y6 { get; set; }
public int ShapeNum { get; set; }
public HexagonRegistryList()
{
this.AdjacentShapeNumbers = new List<int>();
}
public List<int> AdjacentShapeNumbers { get; set; }
public IEnumerable<(float x, float y)> GetPoints()
{
yield return (x1, y1);
yield return (x2, y2);
yield return (x3, y3);
yield return (x4, y4);
yield return (x5, y5);
yield return (x6, y6);
}
public bool IsAdjacentTo(HexagonRegistryList other)
{
var isAdjacentTo =
GetPoints().Intersect(other.GetPoints()).Count() >= 2;
if (isAdjacentTo)
{
if (other.ShapeNum != 0)
{
AdjacentShapeNumbers.Add(other.ShapeNum);
}
}
return isAdjacentTo;
}
}
But now I want something, so the values don't have to be exactly the same, but that they can have a difference of max 1. So when I compare 350 with 350, it could also be 350 with 349, or 350 with 351. Can someone help me with that?
Define a custom comparer:
public struct PointComparer : IEqualityComparer<(float x, float y)>
{
public bool Equals((float x, float y) p1, (float x, float y) p2)
{
return Math.Abs(p1.x - p2.x) < 1f && Math.Abs(p1.y - p2.y) < 1f;
}
public int GetHashCode((float x, float y) obj)
{
return 1;
}
}
Then pass it to Intersect method
GetPoints().Intersect(other.GetPoints(), new PointComparer())
You would need to create an implementation which uses a bounded absolute difference to determine proximity; this can be done as follows.
var DIFF_THRESHOLD = 1.0f;
var DIFF_THRESHOLD_SQUARE = DIFF_THRESHOLD * DIFF_THRESHOLD;
private IsCloseTo(float x1, float y1, float x2, float y2)
{
var EuklideanDistance
= ( x1 - x2 ) * ( x1 - x2 ) + ( y1 - y2 ) * ( y1 - y2 );
return EuklidenDistance <= DIFF_THRESHOLD_SQUARE;
}
Then you could use Linq to determine the number of points which are close to each other.
Am getting color input from user using SfColorPalette, this returns me selected color as hex decimal color codes. But i need this colors as equivalent or exact color names since it will used to filter in search.
Tried below work arouds
Convert Hex to color
Get color from HexColor
String hex to color in windows phone runtime
Here is my solution:
Firstly, I made a custom Class:
public class ColorReference
{
public string Name { get; set; }
public Vector3 Argb { get; set; }
}
This is to construct the known color which get from this site
private static ColorReference[] GetColorReferences()
{
return new ColorReference[] {
new ColorReference() { Name="AliceBlue", Argb=new Vector3 (
240,248,255) },
new ColorReference() { Name="LightSalmon", Argb=new Vector3 (
255,160,122) },
......
};
}
Secondly, I treat these Vectors as three-dimensional vectors, for a single vector, I can get the closest one based on Vector3.Distance method.
private static ColorReference GetClosestColor(ColorReference[] colorReferences, Vector3 currentColor)
{
ColorReference tMin = null;
float minDist = float.PositiveInfinity;
foreach (ColorReference t in colorReferences)
{
float dist = Vector3.Distance(t.Argb, currentColor);
if (dist < minDist)
{
tMin = t;
minDist = dist;
}
}
return tMin;
}
Use the above method to get the nearest color's name:
public static string GetNearestColorName(Vector3 vect)
{
var cr = GetClosestColor(GetColorReferences(), vect);
if( cr != null )
{
return cr.Name;
}
else
return string.Empty;
}
Also need this method to extract argb value from hex demical value:
public static Vector3 GetSystemDrawingColorFromHexString(string hexString)
{
if (!System.Text.RegularExpressions.Regex.IsMatch(hexString, #"[#]([0-9]|[a-f]|[A-F]){6}\b"))
throw new ArgumentException();
int red = int.Parse(hexString.Substring(1, 2), NumberStyles.HexNumber);
int green = int.Parse(hexString.Substring(3, 2), NumberStyles.HexNumber);
int blue = int.Parse(hexString.Substring(5, 2), NumberStyles.HexNumber);
return new Vector3(red, green, blue);
}
Screenshot:
Check my completed demo from here: Github Link
----------Update 07/26/2016--------
For Windows/Phone 8.1, because the Vector3 class is missing, use the following class in your project:
public class Vector3
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
public Vector3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public static float Distance(Vector3 a, Vector3 b)
{
return (float)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2) + Math.Pow(a.Z - b.Z, 2)); ;
}
}
I'm doing K-means clustering of n points and k centers.
To start off, here's my code so far:
A class for a point:
public class Point
{
public int Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public Point()
{
Id = -1;
X = -1;
Y = -1;
}
public Point(int id, double x, double y)
{
this.Id = id;
this.X = x;
this.Y = y;
}
public static double FindDistance(Point pt1, Point pt2)
{
double x1 = pt1.X, y1 = pt1.Y;
double x2 = pt2.X, y2 = pt2.Y;
double distance = Math.Sqrt(Math.Pow(x2 - x1, 2.0) + Math.Pow(y2 - y1, 2.0));
return (distance);
}
}
A class for data:
public class PointCollection : List<Point>
{
public Point Centroid { get; set; }
public PointCollection()
: base()
{
Centroid = new Point();
}
public void AddPoint(Point p)
{
this.Add(p);
UpdateCentroid();
}
public Point RemovePoint(Point p)
{
Point removedPoint = new Point(p.Id, p.X, p.Y);
this.Remove(p);
UpdateCentroid();
return (removedPoint);
}
public void UpdateCentroid()
{
double xSum = (from p in this select p.X).Sum();
double ySum = (from p in this select p.Y).Sum();
Centroid.X = (xSum / (double)this.Count);
Centroid.Y = (ySum / (double)this.Count);
}
}
Main class:
public class KMeans
{
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount)
{
List<PointCollection> allClusters = new List<PointCollection>();
List<List<Point>> allGroups = ListUtility.SplitList<Point>(points, clusterCount);
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.AddRange(group);
allClusters.Add(cluster);
}
int movements = 1;
while (movements > 0)
{
movements = 0;
foreach (PointCollection cluster in allClusters)
{
for (int pointIndex = 0; pointIndex < cluster.Count; pointIndex++)
{
Point point = cluster[pointIndex];
int nearestCluster = FindNearestCluster(allClusters, point);
if (nearestCluster != allClusters.IndexOf(cluster))
{
if (cluster.Count > 1)
{
Point removedPoint = cluster.RemovePoint(point);
allClusters[nearestCluster].AddPoint(removedPoint);
movements += 1;
}
}
}
}
}
return (allClusters);
}
public static int FindNearestCluster(List<PointCollection> allClusters, Point point)
{
double minimumDistance = 0.0;
int nearestClusterIndex = -1;
for (int k = 0; k < allClusters.Count; k++)
{
double distance = Point.FindDistance(point, allClusters[k].Centroid);
if (k == 0)
{
minimumDistance = distance;
nearestClusterIndex = 0;
}
else if (minimumDistance > distance)
{
minimumDistance = distance;
nearestClusterIndex = k;
}
}
return (nearestClusterIndex);
}
}
And finally helping function for list split:
public static List<List<T>> SplitList<T>(List<T> items, int groupCount)
{
List<List<T>> allGroups = new List<List<T>>();
int startIndex = 0;
int groupLength = (int)Math.Round((double)items.Count / (double)groupCount, 0);
while (startIndex < items.Count)
{
List<T> group = new List<T>();
group.AddRange(items.GetRange(startIndex, groupLength));
startIndex += groupLength;
if (startIndex + groupLength > items.Count)
{
groupLength = items.Count - startIndex;
}
allGroups.Add(group);
}
if (allGroups.Count > groupCount && allGroups.Count > 2)
{
allGroups[allGroups.Count - 2].AddRange(allGroups.Last());
allGroups.RemoveAt(allGroups.Count - 1);
}
return (allGroups);
}
So, now I'm trying to write a second method for the main class, which would accept a predefined starting centres. I have trouble grasping that though, as I can't find anything on the internet where the k-means algorithm would use initial centers. Can someone point me in a direction of such guide or give me any ideas how to modify the code? Thanks.
Edit: Maybe something more why am I trying to do this: I try to write LBG algorithm using k-means, like so https://onlinecourses.science.psu.edu/stat557/node/67
I can access the computed centers for splitting in each of the steps with my code, however I need to find a way to feed them back to the k-means class. Like, if I calculated the starting centre, i need to put this centre and another offset by epsilon into k-means algorithm.
Edit2: Code now in English(I hope)
Found a solution, maybe someone will use:
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount, Point[] startingCentres)
{
// code...
int ctr = 0;
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.c.X = startingCentres[ctr].X;
cluster.c.Y = startingCentres[ctr].Y;
cluster.AddRange(group);
allClusters.Add(cluster);
}
// rest of code the same
}