(I have rearranged my question, I hope now is clearer, I apologise). I have a question regarding how to rearrange lists in an special manner. I have heard about the hashset but I do not fully understand how that thing works or if it is the best option for this issue. Basically I have 3 lists: LIST1, LIST2 and LIST3. LIST1 is a set of xyz points and LIST2 shows how the points are connected in list 1 (e.g point1 -> point 4 -> point 3). Please note that the value -1 at the end of each line in list 2 does not mean anything at the minute but I need to keep it for future usage.
The idea is to get 3 groups of values:
· Group 1 including all the values that are clockwise (list3) with the points in order in list1 and showing connections starting from zero on in list2.
· Group 2 including all the values that are counterclockwise (list3) with the points in order in list1 and showing connections starting from zero on in list2.
· Group 3 including all the values that are coolinear (list3) with the points in order in list1 and showing connections starting from zero on in list2.
The idea is to be able to organise the tables depending on the values in List 3 (0,1, or -1) which will also affect table 2 as indicated in the example below:
The three lists looks initially like this:
LIST1 LIST2 LIST3
0 0 128.588459085565, 0, 1, 2,-1 1
25 0 134.979628462965, 1, 4, 2,-1 1
0 0 134.979628462965, 2, 4, 5,-1 1
25 0 128.588459085565, 4, 6, 5,-1 -1
25 0 140.100717207301, 5, 6, 7,-1 -1
0 0 140.100717207301, 6, 8, 7,-1 -1
25 0 143.87494372892, 7, 8, 9,-1 1
0 0 143.87494372892, 8, 10, 9,-1 1
25 0 146.245863353464, 9, 10, 11,-1 1
0 0 146.245863353464, 10, 12, 11,-1 -1
25 0 147.181161545899, 11, 12, 13,-1 -1
0 0 147.181161545899, 12, 14, 13,-1 -1
25 0 146.668001779529, 13, 14, 15,-1 -1
0 0 146.668001779529, 14, 16, 15,-1 -1
25 0 144.711312619297, 15, 16, 17,-1 -1
0 0 144.711312619297, 16, 18, 17,-1 -1
25 0 141.340784276868, 17, 18, 19,-1 -1
0 0 141.340784276868, 18, 20, 19,-1 -1
25 0 136.604499944182, 19, 20, 21,-1 -1
0 0 136.604499944182, 20, 22, 21,-1 0
25 0 130.573315654463, 21, 22, 23,-1 0
0 0 130.573315654463, 22, 24, 23,-1 0
25 0 123.341482952817, 23, 24, 25,-1 -1
0 0 123.341482952817, 24, 26, 25,-1 -1
25 0 115.031289382498, 25, 26, 27,-1 -1
0 0 115.031289382498, 0, 3, 1,-1 1
25 0 105.793445439687, 1
0 0 105.793445439687, 1
Now I will filter which values in list 1 and list 2 correspond to the value 0 in list 3:
LIST1 LIST2 LIST3
0 0 136.604499944182, 20, 22, 21,-1 0
25 0 130.573315654463, 21, 22, 23,-1 0
0 0 130.573315654463, 22, 24, 23,-1 0
However I cannot keep list 2 as shown above. I need to show the list 2 from the beginning (it will contain zero somewhere, then 1,2,3,4 will be placed accordingly. The desired result is as follows:
LIST1 LIST2 LIST3
0 0 136.604499944182, 0, 2, 1,-1 0
25 0 130.573315654463, 2, 2, 3,-1 0
0 0 130.573315654463, 2, 4, 3,-1 0
Hope this is clear enough! Any help will be extremely welcome!!!
Many thanks!!

VulpesPosted May 17, 2014, 5:28 PM
Try this:
The output is:
HappyCode HappyCodePosted Nov 17, 2014, 12:03 PM
Hello Vulpes,
Many thanks for all your help in this matter. I have been thinking about the subdivision problem and I can only see non straightforward workarounds. I will have to look deeper into the subdivision issue later on but for now I have decided to move on. I am currently working on the contouring code. The contouring code was working for the expected cases represented by Groups 11,12,21,22 but we had problems when it came to the groups 31,32.
After a lot of thinking I came up with an algorithm that might work properly for such groups. I would like to add this algorithm to the current code but I am bit lost about how to proceed.
I have attached a file explaining the algorithm and a practical example to clarify its implementation. Can you please have a look at it?
My final code should be as follows:
////////////////////////////////////////////////
3 possible inputs:
i) Group11 and Group12 or
ii) Group21 and Group22 or
iii) Group31 and Group32
For i) and ii) we can use the current code.
For iii) we have to use the new algorithm.
Outputs:
Contour indices (go to txt file)
Contour points (go to txt file and to xls file)
/////////////////////////////////////////////////
The contouring code I am referring to is the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
#region Structs
struct Pair
{
public int X { get; set; }
public int Y { get; set; }
public Pair(int x, int y)
: this()
{
X = x;
Y = y;
}
public bool Equals(Pair p)
{
if ((p.X == this.X && p.Y == this.Y) || (p.X == this.Y && p.Y == this.X))
return true;
return false;
}
public override string ToString()
{
return String.Format("{0}, {1}, -1,", this.X, this.Y);
}
}
#endregion
class Program
{
static void Main()
{
//In the example attached we use directly the list pairs rather than group12 //because it would be very complicated to generate an example that contains all the //possible cases directly from here.
#region Lists
int[] a0 = { 1,3,3,-1, };
int[] a1 = { 3,2,0,-1, };
int[] a2 = { 1,0,2,-1, };
int[] a3 = { 0,1,2, -1, };
int[] a4 = { 4,2,0, -1, };
int[] a5 = { 5,4,3, -1, };
int[] a6 = { 4,3,2, -1, };
int[] a7 = { 5,4,5, -1, };
var group12 = new List<int[]> { a0, a1, a2, a3, a4, a5, a6, a7
};
var pairs = new List<Pair>();
var edge13 = new List<int[]>();
var pairsUsed = new List<int>();
#endregion
/* We need to exclude all isolated triangles from group12
when generating the list of possible pairs.
A triangle will be isolated if its vertices are not shared by
any other triangle in group12. So we start by counting how many times
each vertex occurs
*/
#region count vertex
var vertexCount = new Dictionary<int, int>();
foreach (int[] a in group12)
{
foreach (int b in a)
{
if (vertexCount.ContainsKey(b))
{
vertexCount[b]++;
}
else
{
vertexCount.Add(b, 1);
}
}
}
#endregion
#region get all possible pairs from group12 but exclude isolated triangles
foreach (int[] a in group12)
{
if (vertexCount[a[0]] == 1 &&
vertexCount[a[1]] == 1 &&
vertexCount[a[2]] == 1) continue;
pairs.Add(new Pair(a[0], a[1]));
pairs.Add(new Pair(a[1], a[2]));
pairs.Add(new Pair(a[2], a[0]));
}
#endregion
#region remove duplicates leaving just unique pairs
for (int i = 0; i < pairs.Count - 1; i++)
{
bool isDuplicated = false;
for (int j = pairs.Count - 1; j > i; j--)
{
if (pairs[i].Equals(pairs[j]))
{
pairs.RemoveAt(j);
isDuplicated = true;
}
}
if (isDuplicated)
{
pairs.RemoveAt(i);
i--;
}
}
#endregion
#region sort pairs by X and then by y
pairs = (from p in pairs orderby p.X, p.Y select p).ToList();
while (pairs.Count > 0)
{
pairsUsed.Clear();
pairsUsed.Add(0);
int next = pairs[0].Y;
bool found;
do
{
found = false;
for (int j = 1; j < pairs.Count; j++)
{
if (pairs[j].X == next && !pairsUsed.Contains(j)) //****
{
pairsUsed.Add(j);
next = pairs[j].Y;
found = true;
break;
}
}
}
while (found && pairs[0].X != next);
int[] a = new int[pairsUsed.Count + 2];
for (int k = 0; k < pairsUsed.Count; k++)
{
a[k] = pairs[pairsUsed[k]].X;
}
a[a.Length - 2] = next;
a[a.Length - 1] = -1;
edge13.Add(a);
pairsUsed.Sort();
for (int l = pairsUsed.Count - 1; l >= 0; l--)
{
pairs.RemoveAt(pairsUsed[l]);
}
}
#endregion
#region printout edge13 list to file
foreach (int[] a in edge13)
{
Console.WriteLine(String.Join(", ", a));
Console.WriteLine();
}
#endregion
Console.ReadKey();
}
}
Your help is greatly appreciated! Many thanks!!!
VulpesPosted Nov 5, 2014, 5:15 AM
Renaming the vertices (if they duplicated existing vertices in list1) wouldn't alter how they were displayed - they're still the same points - and not removing duplicate triangles wouldn't alter the drawing either because such triangles would just overwrite each other.
I'm probably missing something fundamental here but it does seem that this strategy is not working and that a rethink is needed.
HappyCode HappyCodePosted Nov 4, 2014, 9:59 AM
Please see attached the result obtained. It seems like the points are not properly connected for whatever reason. I have spent 5 hours trying to understand why this is happening with no success :-(.
Any ideas?
Many thanks!
VulpesPosted Nov 3, 2014, 4:10 PM
I've also added a new method, AreEqual, in the Point class to help with this and, as there's a lot of output, I'm now redirecting it to a file (triangles.txt) rather than the console.
I've assumed that it does no harm to still clean the input before sub-division.
As far as I can tell it's working OK but whether the graphical output will be satisfactory remains to be seen :)
HappyCode HappyCodePosted Nov 3, 2014, 6:29 AM
VulpesPosted Oct 30, 2014, 8:47 PM
I'm wondering therefore whether the best way to approach this would be check, in the Subdivide method itself, whether each new vertex is already in the list and, if it is, use its original index rather than adding it again. We wouldn't then need to clean the lists afterwards and rename the duplicate vertices.
It's not going to be easy to code however we do it but does that approach make sense to you?
HappyCode HappyCodePosted Oct 29, 2014, 1:09 PM
I have tried the code and the small examples work well with this code. However the complex example does not really work as it should (it looks like it works because all the points have the same orientation before and after the subdivision).
Actually the subdivision code splits the triangles correctly, however when the points pass through the "CleanLists" many of them seems to disappear as it can be seen in the attachment. However I want all of them, I just want them to be renamed if they are duplicated.
I have check the CleanList code but I do not really get how I could modify it to fix it.
Any hints?
Thank you!!
VulpesPosted Oct 23, 2014, 12:12 PM
It dawned on me that we shouldn't need to try all 6 combinations of the 3 points. If the first combination gives the wrong orientation, then all we need to do is to reverse the order of the first two points as that should then give the correct orientation.
Anyway, the only thing I've changed from the way you last had it is the Subdivide method which now looks like this:
HappyCode HappyCodePosted Oct 23, 2014, 5:54 AM
Hello Vulpes,
Many thanks for this. I have drawn the results obtained and I found out that the connectivity between the points was not correct using the previous code. I have made some modifications in the code and now the connectivity is OK and List3 show positive values for the previous example. To test it I have decided to change the orientation of one of the two triangles in List2 to see if the lists are properly split into groups and it worked OK as well. Being happy with the result I have created a more complex example… But unfortunately this example scuppered my initial joy. In this example all the triangles before the subdivision have the same orientation but after the subdivision they have mixed orientations. At this point I see that the only robust solution we might have is the option of trying all the combinations to get the good orientation as you suggested.
Can you please help me implementing this option?
Below is the code with the modifications I have done (it includes the lists for the simple example with two triangles and different orientations –which works ok-) and also the lists for the "complex" example (which does not work).
Thank you very much for all your help, it is greatly appreciated.
using System;
using System.Collections.Generic;
struct Point
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Point(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return String.Format("{0} {1} {2},", X, Y, Z);
}
private static double Determinant(Point a, Point b, Point c)
{
return ((b.Y - a.Y) * (c.Z - a.Z) + (b.Z - a.Z) * (c.X - a.X) + (b.X - a.X) * (c.Y - a.Y) - (b.Z - a.Z) * (c.Y - a.Y) - (b.X - a.X) * (c.Z - a.Z) - (b.Y - a.Y) * (c.X - a.X));
}
public static bool CounterClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) > 0.0;
}
public static bool ClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) < 0.0;
}
public static bool Collinear(Point a, Point b, Point c)
{
return Determinant(a, b, c) == 0.0;
}
public static Point FromString(string p)
{
string[] items = p.Replace(",", "").Split(' ');
double[] coords = new double[3];
for (int i = 0; i < 3; i++) coords[i] = double.Parse(items[i]);
return new Point(coords[0], coords[1], coords[2]);
}
public static double GetArea(Point a, Point b, Point c)
{
return Math.Abs(Determinant(a, b, c)) / 2.0;
}
//GET determinant value
public static double GetDet(Point a, Point b, Point c)
{
return Determinant(a, b, c);
}
//
public static Point Middle(Point a, Point b)
{
return new Point((a.X + b.X) / 2.0, (a.Y + b.Y) / 2.0, (a.Z + b.Z) / 2.0);
}
public static int GetCode(Point a, Point b, Point c)
{
if (CounterClockWise(a, b, c)) return 1;
if (ClockWise(a, b, c)) return -1;
return 0;
}
}
class Program
{
static void Main()
{
var list1 = new List<string>
{
"0 50 0,",
"50 50 0,",
"50 0 0,",
"0 0 0,"
};
var list2 = new List<string>
{
"3, 0, 2,-1",
"0, 2, 1,-1",
};
var list3 = new List<int> { 1, 1 };
CleanLists(list1, list2, list3, null, false); // remove duplicates before sub-division
// work out areas for each triangle in List2 and total area
var areas = new List<double>();
double totalArea = 0.0;
foreach (string s in list2)
{
string[] items = s.Split(',');
int[] indices = new int[3];
for (int i = 0; i < 3; i++) indices[i] = int.Parse(items[i]);
Point a = Point.FromString(list1[indices[0]]);
Point b = Point.FromString(list1[indices[1]]);
Point c = Point.FromString(list1[indices[2]]);
double area = Point.GetArea(a, b, c);
areas.Add(area);
totalArea += area;
}
// print out areas
Console.WriteLine("-- ALL GROUPS (BEFORE SUBDIVISION) --");
Console.WriteLine("\nAREAS\n");
for (int i = 0; i < areas.Count; i++) Console.WriteLine("{0,2} : {1}", i, areas[i]);
Console.WriteLine();
// work out average area of each triangle
double origCount = list2.Count;
double avgArea = totalArea / origCount;
Console.WriteLine("Average area is {0}\n", avgArea);
//determinant values
var dets = new List<double>();
double totalDet = 0.0;
foreach (string s in list2)
{
string[] items = s.Split(',');
int[] indices = new int[3];
for (int i = 0; i < 3; i++) indices[i] = int.Parse(items[i]);
Point a = Point.FromString(list1[indices[0]]);
Point b = Point.FromString(list1[indices[1]]);
Point c = Point.FromString(list1[indices[2]]);
double det = Point.GetDet(a, b, c);
dets.Add(det);
totalDet+= det;
}
// print out det value
Console.WriteLine("-- ALL GROUPS (BEFORE SUBDIVISION) --");
Console.WriteLine("\nDETERMINANT\n");
for (int i = 0; i < dets.Count; i++) Console.WriteLine("{0,2} : {1}", i, dets[i]);
Console.WriteLine();
// subdivide the triangles whose area is greater than average until it is no longer greater than average
for (int i = 0; i < list2.Count; i++)
{
if (areas[i] > 0.5 * avgArea) //****** (remember to remove 0.5)
{
Subdivide(i, areas[i], list1, list2, list3, areas);
--i;
}
}
CleanLists(list1, list2, list3, areas, true); // remove duplicates after sub-division
// print out all lists after subdivision
Console.WriteLine("-- ALL GROUPS (AFTER SUBDIVISION) --");
Console.WriteLine("\nLIST1\n");
for (int i = 0; i < list1.Count; i++) Console.WriteLine("{0,2} : {1}", i, list1[i]);
Console.WriteLine("\nLIST2\n");
for (int i = 0; i < list2.Count; i++) Console.WriteLine("{0,2} : {1}", i, list2[i]);
Console.WriteLine("\nLIST3\n");
for (int i = 0; i < list3.Count; i++) Console.WriteLine("{0,2} : {1}", i, list3[i]);
Console.WriteLine("\nAREAS\n");
for (int i = 0; i < areas.Count; i++) Console.WriteLine("{0,2} : {1}", i, areas[i]);
Console.WriteLine();
var group11 = new List<string>(); // group 1, list1
var group12 = new List<string>(); // group 1, list2
var group21 = new List<string>(); // group 2, list1
var group22 = new List<string>(); // group 2, list2
var group31 = new List<string>(); // group 3, list1
var group32 = new List<string>(); // group 2, list2
// split up the data into the appropriate groups
for (int i = 0; i < Math.Min(list2.Count, list3.Count); i++)
{
switch (list3[i])
{
case 1: // counterclockwise
group12.Add(list2[i]);
break;
case -1: // clockwise
group22.Add(list2[i]);
break;
case 0: // collinear
group32.Add(list2[i]);
break;
}
}
group11 = ProcessGroup(list1, group12);
group21 = ProcessGroup(list1, group22);
group31 = ProcessGroup(list1, group32);
// print out groups
PrintGroup(1, group11, group12);
PrintGroup(2, group21, group22);
PrintGroup(3, group31, group32);
Console.ReadKey();
}
static List<string> ProcessGroup(List<string> points, List<string> connections)
{
// figure out which points are used in the group and store them
int[,] indices = new int[connections.Count, 3];
var allIndices = new List<int>();
for (int i = 0; i < connections.Count; i++)
{
string[] items = connections[i].Split(',');
indices[i, 0] = int.Parse(items[0]);
indices[i, 1] = int.Parse(items[1]);
indices[i, 2] = int.Parse(items[2]);
if (!allIndices.Contains(indices[i, 0])) allIndices.Add(indices[i, 0]);
if (!allIndices.Contains(indices[i, 1])) allIndices.Add(indices[i, 1]);
if (!allIndices.Contains(indices[i, 2])) allIndices.Add(indices[i, 2]);
}
allIndices.Sort();
var dict = new Dictionary<int, int>();
for (int i = 0; i < allIndices.Count; i++) dict.Add(allIndices[i], i);
for (int i = 0; i < connections.Count; i++)
{
for (int j = 0; j < 3; j++) indices[i, j] = dict[indices[i, j]]; // remap actual indices to 0, 1, 2 etc
connections[i] = String.Format("{0}, {1}, {2},-1", indices[i, 0], indices[i, 1], indices[i, 2]);
}
var groupPoints = new List<string>();
foreach (int index in allIndices) groupPoints.Add(points[index]);
return groupPoints;
}
static void PrintGroup(int num, List<string> points, List<string> connections)
{
Console.WriteLine("-- GROUP {0} --", num);
Console.WriteLine("\nLIST1\n");
foreach (string point in points) Console.WriteLine(point);
Console.WriteLine("\nLIST2\n");
foreach (string connection in connections) Console.WriteLine(connection);
Console.WriteLine();
}
//NEW SUBDIVIDE CLASS (22/10/2014). KEEPS PROPER ORIENTATION
static void Subdivide(int index, double area, List<string> points, List<string> triangles, List<int> codes, List<double> areas)
{
string[] items = triangles[index].Split(',');
int[] indices = new int[6];
for (int i = 0; i < 3; i++) indices[i] = int.Parse(items[i]);
Point v0 = Point.FromString(points[indices[0]]);
Point v1 = Point.FromString(points[indices[1]]);
Point v2 = Point.FromString(points[indices[2]]);
Point v3 = Point.Middle(v0, v1); // v01
Point v4 = Point.Middle(v1, v2); // v12
Point v5 = Point.Middle(v2, v0); // v20
int count = points.Count;
points.Add(v3.ToString());
points.Add(v4.ToString());
points.Add(v5.ToString());
indices[3] = count;
indices[4] = count + 1;
indices[5] = count + 2;
triangles.Insert(index + 1, String.Format("{0}, {1}, {2},-1", indices[0], indices[5], indices[3]));
triangles.Insert(index + 2, String.Format("{0}, {1}, {2},-1", indices[3], indices[5], indices[4]));
triangles.Insert(index + 3, String.Format("{0}, {1}, {2},-1", indices[3], indices[4], indices[1]));
triangles.Insert(index + 4, String.Format("{0}, {1}, {2},-1", indices[5], indices[2], indices[4]));
triangles.RemoveAt(index);
area /= 4.0;
areas.Insert(index + 1, area);
areas.Insert(index + 2, area);
areas.Insert(index + 3, area);
areas.Insert(index + 4, area);
areas.RemoveAt(index);
codes.Insert(index + 1, Point.GetCode(Point.FromString(points[indices[0]]), Point.FromString(points[indices[5]]), Point.FromString(points[indices[3]])));
codes.Insert(index + 2, Point.GetCode(Point.FromString(points[indices[3]]), Point.FromString(points[indices[5]]), Point.FromString(points[indices[4]])));
codes.Insert(index + 3, Point.GetCode(Point.FromString(points[indices[3]]), Point.FromString(points[indices[4]]), Point.FromString(points[indices[1]])));
codes.Insert(index + 4, Point.GetCode(Point.FromString(points[indices[5]]), Point.FromString(points[indices[2]]), Point.FromString(points[indices[4]])));
codes.RemoveAt(index);
}
static bool AreTrianglesEqual(string triangle1, string triangle2)
{
string[] items1 = triangle1.Split(',');
int[] vertices1 = new int[3];
for (int i = 0; i < 3; i++) vertices1[i] = int.Parse(items1[i]);
Array.Sort(vertices1);
string[] items2 = triangle2.Split(',');
int[] vertices2 = new int[3];
for (int i = 0; i < 3; i++) vertices2[i] = int.Parse(items2[i]);
Array.Sort(vertices2);
if ((vertices1[0] == vertices2[0]) && (vertices1[1] == vertices2[1]) && (vertices1[2] == vertices2[2])) return true;
return false;
}
static void CleanLists(List<string> list1, List<string> list2, List<int> list3, List<double> areas, bool afterSubdivision)
{
string ab = afterSubdivision ? "After" : "Before";
// check for duplicates in list1
int[] remappings = new int[list1.Count];
var duplicates = new List<int>();
for (int i = 0; i < list1.Count; i++) remappings[i] = i;
for (int i = 0; i < list1.Count - 1; i++)
{
if (remappings[i] != i) continue;
for (int j = i + 1; j < list1.Count; j++)
{
if (list1[i] == list1[j])
{
duplicates.Add(j);
remappings[j] = i;
}
}
}
if (duplicates.Count > 0)
{
var reorderings = new int[list1.Count];
for (int i = 0; i < list1.Count; i++) reorderings[i] = i;
duplicates.Sort();
foreach (int dup in duplicates)
{
reorderings[dup] = -1;
}
for (int i = list1.Count - 1; i >= 0; i--)
{
if (reorderings[i] == -1 && i < list1.Count - 1)
{
for (int j = i + 1; j < list1.Count; j++)
{
if (reorderings[j] != -1) reorderings[j]--;
}
}
}
for (int i = 0; i < list1.Count; i++)
{
remappings[i] = reorderings[remappings[i]];
}
// now delete duplicates from list1
for (int i = duplicates.Count - 1; i >= 0; i--)
{
list1.RemoveAt(duplicates[i]);
}
// finally remap vertices in list2
for (int i = 0; i < list2.Count; i++)
{
string[] items = list2[i].Split(',');
int[] indices = new int[3];
for (int j = 0; j < 3; j++)
{
indices[j] = int.Parse(items[j]);
indices[j] = remappings[indices[j]];
}
list2[i] = String.Join(", ", indices) + ",-1";
}
// check it worked
Console.WriteLine(ab + " sub-division, duplicates were found and removed from list1 and list2 remapped as follows\n");
foreach (string s in list1) Console.WriteLine(s);
Console.WriteLine();
foreach (string s in list2) Console.WriteLine(s);
Console.WriteLine();
}
else
{
Console.WriteLine(ab + " sub-division, no duplicates found in list1\n");
}
// now check for duplicates in list2
duplicates.Clear();
for (int i = 0; i < list2.Count - 1; i++)
{
if (duplicates.Contains(i)) continue;
for (int j = i + 1; j < list2.Count; j++)
{
if (duplicates.Contains(j)) continue;
if (AreTrianglesEqual(list2[i], list2[j]))
{
duplicates.Add(j);
}
}
}
if (duplicates.Count > 0)
{
duplicates.Sort();
// delete duplicates from list2 and the corresponding entry in list3 and (if after sub-division) areas
for (int i = duplicates.Count - 1; i >= 0; i--)
{
list2.RemoveAt(duplicates[i]);
list3.RemoveAt(duplicates[i]);
if (afterSubdivision) areas.RemoveAt(duplicates[i]);
}
// check it worked
Console.WriteLine(ab + " sub-division, duplicates were found and removed from list2 and list3 adjusted as follows\n");
foreach (string s in list2) Console.WriteLine(s);
Console.WriteLine();
foreach (int i in list3) Console.WriteLine(i);
Console.WriteLine();
}
else
{
Console.WriteLine(ab + " sub-division, no duplicates found in list2\n");
}
}
}
THE LISTS FOR THE COMPLEX EXAMPLE:
var list1 = new List
{
"20 -20 20,",
"20 -20 -20,",
"20 -9.2388 3.82683,",
"20 -7.07107 7.07107,",
"20 -3.82683 9.2388,",
"20 6.12323e-016 10,",
"20 20 20,",
"20 20 -20,",
"20 -1.83697e-015 -10,",
"20 -3.82683 -9.2388,",
"20 -7.07107 -7.07107,",
"20 -9.2388 -3.82683,",
"20 -10 1.22465e-015,",
"20 9.2388 3.82683,",
"20 10 0,",
"20 9.2388 -3.82683,",
"20 7.07107 -7.07107,",
"20 3.82683 -9.2388,",
"20 3.82683 9.2388,",
"20 7.07107 7.07107,",
};
var list2 = new List
{
"0,1,2,-1",
"0,2,3,-1",
"0,3,4,-1",
"0,4,5,-1",
"0,5,6,-1",
"1,7,8,-1",
"1,8,9,-1",
"1,9,10,-1",
"1,10,11,-1",
"1,11,12,-1",
"1,12,2,-1",
"7,13,14,-1",
"7,14,15,-1",
"7,15,16,-1",
"7,16,17,-1",
"7,17,8,-1",
"6,5,18,-1",
"6,18,19,-1",
"6,19,13,-1",
"6,13,7,-1",
};
var list3 = new List { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
VulpesPosted Oct 21, 2014, 4:11 PM
But when I ran it, disappointingly, all the entries in list3 were -1:
HappyCode HappyCodePosted Oct 21, 2014, 1:47 PM
Hello Vulpes,
I came out with an idea that might work in all cases (see attached). If you see a problem with this method I would have no problem about going with the combinations option you mentioned earlier.
Let me know what you think.
Thank you!
VulpesPosted Oct 21, 2014, 11:56 AM
The problem lines are these:
triangles.Insert(index + 1, String.Format("{0}, {1}, {2},-1", indices[0], indices[3], indices[5]));
triangles.Insert(index + 2, String.Format("{0}, {1}, {2},-1", indices[3], indices[4], indices[5]));
triangles.Insert(index + 3, String.Format("{0}, {1}, {2},-1", indices[3], indices[1], indices[4]));
triangles.Insert(index + 4, String.Format("{0}, {1}, {2},-1", indices[5], indices[4], indices[2]));
What we need to do is to order the points of the triangles in such a way that they have the same orientation as the triangle that was divided.
Whilst one could do this by trying different combinations until one was found which did have the same orientation, I wondered whether you knew a way of doing this that would always work?
HappyCode HappyCodePosted Oct 21, 2014, 9:04 AM
I think we are nearly there :). There is just a small problem here regarding the ordering of the triangles after the subdivision. Basically if a triangle before the subdivision is for instance counterclockwise and we divide it into four small triangles then these 4 triangles must be also counterclockwise. However this is something that is not happening now. Let's look at the input lists 1 and 2.
list1 =
"0 50 0,",
"50 50 0,",
"50 0 0,",
"0 0 0,",
list2 =
"3, 0, 2,-1",
"0, 1, 2,-1",
List 3 will be populated based on List 1 and List 2 using the determinant. So:
If det>0 the turn is CCW and we add "1" to List3
If det<0 the turn is CW and we add "-1" to List3
If det=0 the points are collinear and we add "0" to List3.
Based on that, in this particular example (before subdivision) List3={1,1} because the two triangles are CCW. That also mean that we will only have Group11 and Group12, the other groups will be void (List 3 does not contain -1 or 0 values).
Then after the subdivision (each triangle is divided into four in this example) List3 should look like:
List 3= {1,1,1,1,1,1,1,1}
However if we look at the results obtained we obtain negative values for List3 which is not correct.
LIST3
0 : -1
1 : -1
2 : -1
3 : -1
4 : -1
5 : -1
6 : -1
7 : -1
That in return give us a wrong result for group12 because the triangles are oriented the other way around (counterclockwise). -Sorry I did not noticed this before-.
The expected result should look as follows:
-- ALL GROUPS (AFTER SUBDIVISION) --
LIST1
0 : 0 50 0,
1 : 50 50 0,
2 : 50 0 0,
3 : 0 0 0,
4 : 0 25 0,
5 : 25 25 0,
6 : 25 0 0,
7 : 25 50 0,
8 : 50 25 0,
LIST2 (in this case LIST2 contains only CCW point but it could contain also CW or collinear in other examples)
0 : 3, 6, 4,-1
1 : 4, 6, 5,-1
2 : 4, 5, 0,-1
3 : 6, 2, 5,-1
4 : 0, 5, 7,-1
5 : 7, 5, 8,-1
6 : 7, 8, 1,-1
7 : 5, 2, 8,-1
LIST3
0 : 1
1 : 1
2 : 1
3 : 1
4 : 1
5 : 1
6 : 1
7 : 1
-- GROUP 1 --
LIST1
0 50 0,
50 50 0,
50 0 0,
0 0 0,
0 25 0,
25 25 0,
25 0 0,
25 50 0,
50 25 0,
LIST2 (points must be ordered CCW because LIST 3 contain "1" values in it)
3, 6, 4,-1
4, 6, 5,-1
4, 5, 0,-1
6, 2, 5,-1
0, 5, 7,-1
7, 5, 8,-1
7, 8, 1,-1
5, 2, 8,-1
-- GROUP 2 -- Void group because List3 does not contain -1 values
LIST1
LIST2
-- GROUP 3 --Void group because List3 does not contain 0 values
LIST1
LIST2
Is there a way to change the code to ensure that the triangles before and after the subdivision keep the same orientation?
Thank you very much!
VulpesPosted Oct 20, 2014, 3:50 PM
Rather than repeat the 'duplicate checking' code, I've moved it to a method called 'CleanLists' which I've customized slightly depending on whether it's being called before or after sub-division.
Here's the revised code:
The output, which looks to be correct now, is:
HappyCode HappyCodePosted Oct 20, 2014, 12:21 PM
The problem here is that the duplicate points problem arise after the subdivision of the triangles. Let me explain this using the examples attached earlier.
If you look at the drawing on the left you will see that the figure is formed by two triangles (before subdivision). The drawing on the right is the result of dividing these triangles. Please note that since both triangles have the same area then subdivision would not be required in this case as the subdivision criteria is based on the average area. However, in order to force the subdivision I have changed the subdivision criteria to 0.5*Average area in the code to make it work for this example (see highlighted). That means that each of the triangles will be divided into 4 triangles and therefore we will have 8 triangles in total after the subdivision in this example.
Let's use following input lists representing the drawing on the left (without duplicate values):
list1 =
"0 50 0,",
"50 50 0,",
"50 0 0,",
"0 0 0,",
list2 =
"3, 0, 2,-1",
"0, 1, 2,-1",
list3 = {1, 1};
And not let's subdivide the two triangles of the diagram based on 0.5 times the Average area to get the diagram on the right. The results of the subdivision obtained with the last code (picture on the right) are as follows:
-- GROUP 1 --
LIST1
0 50 0,
50 50 0,
50 0 0,
0 0 0,
0 25 0,
25 25 0,
25 0 0,
25 50 0,
50 25 0,
25 25 0,
LIST2
3, 4, 6,-1
4, 5, 6,-1
4, 0, 5,-1
6, 5, 2,-1
0, 7, 9,-1
7, 8, 9,-1
7, 1, 8,-1
9, 8, 2,-1
Which are not correct because number 5 and 9 represent the same point.
This is what I mean when I say that the duplication problem appears after the subdivision.
Does it make sense to you?
See the code below with my changes highlighted:
using System;
using System.Collections.Generic;
struct Point
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Point(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return String.Format("{0} {1} {2},", X, Y, Z);
}
private static double Determinant(Point a, Point b, Point c)
{
return (b.Y - a.Y) * (c.Z - a.Z) + (b.Z - a.Z) * (c.X - a.X) + (b.X - a.X) * (c.Y - a.Y) - (b.Z - a.Z) * (c.Y - a.Y) - (b.X - a.X) * (c.Z - a.Z) - (b.Y - a.Y) * (c.X - a.X);
}
public static bool CounterClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) > 0.0;
}
public static bool ClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) < 0.0;
}
public static bool Collinear(Point a, Point b, Point c)
{
return Determinant(a, b, c) == 0.0;
}
public static Point FromString(string p)
{
string[] items = p.Replace(",", "").Split(' ');
double[] coords = new double[3];
for (int i = 0; i < 3; i++) coords[i] = double.Parse(items[i]);
return new Point(coords[0], coords[1], coords[2]);
}
public static double GetArea(Point a, Point b, Point c)
{
return Math.Abs(Determinant(a, b, c)) / 2.0;
}
public static Point Middle(Point a, Point b)
{
return new Point((a.X + b.X) / 2.0, (a.Y + b.Y) / 2.0, (a.Z + b.Z) / 2.0);
}
public static int GetCode(Point a, Point b, Point c)
{
if (CounterClockWise(a, b, c)) return 1;
if (ClockWise(a, b, c)) return -1;
return 0;
}
}
class Program
{
static void Main()
{
var list1 = new List
{
"0 50 0,",
"50 50 0,",
"50 0 0,",
"0 0 0,",
};
var list2 = new List
{
"3, 0, 2,-1",
"0, 1, 2,-1",
};
var list3 = new List
// check for duplicates in list1
int[] remappings = new int[list1.Count];
var duplicates = new List
for (int i = 0; i < list1.Count; i++) remappings[i] = i;
for (int i = 0; i < list1.Count - 1; i++)
{
if (remappings[i] != i) continue;
for (int j = i + 1; j < list1.Count; j++)
{
if (list1[i] == list1[j])
{
duplicates.Add(j);
remappings[j] = i;
}
}
}
if (duplicates.Count > 0)
{
var reorderings = new int[list1.Count];
for (int i = 0; i < list1.Count; i++) reorderings[i] = i;
duplicates.Sort();
foreach (int dup in duplicates)
{
reorderings[dup] = -1;
}
for (int i = list1.Count - 1; i >= 0; i--)
{
if (reorderings[i] == -1 && i < list1.Count - 1)
{
for (int j = i + 1; j < list1.Count; j++)
{
if (reorderings[j] != -1) reorderings[j]--;
}
}
}
for (int i = 0; i < list1.Count; i++)
{
remappings[i] = reorderings[remappings[i]];
}
// now delete duplicates from list1
for (int i = duplicates.Count - 1; i >= 0; i--)
{
list1.RemoveAt(duplicates[i]);
}
// finally remap vertices in list2
for (int i = 0; i < list2.Count; i++)
{
string[] items = list2[i].Split(',');
int[] indices = new int[3];
for (int j = 0; j < 3; j++)
{
indices[j] = int.Parse(items[j]);
indices[j] = remappings[indices[j]];
}
list2[i] = String.Join(", ", indices) + ",-1";
}
// check it worked
Console.WriteLine("Duplicates were found and removed from list1 and list2 remapped as follows\n");
foreach (string s in list1) Console.WriteLine(s);
Console.WriteLine();
foreach (string s in list2) Console.WriteLine(s);
Console.WriteLine();
}
else
{
Console.WriteLine("No duplicates found in list1\n");
}
// now check for duplicates in list2
duplicates.Clear();
for (int i = 0; i < list2.Count - 1; i++)
{
if (duplicates.Contains(i)) continue;
for (int j = i + 1; j < list2.Count; j++)
{
if (duplicates.Contains(j)) continue;
if (AreTrianglesEqual(list2[i], list2[j]))
{
duplicates.Add(j);
}
}
}
if (duplicates.Count > 0)
{
duplicates.Sort();
// delete duplicates from list2 and the corresponding entry in list3
for (int i = duplicates.Count - 1; i >= 0; i--)
{
list2.RemoveAt(duplicates[i]);
list3.RemoveAt(duplicates[i]);
}
// check it worked
Console.WriteLine("Duplicates were found and removed from list2 and list3 adjusted as follows\n");
foreach (string s in list2) Console.WriteLine(s);
Console.WriteLine();
foreach (int i in list3) Console.WriteLine(i);
Console.WriteLine();
}
else
{
Console.WriteLine("No duplicates found in list2\n");
}
// work out areas for each triangle in List2 and total area
var areas = new List
double totalArea = 0.0;
foreach (string s in list2)
{
string[] items = s.Split(',');
int[] indices = new int[3];
for (int i = 0; i < 3; i++) indices[i] = int.Parse(items[i]);
Point a = Point.FromString(list1[indices[0]]);
Point b = Point.FromString(list1[indices[1]]);
Point c = Point.FromString(list1[indices[2]]);
double area = Point.GetArea(a, b, c);
areas.Add(area);
totalArea += area;
}
// print out areas
Console.WriteLine("-- ALL GROUPS (BEFORE SUBDIVISION) --");
Console.WriteLine("\nAREAS\n");
for (int i = 0; i < areas.Count; i++) Console.WriteLine("{0,2} : {1}", i, areas[i]);
Console.WriteLine();
// work out average area of each triangle
double origCount = list2.Count;
double avgArea = totalArea / origCount;
Console.WriteLine("Average area is {0}\n", avgArea);
// subdivide the triangles whose area is greater than 0.5*average until it is no longer greater than average
for (int i = 0; i < list2.Count; i++)
{
if (areas[i] > 0.5*avgArea)
{
Subdivide(i, areas[i], list1, list2, list3, areas);
--i;
}
}
// print out all lists after subdivsion
Console.WriteLine("-- ALL GROUPS (AFTER SUBDIVISION) --");
Console.WriteLine("\nLIST1\n");
for (int i = 0; i < list1.Count; i++) Console.WriteLine("{0,2} : {1}", i, list1[i]);
Console.WriteLine("\nLIST2\n");
for (int i = 0; i < list2.Count; i++) Console.WriteLine("{0,2} : {1}", i, list2[i]);
Console.WriteLine("\nLIST3\n");
for (int i = 0; i < list3.Count; i++) Console.WriteLine("{0,2} : {1}", i, list3[i]);
Console.WriteLine("\nAREAS\n");
for (int i = 0; i < areas.Count; i++) Console.WriteLine("{0,2} : {1}", i, areas[i]);
Console.WriteLine();
var group11 = new List
var group12 = new List
var group21 = new List
var group22 = new List
var group31 = new List
var group32 = new List
// split up the data into the appropriate groups
for (int i = 0; i < Math.Min(list2.Count, list3.Count); i++)
{
switch (list3[i])
{
case -1: // clockwise
group12.Add(list2[i]);
break;
case 1: // counterclockwise
group22.Add(list2[i]);
break;
case 0: // collinear
group32.Add(list2[i]);
break;
}
}
group11 = ProcessGroup(list1, group12);
group21 = ProcessGroup(list1, group22);
group31 = ProcessGroup(list1, group32);
// print out groups
PrintGroup(1, group11, group12);
PrintGroup(2, group21, group22);
PrintGroup(3, group31, group32);
Console.ReadKey();
}
static List
{
// figure out which points are used in the group and store them
int[,] indices = new int[connections.Count, 3];
var allIndices = new List
for (int i = 0; i < connections.Count; i++)
{
string[] items = connections[i].Split(',');
indices[i, 0] = int.Parse(items[0]);
indices[i, 1] = int.Parse(items[1]);
indices[i, 2] = int.Parse(items[2]);
if (!allIndices.Contains(indices[i, 0])) allIndices.Add(indices[i, 0]);
if (!allIndices.Contains(indices[i, 1])) allIndices.Add(indices[i, 1]);
if (!allIndices.Contains(indices[i, 2])) allIndices.Add(indices[i, 2]);
}
allIndices.Sort();
var dict = new Dictionary
for (int i = 0; i < allIndices.Count; i++) dict.Add(allIndices[i], i);
for (int i = 0; i < connections.Count; i++)
{
for (int j = 0; j < 3; j++) indices[i, j] = dict[indices[i, j]]; // remap actual indices to 0, 1, 2 etc
connections[i] = String.Format("{0}, {1}, {2},-1", indices[i, 0], indices[i, 1], indices[i, 2]);
}
var groupPoints = new List
foreach (int index in allIndices) groupPoints.Add(points[index]);
return groupPoints;
}
static void PrintGroup(int num, List
{
Console.WriteLine("-- GROUP {0} --", num);
Console.WriteLine("\nLIST1\n");
foreach (string point in points) Console.WriteLine(point);
Console.WriteLine("\nLIST2\n");
foreach (string connection in connections) Console.WriteLine(connection);
Console.WriteLine();
}
static void Subdivide(int index, double area, List
{
string[] items = triangles[index].Split(',');
int[] indices = new int[6];
for (int i = 0; i < 3; i++) indices[i] = int.Parse(items[i]);
Point v0 = Point.FromString(points[indices[0]]);
Point v1 = Point.FromString(points[indices[1]]);
Point v2 = Point.FromString(points[indices[2]]);
Point v3 = Point.Middle(v0, v1);
Point v4 = Point.Middle(v1, v2);
Point v5 = Point.Middle(v2, v0);
int count = points.Count;
points.Add(v3.ToString());
points.Add(v4.ToString());
points.Add(v5.ToString());
indices[3] = count;
indices[4] = count + 1;
indices[5] = count + 2;
triangles.Insert(index + 1, String.Format("{0}, {1}, {2},-1", indices[0], indices[3], indices[5]));
triangles.Insert(index + 2, String.Format("{0}, {1}, {2},-1", indices[3], indices[4], indices[5]));
triangles.Insert(index + 3, String.Format("{0}, {1}, {2},-1", indices[3], indices[1], indices[4]));
triangles.Insert(index + 4, String.Format("{0}, {1}, {2},-1", indices[5], indices[4], indices[2]));
triangles.RemoveAt(index);
area /= 4.0;
areas.Insert(index + 1, area);
areas.Insert(index + 2, area);
areas.Insert(index + 3, area);
areas.Insert(index + 4, area);
areas.RemoveAt(index);
codes.Insert(index + 1, Point.GetCode(Point.FromString(points[indices[0]]), Point.FromString(points[indices[3]]), Point.FromString(points[indices[5]])));
codes.Insert(index + 2, Point.GetCode(Point.FromString(points[indices[3]]), Point.FromString(points[indices[4]]), Point.FromString(points[indices[5]])));
codes.Insert(index + 3, Point.GetCode(Point.FromString(points[indices[3]]), Point.FromString(points[indices[1]]), Point.FromString(points[indices[4]])));
codes.Insert(index + 4, Point.GetCode(Point.FromString(points[indices[5]]), Point.FromString(points[indices[4]]), Point.FromString(points[indices[2]])));
codes.RemoveAt(index);
}
static bool AreTrianglesEqual(string triangle1, string triangle2)
{
string[] items1 = triangle1.Split(',');
int[] vertices1 = new int[3];
for (int i = 0; i < 3; i++) vertices1[i] = int.Parse(items1[i]);
Array.Sort(vertices1);
string[] items2 = triangle2.Split(',');
int[] vertices2 = new int[3];
for (int i = 0; i < 3; i++) vertices2[i] = int.Parse(items2[i]);
Array.Sort(vertices2);
if ((vertices1[0] == vertices2[0]) && (vertices1[1] == vertices2[1]) && (vertices1[2] == vertices2[2])) return true;
return false;
}
}
VulpesPosted Oct 20, 2014, 10:59 AM
I also found a small print out error in the previous code (list1 should have been list3) which I've corrected:
The output is:
If you're happy with that, we can then combine it with the code which is working out the contours.
HappyCode HappyCodePosted Oct 20, 2014, 7:51 AM
I think we are nearly there. Yes, the result is the desired one. The only thing is that we will not have duplicated points in LIST 1 or 2 (even though is worth checking just in case). The duplicated points will appear for sure after the subdivision phase, in groups 11, 12, 21, 22, 31, 32. So I think we should either modify the subdivision bit of the code to avoid such duplicated points or just do the cleaning of these groups after the subdivision using your last code highlighted.
What do you think?
See attached a graphic example for clarity.
Thank you!
VulpesPosted Oct 17, 2014, 7:45 PM
I've gone back to the last code we had which included the 'raw' points data, incorporated your new example and inserted some code (highlighted) to 'clean' the lists of any duplicates.
As I wasn't sure what to do about list3 and whether I should also check list2 to see if any duplicate triangles had now revealed themselves, I've just 'returned' from the program after removing the duplicates and haven't therefore done the rest of the calculations at this stage:
I thought I'd also test the code by creating another duplicate in the middle of list1 so I changed the second point from '50 50 0' to '0 50 0' so it is now duplicating the first point.
The output which shows that the code is renumbering the rest of the points correctly is:
I'll wait now to see if you're happy with this before doing anything else.
HappyCode HappyCodePosted Oct 17, 2014, 2:17 PM
Hello Vulpes,
I think I have identified the source of the problem after creating many different examples.
Basically what happens is that if we have repeated points in our lists (e.g. one triangle vertex is called "5" for one triangle and the same vertex point is called "9" for another triangle) then we get the problem. To solve such problem will require "cleaning" all the repeated points in all the groups before starting with the contour extraction. Once this issue is solved, everything should work ok.
Actually the previous code (not the last one you posted) would work for all the cases if such points are deleted.
I have attached a better explanation and the code for a simple example.
Would you be so kind to help me doing "the housekeeping" of the lists?
Many thanks for all your help!!
HappyCode HappyCodePosted Oct 17, 2014, 7:16 AM
VulpesPosted Oct 16, 2014, 8:14 PM
The basic problem is that to exclude the internal triangles you need to know whether they're touching a contour or not but we can't do that until we've determined what the contours are - a classic 'chicken and egg' scenario!
I've come up with a (sort of) solution which is to assume that the contours will be mutually exclusive i.e. will have no vertices in common which allows us to exclude points dynamically. This is quite easy to code (highlighted) and gives the desired result for your new example:
So, if we substitute this data:
the contours output now are:
In fact this is even better than before as the second contour is closed - previously it ended at point 0.
However, it's less good for some of the point sets we've tested - there's a lot more unclosed contours - and you have produced some examples where the contours definitely did have vertices in common so it's certainly not a general solution.
The question is whether we can identify in advance where it's acceptable for the contours to be unconnected and where it isn't so we can 'turn off' the extra bit of code.
HappyCode HappyCodePosted Oct 14, 2014, 1:12 PM
Triangles that are isolated must be taken out (no vertices touching the contours). In fact, everything that is not in contact with the contours must be deleted (edges of the triangle or points of the triangle). The possible situations are as follows:
- If a triangle is touching a contour by one or two of its edges, then the edge(s) and the edge(s) points must be considered. The other edge(s) and points must be deleted. THE CURRENT CODE CAN DO THIS AT THE MINUTE.
- If a triangle is not touching the contour at all (isolated), then nothing is to be considered. Everything must be deleted. THE CURRENT CODE CAN DO THIS AT THE MINUTE.
- If a triangle is touching a contour by one, two or three of its vertices (without being the edges in contact with the contour), then this point(s) must be considered. All the edges and the other two points must be deleted. THE CURRENT CODE CANNOT DO THIS AT THE MINUTE
(SEE ATTACHED "Cases.png" representing this).
Please see also attached the numerical example as requested (including the code and explanation of the above comment).
Is this any clearer?
Many thanks!
Vithal WadjePosted Oct 13, 2014, 2:10 PM
VulpesPosted Oct 13, 2014, 1:51 PM
I think we may have been talking at cross purposes here as what I've been describing as an isolated triangle is one which doesn't have any vertices in common with the other triangles in the group.
It appears from the diagrams that the triangles to be removed are not isolated in that sense since they do have edges in common.
Can you perhaps do a simple numerical example like the one you did originally?
HappyCode HappyCodePosted Oct 13, 2014, 12:46 PM
Thank you Vulpes.
I agree with you regarding groups 31 and 32, chains of collinear points rather than triangles should work much better than triangles. Connecting each point to its closest would be sufficient I think (as indicated in the previous attachment).
There is still a problem regarding the first issue. The new code actually works if we have triangles isolated but it does not clear up all the internal triangles in order to get the contours only.
Please see attached the code with the lists for such example and a picture of the result obtained in the word document.
Do you have any suggestions?
Many thanks!
VulpesPosted Oct 13, 2014, 11:42 AM
So 8, 9, 10, 8, -1 has now gone.
However, the bad news is that it's made the 'collinear facets' problem worse :(
If you replace the set of arrays and group12 with the following:
the output now is:
So 0, 1, 3, 0, -1 has gone altogether because it appears to be isolated from the other triangles.
It's therefore clear that we need a different approach to solve this problem.
The difficulty is that the program has no way of knowing that vertex 2 is collinear with 0,1 and 3. Even if it could deduce this from the order of the vertices, it still wouldn't know that 4 and 5 followed on from 3 - for all it knows they could be at right angles to 0,1, and 3 but still be collinear with 2.
HappyCode HappyCodePosted Oct 13, 2014, 6:36 AM
Many thanks for your help on this first issue (small changes make big differences :-)).
However I still need to delete the internal triangles (see attached).
I am looking forward to see what you can come out with for this two issues.
Again thank you!
VulpesPosted Oct 10, 2014, 1:10 PM
This wasn't in fact anything to do with the isolated triangles but was simply due to the fact that we weren't removing pairs until after each contour had been found. This was sometimes resulting in an infinite loop when previously used pairs were referenced again.
Anyway I've fixed it now and highlighted the change. I've also inserted a bank line between each contour so they're easier to read (also highlighted):
In view of this, you might want to reconsider excluding isolated triangles from the results.
I'll look into the other problem when I have more time.
HappyCode HappyCodePosted Oct 10, 2014, 10:25 AM
Many thanks for your kind help Vulpes, you always have an answer pointing in the right direction!
I have been testing the code in some examples and in general terms it works very well. However I do not want the isolated triangles to form a contour as I have found some problems that might be related to this (I think).
I have attached a document showing these two examples (.tif) and the code including the lists already in place in two separated word documents for each example.
Just to clarify what ultimately I would like to get is the following:
Group11_Contour: includes only the points in Group11 that are on the contour.
Group12_Contour: includes the connections referring to points in Group11_Contour. This is what the current code is doing (needs to correct the first problem as indicated in the document attached).
Group21_Contour: includes only the points in Group11 that are on the contour.
Group22_Contour: includes the connections referring to points in Group21_Contour. This is what the current code is doing (needs to correct the first problem as indicated in the document attached).
Group31_Contour: includes only the points in Group31 that are on the contour.
Group32_Contour: includes the connections referring to points in Group31_Contour. It needs to correct the second problem as indicated in the document attached).
I presume that it will be a matter of obtaining first Groups 12, 22 and 32 and then relate them to groups 11, 21 and 31 respectively to get only the points in the contour.
If you could have a look at it and modify the code accordingly it would be much appreciated.
Thank you very much!!
VulpesPosted Oct 8, 2014, 1:01 PM
With the help of a Pair struct, the first part was quite easy but the second part less so.
For now, rather than getting bogged down linking this to the existing program before it's fully tested, I've coded it as a separate program as follows:
With regard to the special case, as the program stands, if there is a pair (0, 1) and then two (or more) pairs starting with 1, then a separate contour would be created for the additional paths. However, since pairs are removed after they've been used for a contour, then the second one would begin with 1 rather than 0.
Note also that if you had a triangle which was isolated from the others (say 8, 9, 10, -1), then this would form a contour on its own (8, 9, 10, 8, -1) which may not be what you want.
HappyCode HappyCodePosted Oct 7, 2014, 11:45 AM
Hello!
I have to develop a little more the code we discuss about in the previous post and I have no idea about how to proceed. I need to add an "additional feature" to this code so I can identify contours within the list of points in Group11, Group21 and Group31. In the previous code we saw that the connectivity of these points (forming triangles) is indicated in Group12, Group22 and Group32 respectively.
I propose a strategy (see example attached) to solve the contouring problem that I think it will work but I am not too sure about its implementation. Can you help me modifying the code?
In line with the example in the picture I have modified the previous code (also attached) "deactivating" the subdivision bit to make things simpler. Please note that I have changed the input lists 1, 2 and 3 so we only have Group11 and Group12 as outputs (the other groups are void for the new input lists) for this example.
However the final code will need to work for all the groups in general terms.
This is much better explained in the picture attached.
At the bottom of the picture I have also added a special case I might have to deal with. The most important problem for me today is how to modify the code but if you could help me with this special case as well that would be great.
Many thanks!
HappyCode HappyCodePosted Jul 17, 2014, 4:47 AM
VulpesPosted Jul 11, 2014, 8:19 PM
I've added some more static methods to the struct to make some of the calcs a bit easier.
It's difficult to be sure that it's working properly so I'd check it carefully.
Slightly worried that there are no longer any entries in Group 3 though it may be due to the subdivision:
HappyCode HappyCodePosted Jul 11, 2014, 9:32 AM
Hello,
Yes you are right in everything!
Thanks!!
VulpesPosted Jul 10, 2014, 8:08 PM
On the second picture, the edge between v0 and v2, it gives the midpoint as (v2 + v3)/2.
As there's no v3 I imagine this is a mistake and it should be (v0 + v2)/2?
on the third picture, it says that as the area of the individual triangles is 3 which is greater than 2.5 that we can stop splitting. However, I thought that the object of the exercise was to get the triangle areas below the target area of 2.5 and so a further split would be necessary?
It's a long time since I did any 3D geometry and I wonder from your own knowledge of the subject whether you know if the method of splitting is such that the areas of the 4 individual triangles will always be the same i.e. a quarter of the original triangle area? If this is so, then we won't need to check the area of each one.
VulpesPosted Jul 10, 2014, 12:22 PM
As it's something which requires some thought, I'll try and get back to you later when I have more time.
HappyCode HappyCodePosted Jul 10, 2014, 12:18 PM
Sorry I think I had a problem uploading this (I did not realised I had to do it as a zip file, just inserted the picture before)
Is it ok now?
VulpesPosted Jul 10, 2014, 12:14 PM
HappyCode HappyCodePosted Jul 10, 2014, 11:17 AM
Hello,
Please see the picture with the example attached now.
The area is given by the determinant in the struct:
Area of the triangle = 0.5 * determinant of the struct
LIST1 is a set of xyz points and LIST2 shows how the points are connected in list 1. In other words, List2 tell us how the triangles are formed (e.g point 0 -> point 3 -> point 1). . The value -1 in LIST2 needs to be there but it does not give any information. Finally List 3 is formed from List 1 and List 2 in a way that List 3 will contain a 1 if the points that forms the triangle are counterclockwise, a -1 if they are clockwise and 0 if they are collinear.
I think the attachment should explain this in a more graphical way.
Hope this is more clear now.
Thanks!!
VulpesPosted Jul 9, 2014, 8:17 PM
I assume you're talking here about a triangle in 3-dimensional space?
If so, I don't see the formula for the area in the struct.
Also when you talk about List3 I assume you mean the last of the four numbers added to List2.
HappyCode HappyCodePosted Jul 9, 2014, 1:22 PM
Hello!
I am carrying out a new task about subdividing triangles given by a set of points and to be honest I feel very lost. As you can see from the previous post I have 3 lists: LIST1, LIST2 and LIST3. LIST1 is a set of xyz points and LIST2 shows how the points are connected in list 1 (e.g point1 -> point 4 -> point 3). These 3 points form a triangle.
The idea was to get 3 groups of values:
· Group 1 including all the values that are clockwise (-1 in list3) with the points in order in list1 and showing connections starting from zero on in list2.
· Group 2 including all the values that are counterclockwise (1 in list3) with the points in order in list1 and showing connections starting from zero on in list2.
· Group 3 including all the values that are coolinear (0 in list3) with the points in order in list1 and showing connections starting from zero on in list2.
The way the points are organised (list 3) is determined by the area of the triangle that can be calculated thanks to the following struct:
struct Point
{
public readonly double X;
public readonly double Y;
public readonly double Z;
public Point(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
public override string ToString()
{
return String.Format("{0} {1} {2},", X, Y, Z);
}
private static double Determinant(Point a, Point b, Point c)
{
return (b.Y - a.Y) * (c.Z - a.Z) + (b.Z - a.Z) * (c.X - a.X) + (b.X - a.X) * (c.Y - a.Y) - (b.Z - a.Z) * (c.Y - a.Y) - (b.X - a.X) * (c.Z - a.Z) - (b.Y - a.Y) * (c.X - a.X);
}
public static bool CounterClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) > 0.0;
}
public static bool ClockWise(Point a, Point b, Point c)
{
return Determinant(a, b, c) < 0.0;
}
public static bool Collinear(Point a, Point b, Point c)
{
return Determinant(a, b, c) == 0.0;
}
}
So now I have to go one step forward. I need to carry out some recursive subdivision of the triangles and update the three lists (and the groups) based on that. In other words: the triangles cannot be greater than a target triangle area and if they are they will need to be split. So being the "target triangle area":
Target triangle area = (sum of the area of all triangles)/(number of triangles).
If the area of the triangle < target triangle area then we need to continue subdividing the triangle. In the end all triangles in the list (including the newly created must be less than the target area).
The three lists need to be updated including these newly created triangles and deleting the big triangles they form.
For example let's consider a single triangle (triangle 1) formed by Point A, Point B and Point C which is greater than the specified target area. Also we assume that 1 iteration is enough to get 3 triangles which area is less than the specified target area and therefore complies with our purpose. When we subdivide this triangle in 3 smaller triangles (triangle 1.1, triangle 1.2 and triangle 1.3) we need to eliminate triangle 1 from the lists and put the 3 subdividing triangles instead (1.1, 1.2 and 1.3) updating the lists 1, 2 and 3. I have attached a picture with an example that might help.
Do you have a suggestion about how the code can be modified to accomplish this?
Many thanks!
HappyCode HappyCodePosted May 19, 2014, 10:58 AM
HappyCode HappyCodePosted May 17, 2014, 2:53 PM
Many thanks for your answer. Yes you are right, I made a mistake, apologies. You understood perfectly what my problem is. The set of points in LIST1 that relates to indices 20, 21, 22, 23 and 24 in LIST 2 are:
and then the indices in LIST2 should be renumbered from 0 to 4 in this case.
And once again you are right, what Im looking for is to have LIST 1 and LIST2 for the three cases (1,-1,0) reading through LIST3. The reason of this is because I need to write a txt file with all the points classified as follows
blah blah...
Here I need to put List 1 (clockwise points)
blah blah
Here I need to put indices from list 2 (renumbered) that relate to -1 in list3
blah blah
Here I need to put List 1 (counter clockwise points)
blah blah
Here I need to put indices from list 2 (renumbered) that relate to 1 in list3
blah blah
Here I need to put List 1 (collinear points)
blah blah
Here I need to put indices from list 2 (renumbered) that relate to 0 in list3
Any ideas about how to deal with this?
Thank you very much!
VulpesPosted May 16, 2014, 2:26 PM
As I understand it, LIST1 is just a list of points with indices from 0 to 27.
The connections between these points appear in LIST2 and LIST3 shows whether the corresponding connection in LIST2 is a 1, -1 or 0.
So those connections in LIST2 which are collinear (i.e. 0) are these:
However, there's no sense in which these connections relate to the following set of points which just happen to have the same indices in LIST1 as the connections in LIST2:
Instead they relate to this set of points with indices 20, 21, 22, 23 and 24 which in order are:
So are you saying that Group 3 should show these points and that they should be renumbered 0 to 4 so that the corresponding entries in LIST2 are:
Also, if this is correct, do you want to show the results as two lists - one for LIST1 and the other for LIST2 - for each of the 3 groups?