I have a set of bounding boxes(rectangular) in a 3D space. The bounds of each box are computed and stored in a dictionary named "RegionBounds". Also, a set of points are populated in a List named "PointsToCategorize" Given a point(x,y,z) coordinates from the List populated and a bounding box to be checked in, i can check if the point is inside the box or not. The problem is, this is a big dataset. The number of points to be checked are like 1000 and the no of bounding boxes are like 250-300. So, if i loop through each bounding box for each given point; the total time it takes is like 5-6 minutes. Is there any efficient method that would do the process quicker ? If possible, a small code to do so would be great
public struct iBounds {
public double x1, x2;
public double y1, y2;
public double z1, z2;
}
public struct iPoint {
public double x,y,z
}
Dictionary<String, iBounds> RegionBounds = new Dictionary<String, iBounds>();
List<iPoint> PointsToCategorize = new List<iPoint>();
int no_of_bounding_boxes = 300;
int no_of_points_to_categorize = 1000;
for (int i = 1; i <= no_of_bounding_boxes; i++)
{
String boundingBoxName = "bound_" + i;
iBounds boundingBox = new iBounds
{
x1 = Computed By Some Other method and Formulas,
x2 = Computed By Some Other method and Formulas,
y1 = Computed By Some Other method and Formulas,
y2 = Computed By Some Other method and Formulas,
z1 = Computed By Some Other method and Formulas,
z2 = Computed By Some Other method and Formulas
};
RegionBounds.Add(boundingBoxName, boundingBox);
}
////////////Start of Output section /////////////////////////
for(int i= 1; i < = PointsToCategorize.Count; i++){
foreach(var pair in RegionBounds)
{
String myboxNmame = pair.Key;
iBounds myboxBounds = pair.Value;
Console.WriteLine(PointInside(PointsToCategorize[i],myboxBounds).ToString());
}
}
////////////// End of Output section //////////////////
private bool PointInside(iPoint mypoint, iBounds boxToBeCheckedIn)
{
if (mypoint.x > boxToBeCheckedIn.x1) && (mypoint.x < boxToBeCheckedIn.x2){
if (mypoint.y > boxToBeCheckedIn.y1) && (mypoint.y < boxToBeCheckedIn.y2){
if (mypoint.z > boxToBeCheckedIn.z1) && (mypoint.z < boxToBeCheckedIn.z2){
return true;
}
}
}else{
return false;
}
}
You may want to use a OcTree or a kD-tree data structure, which is way more efficient than iterating through all the boxes.
See also this article at the section 2-D orthogonal range searching, it has a very good resume of available techniques and algorithms, which are easily extendable to 3D
Related
It's very easy to generate normally distributed data with a desired mean and standard distribution:
IEnumerable<double> sample = MathNet.Numerics.Distributions.Normal.Samples(mean, sd).Take(n);
However with a sufficiently large value for n you will get values miles away from the mean. To put it into context I have a real world data set with mean = 15.93 and sd = 6.84. For this data set it is impossible to have a value over 30 or under 0, but I cannot see a way to add upper and lower bounds to the data that is generated.
I can remove data that falls outside of this range as below, but this results in the mean and SD for the generated sample differing significantly (in my opinion, probably not statistically) from the values I requested.
Normal.Samples(mean, sd).Where(x => x is >= 0 and <= 30).Take(n);
Is there any way to ensure that the values generated fall within a specified range without effecting the mean and SD of the generated data?
The following proposed solution relies on a specific formula for calculating the standard deviation relative to the bounds: the standard deviation has to be a third of the difference between the mean and the required minimum or maximum.
This first code block is the TruncatedNormalDistribution class, which encapsulates MathNet's Normal class. The main technique for making a truncated normal distribution is in the constructor. Note the resulting workaround that is required in the Sample method:
using MathNet.Numerics.Distributions;
public class TruncatedNormalDistribution {
public TruncatedNormalDistribution(double xMin, double xMax) {
XMin = xMin;
XMax = xMax;
double mean = XMin + (XMax - XMin) / 2; // Halfway between minimum and maximum.
// If the standard deviation is a third of the difference between the mean and
// the required minimum or maximum of a normal distribution, 99.7% of samples should
// be in the required range.
double standardDeviation = (mean - XMin) / 3;
Distribution = new Normal(mean, standardDeviation);
}
private Normal Distribution { get; }
private double XMin { get; }
private double XMax { get; }
public double CumulativeDistribution(double x) {
return Distribution.CumulativeDistribution(x);
}
public double Density(double x) {
return Distribution.Density(x);
}
public double Sample() {
// Constrain results lower than XMin or higher than XMax
// to those bounds.
return Math.Clamp(Distribution.Sample(), XMin, XMax);
}
}
And here is a usage example. For a visual representation of the results, open each of the two output CSV files in a spreadsheet, such as Excel, and map its data to a line chart:
// Put the path of the folder where the CSVs will be saved here
const string chartFolderPath =
#"C:\Insert\chart\folder\path\here";
const double xMin = 0;
const double xMax = 100;
var distribution = new TruncatedNormalDistribution(xMin, xMax);
// Densities
var dictionary = new Dictionary<double, double>();
for (double x = xMin; x <= xMax; x += 1) {
dictionary.Add(x, distribution.Density(x));
}
string csvPath = Path.Combine(
chartFolderPath,
$"Truncated Normal Densities, Range {xMin} to {xMax}.csv");
using var writer = new StreamWriter(csvPath);
foreach ((double key, double value) in dictionary) {
writer.WriteLine($"{key},{value}");
}
// Cumulative Distributions
dictionary.Clear();
for (double x = xMin; x <= xMax; x += 1) {
dictionary.Add(x, distribution.CumulativeDistribution(x));
}
csvPath = Path.Combine(
chartFolderPath,
$"Truncated Normal Cumulative Distributions, Range {xMin} to {xMax}.csv");
using var writer2 = new StreamWriter(csvPath);
foreach ((double key, double value) in dictionary) {
writer2.WriteLine($"{key},{value}");
}
I currently have a line graph in my C# program, and I have a min and max variable. If any the graph ever exceeds the max, or goes below the min, is there any built in way of displaying on the graph (such as a dot at the point) that the limit was passed, and display the x/y values for that point?
int max = 2000;
int min = 2000;
for (int i = 0; i < dgvLoadedValues.RowCount - 1; i++)
{
DateTime x = Convert.ToDateTime(dgvLoadedValues.Rows[i].Cells[0].Value.ToString());
try
{
float y = float.Parse(dgvLoadedValues.Rows[i].Cells[e.ColumnIndex].Value.ToString());
chart1.Series["Series1"].Points.AddXY(x, y);
}
catch
{
Console.WriteLine("Unable to plot point");
}
}
Code above simply shows values taken from a datagridview and displaying it into a line graph
Thank you
Unfortunately there seems to be no way to define such an automatic alert.
But as you know just when the DataPoints are added or bound you can set a Marker where necessary.
Here is a loop that does it after the fact in one go, but of course you can just as well set the markers as you add the points..:
foreach (DataPoint dp in chart1.Series[0].Points)
{
if (dp.YValues[0] < max && dp.YValues[0] > min ) continue;
dp.MarkerStyle = MarkerStyle.Circle;
dp.MarkerColor = Color.Red;
}
Or in your case:
try
{
float y = float.Parse(dgvLoadedValues.Rows[i].Cells[e.ColumnIndex].Value.ToString());
int i = chart1.Series["Series1"].Points.AddXY(x, y);
if (y < min || y > max)
{
chart1.Series["Series1"].Points[i].MarkerStyle = MarkerStyle.Circle;
chart1.Series["Series1"].Points[i].MarkerColor = Color.Red;
}
}
To clear a marker you can set its MarkerStyle = MarkerStyle.None.
Of course you could easily give the min and max points different colors..
Here is an example with the simple circle style, but there are others including images..:
To add the values in a label use a format like this:
dp.Label = "(#VALX{0.0} / #VAL{0.0})" ;
I have the following problem. I create a chart with migradoc in c#.
Suppose I have the following points for my xAxis:
20.4, 20.6, 30.6, 100.4, 200.3
The problem is that it sets every xpoint in the series on an equal distance in the chart.
While what I need is a graph who sets the xpoints on a relative distance. For example, the distance between points 20.6 and 30.6 needs to be way smaller than the distance between 30.6 and 100.4. (The points always differ, as do the number of points)
One way to make the distance good is to add extra points between the existing points. For example the first step is 0.2 extra, the second step is 10.0 extra. So I want to add for example 50 extra points between this step, so that the distance is relative the same.
This is the only thing I can come up with, can somebody give me some advice how to accomplish this? (Or another possible solution?)
This method worked out for me. I first made the distances relative:
Int64[] relAfstand = new Int64[afstand.Count()];
for(int i = 0; i < afstand.Count(); i++){
double tussenRel = Convert.ToDouble(afstand[i]);
double eindRel = Convert.ToDouble(afstand[afstand.Count()-1]);
double beginRel = Convert.ToDouble(afstand[0]);
double Rel = (((eindRel - beginRel) - (eindRel - tussenRel)) / (eindRel - beginRel));
relAfstand[i] = Convert.ToInt64((Rel)*100);
}
Then I converted the data to scale with relative with the same factor as the distances:
List<double> ConvertedData = new List<double>();
int c = 0;
int c2 = 1;
double steps = 0;
bool calcSteps = false;
bool calcDistance = false;
for (int i = 0; i < 100; i++) {
if (calcDistance == false) {
distance.Add(i);
}
if (relAfstand[c] == i) {
ConvertedData.Add(data[c]);
calcSteps = false;
c2 = 1;
c++;
}else {
if (calcSteps == false) {
steps = ((data[c] - data[c-1])/(relAfstand[c] - relAfstand[c-1]));
calcSteps = true;
}
ConvertedData.Add(data[c-1] + (steps * c2));
c2++;
}
}
calcDistance = true;
Probably not the best workaround, but it works. Since the percentages can come close together I scale both now with around 200-300 instead of 100.
Can anyone recommend an efficient port to CSharp of any of the public AABB/triangle intersection algorithms.
I've been looking at Moller's approach, described abstractly here, and if I were to port it, I would probably start from this C++ version. This C++ library by Mike Vandelay seems like it could also be a great starting point.
...or... any other "wheel" that can take a triangle of Vector3's and tell me if it intersects with an AABB), relatively efficiently.
There seem to be a variety of algorithms, but most seem to be written in c++, or just described abstractly in white papers and I need a c# specific implementation for our application. Efficiency is not key, but c# is. (though efficiency is obviously nice too of course ;p )
Any C# options, before I wade through a "math" port ;) would be greatly appreciated! Thanks.
For any two convex meshes, to find whether they intersect, you need to check if there exist a separating plane. If it does, they do not intersect. The plane can be picked from any face of either shape, or the edge cross-products.
The plane is defined as a normal and an offset from Origo. So, you only have to check three faces of the AABB, and one face of the triangle.
bool IsIntersecting(IAABox box, ITriangle triangle)
{
double triangleMin, triangleMax;
double boxMin, boxMax;
// Test the box normals (x-, y- and z-axes)
var boxNormals = new IVector[] {
new Vector(1,0,0),
new Vector(0,1,0),
new Vector(0,0,1)
};
for (int i = 0; i < 3; i++)
{
IVector n = boxNormals[i];
Project(triangle.Vertices, boxNormals[i], out triangleMin, out triangleMax);
if (triangleMax < box.Start.Coords[i] || triangleMin > box.End.Coords[i])
return false; // No intersection possible.
}
// Test the triangle normal
double triangleOffset = triangle.Normal.Dot(triangle.A);
Project(box.Vertices, triangle.Normal, out boxMin, out boxMax);
if (boxMax < triangleOffset || boxMin > triangleOffset)
return false; // No intersection possible.
// Test the nine edge cross-products
IVector[] triangleEdges = new IVector[] {
triangle.A.Minus(triangle.B),
triangle.B.Minus(triangle.C),
triangle.C.Minus(triangle.A)
};
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
{
// The box normals are the same as it's edge tangents
IVector axis = triangleEdges[i].Cross(boxNormals[j]);
Project(box.Vertices, axis, out boxMin, out boxMax);
Project(triangle.Vertices, axis, out triangleMin, out triangleMax);
if (boxMax <= triangleMin || boxMin >= triangleMax)
return false; // No intersection possible
}
// No separating axis found.
return true;
}
void Project(IEnumerable<IVector> points, IVector axis,
out double min, out double max)
{
double min = double.PositiveInfinity;
double max = double.NegativeInfinity;
foreach (var p in points)
{
double val = axis.Dot(p);
if (val < min) min = val;
if (val > max) max = val;
}
}
interface IVector
{
double X { get; }
double Y { get; }
double Z { get; }
double[] Coords { get; }
double Dot(IVector other);
IVector Minus(IVector other);
IVector Cross(IVector other);
}
interface IShape
{
IEnumerable<IVector> Vertices { get; }
}
interface IAABox : IShape
{
IVector Start { get; }
IVector End { get; }
}
interface ITriangle : IShape {
IVector Normal { get; }
IVector A { get; }
IVector B { get; }
IVector C { get; }
}
A good example is the box (±10, ±10, ±10) and the triangle (12,9,9),(9,12,9),(19,19,20). None of the faces can be used as a separating plane, yet they do not intersect. The separating axis is <1,1,0>, which is obtained from the cross product between <1,0,0> and <-3,3,0>.
I noticed a small bug in this implementation which leads to false negatives.
If your triangle has one edge parallel to one axis (for example (1, 0, 0)), then you will have a null vector when computing
triangleEdges[i].Cross(boxNormals[j])
This will lead to equality in the test below and give you a false negative.
replace <= and >= by < and > at line
if (boxMax <= triangleMin || boxMin >= triangleMax)
(strict comparers to remove those cases).
Works well except for that!
Thank you
I am required to create a program which reads in data from a .cvs file, and use these (x, y and z) values for a series of calculations.
I read in the file as a string, and then split this into 3 smaller strings for x, y and z.
The x, y and z coordinates represents the x and y coordinates of the contours of a lake, and the depth (z).
One of the calculations which I have to do, is to calculate the surface area of the lake, using the formula (x[i]*y[i+1])-(x[i+1]*y[i]), where z(depth) = 0.
I can get my code to run perfectly, up until the x[i+1] and y[i+1], where it keeps giving me a value of 0.
Can someone please tell me how to fix this?
Here is my code;
{
string[] ss = File.ReadAllLines(#"C:File.csv");
for (int i = 1; i < ss.Length; i++)
{
string[] valuesAsString = ss[i].Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
double[] X = new double[valuesAsString.Length];
double[] Y = new double[valuesAsString.Length];
double[] Z = new double[valuesAsString.Length];
for (int n = 0; n < 1; n++)
{
X[n] = double.Parse(valuesAsString[0]);
Y[n] = double.Parse(valuesAsString[1]);
}
do
{
double SurfaceArea = (X[n] * Y[n + 1]) - (X[n + 1] * Y[n]);
Console.WriteLine(SurfaceArea);
}
while (Z[n] == 0);
}
}
Ok, im not sure if i got it right, so you if you would take a look to what i did and tell me if its of any help.
After reviewng it a little i came up with the following:
A class for the values
public class ValueXyz
{
public double X { get; set; }
public double Y { get; set; }
public int Z { get; set; }
}
A class to manange the calculation:
public class SurfaceCalculator
{
private ValueXyz[] _valuesXyz;
private double _surface;
private readonly string _textWithValues;
public SurfaceCalculator(string textWithValues)
{
_textWithValues = textWithValues;
SetValuesToCalculate();
}
public double Surface
{
get { return _surface; }
}
public void CalculateSurface()
{
for (var i = 0; i < _valuesXyz.Length; i++)
{
if (_valuesXyz[i].Z == 0)
_surface = (_valuesXyz[i].X*_valuesXyz[i + 1].Y) - (_valuesXyz[i + 1].X*_valuesXyz[i].Y);
}
}
private void SetValuesToCalculate()
{
var valuesXyz = _textWithValues.Split(' ');
_valuesXyz = valuesXyz.Select(item => new ValueXyz
{
X = Convert.ToDouble(item.Split(',')[0]),
Y = Convert.ToDouble(item.Split(',')[1]),
Z = Convert.ToInt32(item.Split(',')[2])
}).ToArray();
}
}
So now your client code could do somethin like:
[TestMethod]
public void TestSurfaceCalculatorGetsAValue()
{
//var textWithValues = File.ReadAllText(#"C:File.csv");
var textWithValues = "424.26,424.26,0 589.43,231.46,0 720.81,14.22,1";
var calculator = new SurfaceCalculator(textWithValues);
calculator.CalculateSurface();
Assert.IsNotNull(calculator.Surface);
}
I'm not very sure i got the idea correct of how to implement the formula, but i just wanted to expose an alternative you can use, you can never have to many ways of doing one thing :).
Cheers.
By the way part of the intent i had, was not tying up your funcionality to the csv in case your source for the text in the future would change.
Step through your code in the debugger. Pay special attention to tbe behavior of the line
for (int n = 0; n < 1; n++)
This loop will execute how many times? What will the value of n be during each iteration through the loop?
Well, one thing i noticed is when you're setting your X, Y, Z vars, you're setting it to the Length of the array object instead of it's value - is that intentional?
Put a debug break on the line with:
double SurfaceArea = (X[n] * Y[n + 1]) - (X[n + 1] * Y[n]);
and check the datatype of "X", "Y" and "Z"
I've had problems in the past where it tries to calculate them as strings (because it took it out of the data source as strings). I ended up fixing it by adding CInt() to each of the variables (or Convert.ToInt32();).
Hope this helps.
As this looks like it might be a homework problem, I am trying not to give a direct solution in my answer, but I see a number of questionable parts of your code that you should examine.
Why are X, Y, Z arrays? You are creating a new array each time through the outer loop, setting the length of the array to the number of elements in the line, then only assigning a value to one element of X and Y, and never assigning Z to anything.
As phoog suggests in his answer, what is the purpose of: for (int n = 0; n < 1; n++)?
What are you trying to accomplish with the do-while loop? As it has been mentioned in the comments by Mr Skeet, X[n], Y[n], Z[n] don't exist because n does not exist outside of the loop it is declared for. Even if it did exist Z[n] will always be zero because you never assign anything to the Z array after it is initialized, so the do-while loop will run forever.