/// <summary> /// create figure using surface function /// </summary> public void AddSurface(SurfaceFunc func, double x1, double x2, double y1, double y2, double zClosest = 0) { // calculate surface function values for each point of coordinate grid, and divide grid cell on subcells, if required (for more accurate calculations) int xCellCount = (int)Math.Ceiling((x2 - x1) / (PixelWidthInternal / DistanceToEyes * (DistanceToEyes + zClosest))); int yCellCount = (int)Math.Ceiling((y2 - y1) / (PixelHeight / DistanceToEyes * (DistanceToEyes + zClosest))); var stackFrames = new SurfaceRenderStackFrame[10]; for (int n = 0; n < stackFrames.Length; n++) { stackFrames[n] = new SurfaceRenderStackFrame(); } double?[] lineZValues = new double?[xCellCount + 1]; double yPrev = 0; for (int yCell = 0; yCell <= yCellCount; yCell++) { double y = (yCell != yCellCount ? y1 + yCell * (y2 - y1) / yCellCount : y2); for (int xCell = 0; xCell <= xCellCount; xCell++) { double x = (xCell != xCellCount ? x1 + xCell * (x2 - x1) / xCellCount : x2); double?z = func(x, y); if (yCell != 0) { var firstFrame = stackFrames[0]; if (xCell <= 1) { InitPoint(ref firstFrame.Points[xCell == 0 ? 0 : 2, 0], x, yPrev, lineZValues[xCell]); InitPoint(ref firstFrame.Points[xCell == 0 ? 0 : 2, 2], x, y, z); } else { firstFrame.Points[0, 0] = firstFrame.Points[2, 0]; firstFrame.Points[0, 2] = firstFrame.Points[2, 2]; InitPoint(ref firstFrame.Points[2, 0], x, yPrev, lineZValues[xCell]); InitPoint(ref firstFrame.Points[2, 2], x, y, z); } if (xCell >= 1) { DivideCell(stackFrames, func); } } lineZValues[xCell] = z; } yPrev = y; } }
private bool IsDivisionRequired(SurfaceRenderStackFrame stackFrame, SurfaceFunc func) { // if some points are out of func domain (when grid cell located on domain edge), then use interpolated values List <double> zValues = new List <double>(3); for (int x1 = 0; x1 <= 2; x1 += 2) { for (int y1 = 0; y1 <= 2; y1 += 2) { if (stackFrame.Points[x1, y1].Z != null) { continue; } zValues.Clear(); for (int x2 = 0; x2 <= 2; x2 += 2) { for (int y2 = 0; y2 <= 2; y2 += 2) { if (stackFrame.Points[x2, y2].Z == null) { continue; } double?z = func(stackFrame.Points[x2, y2].X + (stackFrame.Points[x2, y2].X - stackFrame.Points[x1, y1].X) / 100, stackFrame.Points[x2, y2].Y + (stackFrame.Points[x2, y2].Y - stackFrame.Points[x1, y1].Y) / 100); if (z != null) { zValues.Add(stackFrame.Points[x2, y2].Z.Value + (stackFrame.Points[x2, y2].Z.Value - z.Value) * 100); } } } if (zValues.Count == 0) { return(false); } double zAvg = zValues.Average(); stackFrame.Points[x1, y1].XLeftProj = GetXProj(stackFrame.Points[x1, y1].X, zAvg, true); stackFrame.Points[x1, y1].XRightProj = GetXProj(stackFrame.Points[x1, y1].X, zAvg, false); stackFrame.Points[x1, y1].YProj = GetYProj(stackFrame.Points[x1, y1].Y, zAvg); } } // if adjacent points of grid with same x or y coordinate are projected to non-adjacent points on the screen, then divide grid cell on subcells return(IsDivisionRequired(stackFrame.Points[0, 0].XLeftProj, stackFrame.Points[0, 0].YProj, stackFrame.Points[2, 0].XLeftProj, stackFrame.Points[2, 0].YProj, stackFrame.Points[2, 2].XLeftProj, stackFrame.Points[2, 2].YProj, stackFrame.Points[0, 2].XLeftProj, stackFrame.Points[0, 2].YProj) || IsDivisionRequired(stackFrame.Points[0, 0].XRightProj, stackFrame.Points[0, 0].YProj, stackFrame.Points[2, 0].XRightProj, stackFrame.Points[2, 0].YProj, stackFrame.Points[2, 2].XRightProj, stackFrame.Points[2, 2].YProj, stackFrame.Points[0, 2].XRightProj, stackFrame.Points[0, 2].YProj)); }
public static Shape PullOnSurface90(this Shape2 shape, SurfaceFunc fn) => new Shape { Points3 = shape.Points.Select(p => fn(p.y, p.x)).ToArray(), Convexes = shape.Convexes };
private bool IsDivisionRequired(SurfaceRenderStackFrame stackFrame, SurfaceFunc func) { // if some points are out of func domain (when grid cell located on domain edge), then use interpolated values List<double> zValues = new List<double>(3); for (int x1 = 0; x1 <= 2; x1 += 2) for (int y1 = 0; y1 <= 2; y1 += 2) { if (stackFrame.Points[x1, y1].Z != null) continue; zValues.Clear(); for (int x2 = 0; x2 <= 2; x2 += 2) for (int y2 = 0; y2 <= 2; y2 += 2) { if (stackFrame.Points[x2, y2].Z == null) continue; double? z = func(stackFrame.Points[x2, y2].X + (stackFrame.Points[x2, y2].X - stackFrame.Points[x1, y1].X) / 100, stackFrame.Points[x2, y2].Y + (stackFrame.Points[x2, y2].Y - stackFrame.Points[x1, y1].Y) / 100); if (z != null) zValues.Add(stackFrame.Points[x2, y2].Z.Value + (stackFrame.Points[x2, y2].Z.Value - z.Value) * 100); } if (zValues.Count == 0) return false; double zAvg = zValues.Average(); stackFrame.Points[x1, y1].XLeftProj = GetXProj(stackFrame.Points[x1, y1].X, zAvg, true); stackFrame.Points[x1, y1].XRightProj = GetXProj(stackFrame.Points[x1, y1].X, zAvg, false); stackFrame.Points[x1, y1].YProj = GetYProj(stackFrame.Points[x1, y1].Y, zAvg); } // if adjacent points of grid with same x or y coordinate are projected to non-adjacent points on the screen, then divide grid cell on subcells return IsDivisionRequired(stackFrame.Points[0, 0].XLeftProj, stackFrame.Points[0, 0].YProj, stackFrame.Points[2, 0].XLeftProj, stackFrame.Points[2, 0].YProj, stackFrame.Points[2, 2].XLeftProj, stackFrame.Points[2, 2].YProj, stackFrame.Points[0, 2].XLeftProj, stackFrame.Points[0, 2].YProj) || IsDivisionRequired(stackFrame.Points[0, 0].XRightProj, stackFrame.Points[0, 0].YProj, stackFrame.Points[2, 0].XRightProj, stackFrame.Points[2, 0].YProj, stackFrame.Points[2, 2].XRightProj, stackFrame.Points[2, 2].YProj, stackFrame.Points[0, 2].XRightProj, stackFrame.Points[0, 2].YProj); }
/// <summary> /// calculate point projection for some cell and, if required, divide that cell on subcells and repeat procedure for them /// </summary> private void DivideCell(SurfaceRenderStackFrame[] stackFrames, SurfaceFunc func) { int depth = 0; stackFrames[0].Step = 0; while (depth >= 0) { var currentFrame = stackFrames[depth]; if (currentFrame.Step == 0) { if (depth == stackFrames.Length - 1 || !IsDivisionRequired(currentFrame, func)) // no division required or division limit reached? { // save calculated point projections for (int x = 0; x <= 2; x++) for (int y = 0; y <= 2; y++) if (currentFrame.Points[x, y].Z != null) AdjustNearest(currentFrame.Points[x, y].XLeftProj, currentFrame.Points[x, y].XRightProj, currentFrame.Points[x, y].YProj); depth--; } else { // calculate additional point coordinates and projections (these values will be used for grid subcells) double[] xCoords = { currentFrame.Points[0, 0].X, (currentFrame.Points[0, 0].X + currentFrame.Points[2, 0].X) / 2, currentFrame.Points[2, 0].X }; double[] yCoords = { currentFrame.Points[0, 0].Y, (currentFrame.Points[0, 0].Y + currentFrame.Points[0, 2].Y) / 2, currentFrame.Points[0, 2].Y }; for (int x = 0; x <= 2; x++) for (int y = 0; y <= 2; y++) if (x == 1 || y == 1) InitPoint(ref currentFrame.Points[x, y], xCoords[x], yCoords[y], func(xCoords[x], yCoords[y])); currentFrame.Step++; } } else if (currentFrame.Step >= 1 && currentFrame.Step <= 4) { // initialize next grid subcell and pass to processing of it var nextFrame = stackFrames[depth + 1]; nextFrame.Step = 0; for (int x = 0; x <= 1; x++) for (int y = 0; y <= 1; y++) nextFrame.Points[x * 2, y * 2] = currentFrame.Points[(currentFrame.Step - 1) / 2 + x, (currentFrame.Step - 1) % 2 + y]; currentFrame.Step++; depth++; } else depth--; } }
/// <param name="zFarthest">determines bounds of the grid (grid must coincide with visible region)</param> /// <param name="zClosest">determines base grid cell size</param> public void AddSurface(SurfaceFunc func, double zFarthest, double zClosest = 0) { double addWidth = GetAdditionalWidth(zFarthest), addHeight = GetAdditionalHeight(zFarthest); AddSurface(func, -addWidth, Width + addWidth, -addHeight, Height + addHeight, zClosest); }
/// <summary> /// create figure using surface function /// </summary> public void AddSurface(SurfaceFunc func, double x1, double x2, double y1, double y2, double zClosest = 0) { // calculate surface function values for each point of coordinate grid, and divide grid cell on subcells, if required (for more accurate calculations) int xCellCount = (int)Math.Ceiling((x2 - x1) / (PixelWidthInternal / DistanceToEyes * (DistanceToEyes + zClosest))); int yCellCount = (int)Math.Ceiling((y2 - y1) / (PixelHeight / DistanceToEyes * (DistanceToEyes + zClosest))); var stackFrames = new SurfaceRenderStackFrame[10]; for (int n = 0; n < stackFrames.Length; n++) stackFrames[n] = new SurfaceRenderStackFrame(); double?[] lineZValues = new double?[xCellCount + 1]; double yPrev = 0; for (int yCell = 0; yCell <= yCellCount; yCell++) { double y = (yCell != yCellCount ? y1 + yCell * (y2 - y1) / yCellCount : y2); for (int xCell = 0; xCell <= xCellCount; xCell++) { double x = (xCell != xCellCount ? x1 + xCell * (x2 - x1) / xCellCount : x2); double? z = func(x, y); if (yCell != 0) { var firstFrame = stackFrames[0]; if (xCell <= 1) { InitPoint(ref firstFrame.Points[xCell == 0 ? 0 : 2, 0], x, yPrev, lineZValues[xCell]); InitPoint(ref firstFrame.Points[xCell == 0 ? 0 : 2, 2], x, y, z); } else { firstFrame.Points[0, 0] = firstFrame.Points[2, 0]; firstFrame.Points[0, 2] = firstFrame.Points[2, 2]; InitPoint(ref firstFrame.Points[2, 0], x, yPrev, lineZValues[xCell]); InitPoint(ref firstFrame.Points[2, 2], x, y, z); } if (xCell >= 1) DivideCell(stackFrames, func); } lineZValues[xCell] = z; } yPrev = y; } }
public SurfaceDyno(SurfaceFunc surfaceFunc, Vector2[] uvPoints, (int i, int j)[] connections, Func <Graph.Node, DynoFunc> ruleFn)
/// <param name="zFarthest">determines bounds of the grid (grid must coincide with visible region)</param> /// <param name="zClosest">determines base grid cell size</param> public void AddSurface(SurfaceFunc func, double zFarthest, double zClosest = 0) { double addWidth = GetAdditionalWidth(zFarthest), addHeight = GetAdditionalHeight(zFarthest); AddSurface(func, -addWidth, Width + addWidth, -addHeight, Height + addHeight, zClosest); }
/// <summary> /// calculate point projection for some cell and, if required, divide that cell on subcells and repeat procedure for them /// </summary> private void DivideCell(SurfaceRenderStackFrame[] stackFrames, SurfaceFunc func) { int depth = 0; stackFrames[0].Step = 0; while (depth >= 0) { var currentFrame = stackFrames[depth]; if (currentFrame.Step == 0) { if (depth == stackFrames.Length - 1 || !IsDivisionRequired(currentFrame, func)) // no division required or division limit reached? { // save calculated point projections for (int x = 0; x <= 2; x++) { for (int y = 0; y <= 2; y++) { if (currentFrame.Points[x, y].Z != null) { AdjustNearest(currentFrame.Points[x, y].XLeftProj, currentFrame.Points[x, y].XRightProj, currentFrame.Points[x, y].YProj); } } } depth--; } else { // calculate additional point coordinates and projections (these values will be used for grid subcells) double[] xCoords = { currentFrame.Points[0, 0].X, (currentFrame.Points[0, 0].X + currentFrame.Points[2, 0].X) / 2, currentFrame.Points[2, 0].X }; double[] yCoords = { currentFrame.Points[0, 0].Y, (currentFrame.Points[0, 0].Y + currentFrame.Points[0, 2].Y) / 2, currentFrame.Points[0, 2].Y }; for (int x = 0; x <= 2; x++) { for (int y = 0; y <= 2; y++) { if (x == 1 || y == 1) { InitPoint(ref currentFrame.Points[x, y], xCoords[x], yCoords[y], func(xCoords[x], yCoords[y])); } } } currentFrame.Step++; } } else if (currentFrame.Step >= 1 && currentFrame.Step <= 4) { // initialize next grid subcell and pass to processing of it var nextFrame = stackFrames[depth + 1]; nextFrame.Step = 0; for (int x = 0; x <= 1; x++) { for (int y = 0; y <= 1; y++) { nextFrame.Points[x * 2, y * 2] = currentFrame.Points[(currentFrame.Step - 1) / 2 + x, (currentFrame.Step - 1) % 2 + y]; } } currentFrame.Step++; depth++; } else { depth--; } } }