// Clasifica las líneas en horizontales, verticales y otras public static void Clasifier(Stroke s) { double slope = ExtraMath.Slope(s, 0); // Si pendiente = INFINITE => vertical_strokes // Si pendiente<SLOPE_MAX => horizontal_strokes // Si pendiente>1/SLOPE_MAX=> vertical_strokes // En otro caso => other_strokes if (slope == Facade.INFINITE) { Facade.vertical_strokes.Add(s); } else { if (slope < Facade.SLOPE_MAX) { Facade.horizontal_strokes.Add(s); } else { if (slope > (1 / Facade.SLOPE_MAX)) { Facade.vertical_strokes.Add(s); } else { Facade.other_strokes.Add(s); } } } }
// Determina la distancia que debe existir entre los puntos muestreados public static double DeterminingResampleSpacing(Stroke s) { double dist_between_points; Point top_left = new Point(); Point bottom_right = new Point();; // Se obtiene el BoundingBox del Stroke Rectangle r = s.GetBoundingBox(); // Se asignan a top_left la esquina superior izquierda del boundingbox, // y a bottom_right la esquina inferior derecha. top_left.X = r.Left; top_left.Y = r.Top; bottom_right.X = r.Right; bottom_right.Y = r.Bottom; // Se calcula la longitud de la diagonal formada por las dos esquinas // calculadas double diagonal = ExtraMath.Distance(bottom_right, top_left); // Se calcula la distancia entre los puntos muestreados dist_between_points = diagonal / 40.0; return(dist_between_points); }
// Determina la distancia del tramo (no la distancia más corta) public static double PathDistance(List <Point> resampled, int a, int b) { double distance = 0; for (int i = a; i < b; i++) { distance += (double)ExtraMath.Distance(resampled[i], resampled[i + 1]); } return(distance); }
// Deja los puntos del stroke que esten espaciados en dist_between_points public static List <Point> ResamplePoints(Stroke s, double dist_between_points) { // Se crea una lista con los puntos del stroke List <Point> points = new List <Point>(s.GetPoints()); // Se añade el primer punto del stroke a la coleccion de resampled List <Point> resampled = new List <Point>(); resampled.Add(points[0]); // Se crea un nuevo punto que se utilizara mas adelante Point pt = new Point(); // Se inicializa el marcador de distancia a 0 double distance_holder = 0; // Se inicializa la distancia entre dos puntos double distance_points = 0; // Se recorren todos los puntos del stroke haciendo lo siguiente for (int i = 1; i < points.Count; i++) { // Se calcula la distancia Euclidea entre el punto actual y el anterior distance_points = ExtraMath.Distance(points[i - 1], points[i]); if ((distance_holder + distance_points) >= dist_between_points) { // Se crea un punto nuevo (pt) localizado aproximadamente a una distancia // dist_between_points del último punto muestreado pt.X = (int)(points[i - 1].X + (((dist_between_points - distance_holder) / distance_points) * (points[i].X - points[i - 1].X))); pt.Y = (int)(points[i - 1].Y + (((dist_between_points - distance_holder) / distance_points) * (points[i].Y - points[i - 1].Y))); // Se añade el punto pt a la lista de "resampled" resampled.Add(pt); // Se añade el punto pt al stroke, en la posicion anterior al punto en el // que estamos points.Insert(i, pt); distance_holder = 0; } else { distance_holder += distance_points; } } return(resampled); }
/// <summary> /// Calculated the first time it is asked for and cached after that. /// </summary> /// <returns></returns> public double GetConvexPerimeter() { if (this.convexPerimeter == 0) { double perim = 0; for (int i = 0; i < convexPoints.Length - 1; i++) { perim += ExtraMath.Distance(convexPoints[i], convexPoints[i + 1]); } convexPerimeter = perim; } return(convexPerimeter); }
/// <summary> /// Returns the area of the convex hull, calculated as the sum of the triangles. /// </summary> /// <returns></returns> public double GetConvexArea() { if (this.convexArea == 0) { double area = 0; Point point1 = convexPoints[0]; for (int i = 1; i < convexPoints.Length - 1; i++) { area += ExtraMath.CalculateTriangularArea(point1, convexPoints[i], convexPoints[i + 1]); } convexArea = area; } return(convexArea); }
// Determina si la parte del stroke indicada es una línea o no public static bool IsLine(List <Point> resampled, int a, int b) { double distance = (double)ExtraMath.Distance(resampled[a], resampled[b]); double path_distance = (double)PathDistance(resampled, a, b); if ((distance / path_distance) > Facade.THRESHOLD) { return(true); } else { return(false); } }
/// <summary> /// Uses the Andrew variant of the Graham formula for calculating /// convex hulls. /// </summary> /// <param name="points"></param> /// <returns></returns> public Point[] CalculateConvexHull(Point[] points) { if (points.Length < 3) { return(points); } // TODO: Implement a quicksort algorithm, and find cut off point for switching algorithms. points = BubblesortPoints(points); List <Point> upper = new List <Point>(); upper.Add(points[0]); upper.Add(points[1]); for (int i = 2; i < points.Length; i++) { upper.Add(points[i]); while (upper.Count > 2 && !ExtraMath.IsRightTurn(upper[upper.Count - 3], upper[upper.Count - 2], upper[upper.Count - 1])) { upper.RemoveAt(upper.Count - 2); } } List <Point> lower = new List <Point>(); lower.Add(points[points.Length - 1]); lower.Add(points[points.Length - 2]); for (int i = points.Length - 3; i >= 0; i--) { lower.Add(points[i]); while (lower.Count > 2 && !ExtraMath.IsRightTurn(lower[lower.Count - 3], lower[lower.Count - 2], lower[lower.Count - 1])) { lower.RemoveAt(lower.Count - 2); } } Point[] retPoints = new Point[upper.Count + lower.Count]; for (int i = 0; i < upper.Count; i++) { retPoints[i] = upper[i]; } for (int i = 0; i < lower.Count; i++) { retPoints[i + upper.Count] = lower[i]; } return(retPoints); }
// Compara los strokes por longitud private static int CompareStrokesByLength(Stroke s1, Stroke s2) { if (s1 == null) { if (s2 == null) { // Si s1==s2==null => son iguales return(0); } else { // Si s1==null y s2!=null, s2 es mayor return(-1); } } else { // Si s1!=null... if (s2 == null) // ...y s2==null, s1 es mayor. { return(1); } else { // ... y s2!=null, comparar la longitud de los 2 strokes // ExtraMath.Distance(s.GetPoint(s.GetPoints().Length-1), s.GetPoint(0)) double s1_length = ExtraMath.Distance(s1.GetPoint(s1.GetPoints().Length - 1), s1.GetPoint(0)); double s2_length = ExtraMath.Distance(s2.GetPoint(s2.GetPoints().Length - 1), s2.GetPoint(0)); int retval = s1_length.CompareTo(s2_length); if (retval != 0) { // Si tienen distinta longitud, el más largo es mayor return(retval); } else { // Si tienen el mismo tamaño se toma el primero como mayor return(1); } } } }
// Determina si el stroke "n" pertenece a la relación del stroke "s" public static bool BelongsToRelation(Stroke s, Stroke n, int index) { float dist; float f_index = n.NearestPoint(s.GetPoint(index), out dist); double stroke_lenght = ExtraMath.Distance(s.GetPoint(s.GetPoints().Length - 1), s.GetPoint(0)); if (dist < stroke_lenght * Facade.LENGTH_PERCENTAGE) { return(true); } else { return(false); } }
public static float GetFIndex(Point pt_corner, Stroke s) { double best_distance = Facade.INFINITE; double distance = Facade.INFINITE; float findex = 0; for (int i = 0; i < s.GetPoints().Length; i++) { distance = ExtraMath.Distance(pt_corner, s.GetPoint(i)); if (distance < best_distance) { best_distance = distance; findex = (float)i; } } return(findex); }
// Busca los puntos de resampled que se corresponden con esquinas public static List <int> GetCorners(List <Point> resampled) { // Se crea la lista donde iran las esquinas. Almacenara un conjunto de indices // que referencian puntos. Por ejemplo, corner(i)=j indica que el punto(j) es // la i-esima esquina encontrada List <int> corners = new List <int>(); corners.Add(0); // Se crea una lista con las distancias entre dos puntos separados W puntos del punto // actual List <double> straws = new List <double>(); for (int i = 0; i < Facade.W; i++) { straws.Add(ExtraMath.Distance(resampled[i + Facade.W], resampled[i])); } for (int i = Facade.W; i < (resampled.Count - Facade.W); i++) { straws.Add(ExtraMath.Distance(resampled[i - Facade.W], resampled[i + Facade.W])); } for (int i = (resampled.Count - Facade.W); i < resampled.Count; i++) { straws.Add(ExtraMath.Distance(resampled[i - Facade.W], resampled[i])); } // Se calcula un umbral, threshold. Para ello, ordenamos la lista de straws y calculamos // su mediana. Para calcular la mediana se necesita ordenar la lista. Se trabajara con una // copia de la lista para no modificar la original List <double> copy_straws = new List <double>(straws); copy_straws.Sort(); int middle = copy_straws.Count / 2; double median = (copy_straws.Count % 2 != 0) ? (double)copy_straws[middle] : ((double)copy_straws[middle] + (double)copy_straws[middle - 1]) / 2; double threshold = median * 0.95; // Ahora se recorre la lista de straws. Si la distancia es menor que el umbral, se considera // esquina double local_min; int local_min_index; for (int i = Facade.W; i < (resampled.Count - Facade.W); i++) { if (straws[i] < threshold) { local_min = Facade.INFINITE; local_min_index = i; while (i < straws.Count && straws[i] < threshold) { if (straws[i] < local_min) { local_min = straws[i]; local_min_index = i; } i++; } corners.Add(local_min_index); } } // Se añade el ultimo indice a corners corners.Add(resampled.Count - 1); corners = PostProcessCorners(resampled, corners, straws); return(corners); }
// Determina la relacion en funcion de las puntas // ---------- (Ningun stroke en la punta) // ---------> (Se mira el stroke de la punta más cercano al resto de la flecha, // y se miran los strokes más cercanos a dicho stroke. Si uno es la // la línea larga de la flecha se trata de una asociación direccionada) // --------<> (Los strokes más cercanos al stroke más cercano a la línea larga, // son paralelos) // --------|> (Los strokes más cercanos al stroke más cercano a la línea larga, // no son paralelos) public static void SearchRelationships() { // Se ordenan los strokes de menor a mayor longitud Facade.strokes_without_recognize.Sort(CompareStrokesByLength); // Se le da la vuelta para tenerlos de mayor a menor longitud Facade.strokes_without_recognize.Reverse(); int i = Facade.strokes_without_recognize.Count; do { // Se crean los strokes n1 y n2, correspondientes a los strokes más cercanos // al stroke s (el primero de la lista de los strokes_without_recognize) Stroke n1 = null; Stroke n2 = null; // Será el stroke más cercano a "s" Stroke n = null; // Se crean los strokes nn1 y nn2, correspondientes a los strokes más cercanos // al stroke n Stroke nn1 = null; Stroke nn2 = null; // Se mira el primer stroke. Si es el único, se trata de una asociación (----) Stroke s = Facade.strokes_without_recognize[0]; if (i == 1) { MessageBox.Show("asociacion porque queda 1 stroke"); CreateAssociation(s); i -= 1; } else { // Se miran los strokes mas cercanos a los extremos del primer stroke // (n1 para el primer extremo y n2 para el segundo) NearbyStrokes(s, ref n1, ref n2, Facade.strokes_without_recognize, 1); // Se comprueba que estén a menos de un 10% de la longitud del stroke // inicial. Si es así, pertenecen a la relación. En caso contrario se // trata de una asociación (----)( se crea la asociación y se elimina // el stroke de la lista de no reconocidos) bool belongs_n1 = BelongsToRelation(s, n1, 0); bool belongs_n2 = BelongsToRelation(s, n2, s.GetPoints().Length - 1); if (belongs_n1 == false && belongs_n2 == false) { MessageBox.Show("Asociacion por no belongs"); CreateAssociation(s); i -= 1; } else { // Si no tenemos una asociación, se miran los strokes más cercanos // a los dos extremos del stroke que estaba más cerca del inicial. if (belongs_n1 == true) { n = n1; } if (belongs_n2 == true) { n = n2; } // Se miran los strokes mas cercanos a los extremos de "n" (nn1 para // el primer extremo y nn2 para el segundo) NearbyStrokes(n, ref nn1, ref nn2, Facade.strokes_without_recognize, 0); // Si esos dos strokes son paralelos: ----<> (Se crea relación // y se eliminan strokes de no reconocidos) if (ExtraMath.AreParallels(nn1, nn2)) { MessageBox.Show("agregacion"); CreateAgregation(s, n, nn1, nn2); i -= 5; } else { // Si no son paralelos y si nn1 o nn2 coinciden con s: ----> if (FormManager.CompareStrokes(s, nn1) || FormManager.CompareStrokes(s, nn2)) { MessageBox.Show("asociacion direc"); CreateAssociationDirectional(s, n, nn1, nn2); i -= 3; } else { MessageBox.Show("herencia"); // Si no son paralelos y ni nn1 ni nn2 coinciden con s: // ----|> (Crear relación y eliminar strokes) CreateGeneralization(s, n, nn1, nn2); i -= 4; } } } } } while (i > 0); }