private void InitializeThinSearch(double thinThickness, double xBasis, double yBasis, List <SearchPoint> pattern) { // simply loop through all pixels that could possibly be closer than // a half pixel from a line through (0,0) and parallel to the basis vector int ijmax = (int)(thinThickness + 0.5) + 1; for (int j = -ijmax; j <= ijmax; j++) { for (int i = -ijmax; i <= ijmax; i++) { double x = (double)i; double y = (double)j; // this point must be in same direction as the basis vector. use dot product if (x * xBasis + y * yBasis > 0) { // this point must be within a half pixel of the basis vector if (NuGenMath.DistanceToLine(x, y, 0.0, 0.0, xBasis, yBasis) <= 0.5) { // add new search point SearchPoint p = new SearchPoint(i, j, Math.Sqrt(i * i + j * j)); pattern.Add(p); } } } } pattern.Sort(); }
// return the area and distance geometry info for this pointset private void GeometryInfoArea(ref int pNextRow, List <NuGenGeometryWindowItem> rInfo) { double funcArea = 0.0, polyArea = 0.0; int i = 0, nPoints = points.Count; if (nPoints > 2) { double[] x = new double[nPoints]; double[] y = new double[nPoints]; foreach (NuGenPoint point in points) { x[i] = point.XThetaGraph; y[i] = point.YRGraph; i++; } funcArea = NuGenMath.FunctionArea(nPoints, x, y); polyArea = NuGenMath.PolygonArea(nPoints, x, y); } rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 0, "FuncArea")); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 1, funcArea.ToString())); pNextRow++; rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 0, "PolyArea")); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 1, polyArea.ToString())); pNextRow++; }
// compute pleasing gridMeshSettings display line settings, returning true if successful public void InitGridMesh(ref GridMeshSettings gridSettings) { gridSettings.initialized = false; if (!transform.ValidAxes) { return; } gridSettings.gridSetX = GridSet.AllButStep; gridSettings.gridSetY = GridSet.AllButStep; double xThetaMin = 0.0, xThetaMax = 0.0, yRMin = 0.0, yRMax = 0.0; pointSets.PointSetGraphLimits(coordSettings, transform, ref xThetaMin, ref xThetaMax, ref yRMin, ref yRMax); NuGenMath.AxisScale(xThetaMin, xThetaMax, (coordSettings.xThetaScale == Scale.Linear), out gridSettings.startX, out gridSettings.stopX, out gridSettings.stepX, out gridSettings.countX); NuGenMath.AxisScale(yRMin, yRMax, (coordSettings.yRScale == Scale.Linear), out gridSettings.startY, out gridSettings.stopY, out gridSettings.stepY, out gridSettings.countY); gridSettings.initialized = true; }
private static List <GridlineScreen> MakeGridR(NuGenScreenTranslate transform, CoordSettings coordSettings, GridMeshSettings gridMeshSettings) { int i; int xStartS = 0, yStartS = 0, xStopS = 1, yStopS = 1; double rG = gridMeshSettings.startY; GridlineScreen gridline; List <GridlineScreen> gridlines = new List <GridlineScreen>(); for (i = 0; i < gridMeshSettings.countY; i++) { gridline = new GridlineScreen(); // for polar coordinates we simply piecewise define the elliptical arc until motivated // to implement a better drawing algorithm. segments will be evenly spaced in angle // some pdf documents describing alternative algorithms are found in the doc directory. // it would have been elegant to use QCanvasEllipses but those are axis-aligned. double delta = AngleSpacing(coordSettings.thetaUnits); bool first = true; for (double angle = gridMeshSettings.startX; angle < gridMeshSettings.stopX; angle += delta) { transform.XThetaYRToScreen(coordSettings, angle, rG, out xStopS, out yStopS); if (first) { xStartS = xStopS; yStartS = yStopS; } else { if (NuGenMath.VectorMagnitude(xStopS - xStartS, yStopS - yStartS, 0.0) >= pixelSpacing) { gridline.Start.X = xStartS; gridline.Start.Y = yStartS; gridline.Stop.X = xStopS; gridline.Stop.Y = yStopS; gridline.R = true; xStartS = xStopS; yStartS = yStopS; } } first = false; } if (coordSettings.yRScale == Scale.Linear) { rG += gridMeshSettings.stepY; } else { rG *= gridMeshSettings.stepY; } } return(gridlines); }
bool PointIsCloseToLine(double xLeft, double yLeft, double xInt, double yInt, double xRight, double yRight) { double xProj, yProj; NuGenMath.ProjectPointOnToLine(xInt, yInt, xLeft, yLeft, xRight, yRight, out xProj, out yProj); return( (xInt - xProj) * (xInt - xProj) + (yInt - yProj) * (yInt - yProj) < 0.5 * 0.5); }
private static void TestMatrixMultiplication() { double[][] mat1 = { new double[3] { 3, 3, 3 }, new double[3]{ 3, 3, 3 }, new double[3]{ 3, 3, 3 } }; double[,] mat2 = { { 3, 3, 3 }, { 3, 3, 3 }, { 3, 3, 3 } }; double[,] res = new double[3, 3]; NuGenMath.MatrixMultiply3x3(res, mat1, mat2); }
//Converts a pair of screen coordinates to graph coordinates private void ScreenToGraph(double xScreen, double yScreen, out double xGraph, out double yGraph) { double[] rScreen = new double[3]; double[] rGraph = new double[3]; rScreen[0] = xScreen; rScreen[1] = yScreen; rScreen[2] = 1.0; NuGenMath.MatrixMultiply3x1(rGraph, screenToGraph, rScreen); xGraph = rGraph[0]; yGraph = rGraph[1]; }
// add point to pointset, not worrying about keeping the pointset single valued (which is for // curves only). if new point is on the line between two points then insert it between // those two points (desired behavior for curve and measure pointsets, which happens to not affect // axes and scale pointsets) public void AddPoint(NuGenPoint point) { // insert point between two other points if it lies on the line between the two points const int LineEpsilonPixels = 2; int index = 0; double x = point.XScreen; double y = point.YScreen; NuGenPoint pOld = null; foreach (NuGenPoint pNew in points) { double xNew = pNew.XScreen; double yNew = pNew.YScreen; if (pOld != null) { double xOld = pOld.XScreen; double yOld = pOld.YScreen; double xProj, yProj; NuGenMath.ProjectPointOnToLine(x, y, xOld, yOld, xNew, yNew, out xProj, out yProj); double diff = Math.Sqrt((x - xProj) * (x - xProj) + (y - yProj) * (y - yProj)); if (diff < LineEpsilonPixels) { points.Insert(index, point); point.PointSet = this; RemoveLine(pOld, pNew); AddLine(pOld, point); AddLine(point, pNew); return; } } pOld = pNew; index++; } points.Add(point); point.PointSet = this; if (pOld != null) { AddLine(pOld, point); } }
public void XThetaYRToScreen(CoordSettings coordSettings, double xTheta, double yR, out double xScreen, out double yScreen) { if (ValidAxes || ValidScale) { LinearToLog(coordSettings, ref xTheta, ref yR); double xGraph, yGraph; ConvertToCartesian(NuGenMath.mmUnitize(coordSettings), xTheta, yR, out xGraph, out yGraph); GraphToScreen(xGraph, yGraph, out xScreen, out yScreen); } else { xScreen = xTheta; yScreen = yR; } }
private void GeometryInfoDistancePass1(int i, NuGenPoint pointNext, double xLast, double yLast, double x, double y, double distance, double totalDistance, ref int pNextRow, List <NuGenGeometryWindowItem> rInfo) { double xNext = pointNext.XThetaGraph; double yNext = pointNext.YRGraph; double pcDistance; if (totalDistance <= 0.0) { pcDistance = 100.0; } else { pcDistance = 100.0 * distance / totalDistance; } double[] r1 = new double[3]; double[] r2 = new double[3]; r1[0] = xLast - x; // xLast from last point of pass 0 is used by first point in pass 1 r1[1] = yLast - y; // yLast from last point of pass 0 is used by first point in pass 1 r1[2] = 0.0; r2[0] = xNext - x; r2[1] = yNext - y; r2[2] = 0.0; double angle = NuGenMath.Angle(r1, r2) * 180.0 / Math.PI; double distDiff = totalDistance - distance; double pcDiff = 100.0 - pcDistance; rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 0, x.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 1, y.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 2, i.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 3, distance.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 4, pcDistance.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 5, distDiff.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 6, pcDiff.ToString())); rInfo.Add(new NuGenGeometryWindowItem(pNextRow, 7, angle.ToString())); pNextRow++; }
//Converts a screen coordinate pair to a graph coordinate pair public void ScreenToXThetaYR(CoordSettings coordSettings, double xScreen, double yScreen, out double xTheta, out double yR) { if (ValidAxes || ValidScale) { double xGraph, yGraph; ScreenToGraph(xScreen, yScreen, out xGraph, out yGraph); ConvertFromCartesian(NuGenMath.mmUnitize(coordSettings), xGraph, yGraph, out xTheta, out yR); LogToLinear(coordSettings, ref xTheta, ref yR); } else { // until transform is defined just use identity matrix. this branch is only defined when trying // to connect points with lines, and the transform is not currently defined xTheta = xScreen; yR = yScreen; } }
//Converts a pair of graph coordinates to screen coordinates private void GraphToScreen(double xGraph, double yGraph, out double xScreen, out double yScreen) { if (!(ValidAxes || ValidScale)) { throw new InvalidOperationException("No valid axes or scale"); } double[] rScreen = new double[3]; double[] rGraph = new double[3]; rGraph[0] = xGraph; rGraph[1] = yGraph; rGraph[2] = 1.0; NuGenMath.MatrixMultiply3x1(rScreen, graphToScreen, rGraph); xScreen = rScreen[0]; yScreen = rScreen[1]; }
public static void Test() { double[,] r_screen = new double[3, 3]; double[][] r_graph = { new double[3], new double[3], new double[3] }; r_screen[0, 0] = 69; r_screen[1, 0] = 68; r_screen[2, 0] = 626; r_screen[0, 1] = 577; r_screen[1, 1] = 84; r_screen[2, 1] = 576; r_screen[0, 2] = 1; r_screen[1, 2] = 1; r_screen[2, 2] = 1; r_graph[0][0] = 0; r_graph[1][0] = 0; r_graph[2][0] = 30; r_graph[0][1] = -1; r_graph[1][1] = 1; r_graph[2][1] = -1; r_graph[0][2] = 1; r_graph[1][2] = 1; r_graph[2][2] = 1; double[,] s2g = new double[3, 3]; double[,] g2s = new double[3, 3]; ScreenToGraph(r_graph, r_screen, s2g, g2s); double[] rScreen = new double[3]; double[] rGraph = new double[3]; rScreen[0] = 300; rScreen[1] = 300; rScreen[2] = 1.0; NuGenMath.MatrixMultiply3x1(rGraph, s2g, rScreen); double xGraph = rGraph[0]; double yGraph = rGraph[1]; }
//Computes the graph area based on the scale bar definition public int ComputeScaleTransformation(CoordSettings coordSettings, NuGenPointSet pointSet) { double[][] r_graph = { new double[3], new double[3], new double[3] }; double[,] r_screen = new double[3, 3]; List <NuGenPoint> points = pointSet.Points; scalePointCount = 0; validScale = false; foreach (NuGenPoint point in points) { if (scalePointCount == 2) { throw new InvalidOperationException("Can not define more scale bar points"); } if (point.GraphCoordsAreDefined()) { double xGraph, yGraph; ConvertToCartesian(NuGenMath.mmUnitize(coordSettings), point.XThetaGraph, point.YRGraph, out xGraph, out yGraph); r_graph[0][scalePointCount] = xGraph; r_graph[1][scalePointCount] = yGraph; r_graph[2][scalePointCount] = 1.0; r_screen[0, scalePointCount] = point.XScreen; r_screen[1, scalePointCount] = point.YScreen; r_screen[2, scalePointCount] = 1.0; scalePointCount++; } } if (scalePointCount == 2) { // create virtual third point along a virtual axis which is just orthogonal to the virtual // axis between the other two points. assumes all points are in same z-value (which is 1.0) plane double[] axis1 = new double[3]; double[] axis2 = new double[3]; double[] z = { 0.0, 0.0, 1.0 }; axis1[0] = r_graph[0][1] - r_graph[0][0]; axis1[1] = r_graph[1][1] - r_graph[1][0]; axis1[2] = r_graph[2][1] - r_graph[2][0]; NuGenMath.VectorCrossProduct(z, axis1, axis2); r_graph[0][2] = r_graph[0][0] + axis2[0]; r_graph[1][2] = r_graph[1][0] + axis2[1]; r_graph[2][2] = 1.0; axis1[0] = r_screen[0, 1] - r_screen[0, 0]; axis1[1] = r_screen[1, 1] - r_screen[1, 0]; axis1[2] = r_screen[2, 1] - r_screen[2, 0]; NuGenMath.VectorCrossProduct(z, axis1, axis2); r_screen[0, 2] = r_screen[0, 0] + axis2[0]; r_screen[1, 2] = r_screen[1, 0] + axis2[1]; r_screen[2, 2] = 1.0; int rtnS2G = NuGenMath.ScreenToGraph(r_graph, r_screen, screenToGraph, graphToScreen); // log to linear transformation is not allowed when using scale bar since it would // be such an extremely rare special case that its benefit would be minimal, especially // since it would only confuse the majority of users who would not be expecting it validScale = (rtnS2G == NuGenMath.SUCCESS); switch (rtnS2G) { case NuGenMath.SUCCESS: return(NuGenMath.SUCCESS); case NuGenMath.BAD_GRAPH_COORDINATES: return(NuGenMath.BAD_GRAPH_COORDINATES); case NuGenMath.BAD_SCREEN_COORDINATES: return(NuGenMath.BAD_SCREEN_COORDINATES); } } return(NuGenMath.SUCCESS); }
//Computes the graph area based on the defined axis points, returns a value corresponding // to the result of the calculation public int ComputeAxesTransformation(CoordSettings coordSettings, NuGenPointSet pointSet) { double[][] r_graph = { new double[3], new double[3], new double[3] }; double[,] r_screen = new double[3, 3]; List <NuGenPoint> points = pointSet.Points; axisPointCount = 0; validAxes = false; foreach (NuGenPoint p in points) { if (axisPointCount == 3) { throw new InvalidOperationException("We have three axis points, can not compute more"); } if (p.GraphCoordsAreDefined()) { double xGraph, yGraph; ConvertToCartesian(NuGenMath.mmUnitize(coordSettings), p.XThetaGraph, p.YRGraph, out xGraph, out yGraph); r_graph[0][axisPointCount] = xGraph; r_graph[1][axisPointCount] = yGraph; r_graph[2][axisPointCount] = 1.0; r_screen[0, axisPointCount] = p.XScreen; r_screen[1, axisPointCount] = p.YScreen; r_screen[2, axisPointCount] = 1.0; axisPointCount++; } } if (axisPointCount == 3) { AdjustMidValuedLogCoords(coordSettings, r_graph); int rtnS2G = NuGenMath.ScreenToGraph(r_graph, r_screen, screenToGraph, graphToScreen); validAxes = false; if (rtnS2G == NuGenMath.SUCCESS) { int rtnL2L = ComputeLogToFromLinear(coordSettings, r_graph); switch (rtnL2L) { case NuGenMath.SUCCESS: break; case NuGenMath.NONPOSITIVE_COORDINATE: return(NuGenMath.NONPOSITIVE_COORDINATE); case NuGenMath.NO_SPREAD: return(NuGenMath.NO_SPREAD); } } validAxes = (rtnS2G == NuGenMath.SUCCESS); switch (rtnS2G) { case NuGenMath.SUCCESS: return(NuGenMath.SUCCESS); case NuGenMath.BAD_GRAPH_COORDINATES: return(NuGenMath.BAD_GRAPH_COORDINATES); case NuGenMath.BAD_SCREEN_COORDINATES: return(NuGenMath.BAD_SCREEN_COORDINATES); } } return(NuGenMath.SUCCESS); }
private static void TestMatrixInverse() { NuGenMath.Test(); }