protected override void FindBoundary() { bool output = false; //float acceptanceRatio = 0.9f; //float maxTimeDistance = 0.5f; // ~~~~~~~~~~~ Variable Initializations ~~~~~~~~~~~~~ \\ // Find out: Where do we want the boundary to be? // "One day": Take Okubo etc as predictor. int preferredBoundaryTime = SliceTimeMain + (24 * RedSea.Singleton.NumSubsteps) / _everyNthTimestep; float maxTimeDistance = 1.0f; float maxRadius = 12; float minRadius = 1; float radiusGrowth = StepSize * 3; float stepSizeStreamlines = StepSize; PointSet<Point> boundaryOW = OkuboBoundary(preferredBoundaryTime); // At which point did we find the Boundary? int[] boundaryIndices, boundaryIndicesLast; // At which point did we find the Boundary last time? Initalize 0. boundaryIndicesLast = new int[LineX]; for (int i = 0; i < boundaryIndicesLast.Length; i++) boundaryIndicesLast[i] = int.MaxValue; // Keep the chosen lines in here. LineSet chosenPathlines = new LineSet(new Line[LineX]); // These are lines that do not fullfill the criteria, but are the best ones we got so far. Line[] bestPathlines = new Line[LineX]; float[] bestTimeDistance = new float[LineX]; for (int i = 0; i < bestTimeDistance.Length; i++) bestTimeDistance[i] = float.MaxValue; // ~~~~~~~~~~~~ Inner Circle ~~~~~~~~~~~~~~~~~~~~~~~~ \\ //for (float offsetInnerCircle = 7; offsetInnerCircle < 10; offsetInnerCircle += 0.1f) float offsetInnerCircle = AlphaStable; { chosenPathlines = new LineSet(new Line[LineX]); Console.WriteLine("Current offset: {0}", offsetInnerCircle); // float offsetInnerCircle = AlphaStable; // Save where to transit steady -> unsteady integration. float[] offsetSeeds = new float[LineX]; for (int i = 0; i < offsetSeeds.Length; i++) offsetSeeds[i] = offsetInnerCircle; // Create small circle around selection. Point[] circle = new Point[LineX]; // Seeds for pathlines. PointSet<Point> seeds = boundaryOW; //new PointSet<Point>(circle); // ~~~~~~~~~~~~ Integrate Pathlines and Adapt ~~~~~~~~~~~~~~~~~~~~~~~~ \\ // Setup integrator. Integrator pathlineIntegrator = Integrator.CreateIntegrator(null, IntegrationType, _cores[_selectedCore], _repulsion); pathlineIntegrator.StepSize = StepSize; // Count out the runs for debugging. int run = 0; //while (seeds.Length > 0) //{ // if (output) Console.WriteLine("Starting run {0}, {1} seeds left.", run++, seeds.Length); // ~~~~~~~~~~~~ Integrate Pathlines ~~~~~~~~~~~~~~~~~~~~~~~~ \\ #region IntegratePathlines // Do we need to load a field first? if (_velocity.TimeOrigin > preferredBoundaryTime - 1 || _velocity.TimeOrigin + _velocity.Size.T < preferredBoundaryTime + 1) LoadField(Math.Max(preferredBoundaryTime - STEPS_IN_MEMORY / 2, 0), MemberMain, STEPS_IN_MEMORY); int startStep = (int)_velocity.TimeOrigin; // Integrate first few steps. pathlineIntegrator.Field = _velocity; pathlineIntegrator.Direction = Sign.POSITIVE; LineSet[] pathlines = pathlineIntegrator.Integrate(seeds, true); // Append integrated lines of next loaded vectorfield time slices. float timeLength = RedSea.Singleton.NumSubstepsTotal / _everyNthTimestep; //preferredBoundaryTime * 2; while (_currentEndStep + 1 < timeLength) { // Don't load more steps than we need to! int numSteps = (int)Math.Min(timeLength - _currentEndStep, STEPS_IN_MEMORY); pathlineIntegrator.Field = null; LoadField(_currentEndStep, MemberMain, numSteps); // Integrate further. pathlineIntegrator.Field = _velocity; pathlineIntegrator.IntegrateFurther(pathlines[0]); } // Now, integrate backwards. pathlineIntegrator.Direction = Sign.NEGATIVE; while (startStep > 0) { // Don't load more steps than we need to! int numSteps = (int)Math.Min(startStep + 1, STEPS_IN_MEMORY); pathlineIntegrator.Field = null; LoadField(startStep - numSteps + 1, MemberMain, numSteps); startStep = (int)_velocity.TimeOrigin; // Integrate further. pathlineIntegrator.Field = _velocity; pathlineIntegrator.IntegrateFurther(pathlines[1]); } pathlines[1].Reverse(); pathlines[1].Append(pathlines[0]); chosenPathlines = pathlines[1]; #endregion IntegratePathlines // // ~~~~~~~~~~~~ Get Boundary ~~~~~~~~~~~~~~~~~~~~~~~~ \\ // #region GetBoundary // // The two needes functions. // Line[] distances = FieldAnalysis.GetGraph(_cores[_selectedCore], _selection, pathlines[0], StepSize, _everyNthTimestep, true); // Line[] angles = FieldAnalysis.GetGraph(_cores[_selectedCore], _selection, pathlines[0], StepSize, _everyNthTimestep, false); // // Find the boundary based on angle and distance. // FieldAnalysis.FindBoundaryFromDistanceAngleDonut(distances, angles, out boundaryIndices); // #endregion GetBoundary // // ~~~~~~~~~~~~ Chose or Offset Pathlines ~~~~~~~~~~~~ \\ // int numNewSeeds = 0; // int numPathlines = 0; // float avgTimeDistance = 0; // // Recompute start points. // for (int idx = 0; idx < LineX; ++idx) // { // // We already have an optimal line here. Continue. // if (chosenPathlines[idx] != null) // continue; // // Should we save this line? // Vector3 pos; // // Save this point // if (boundaryIndices[numPathlines] >= 0) // { // pos = pathlines[numPathlines][boundaryIndices[numPathlines]]; // _allBoundaryPoints.Add(new Point(pos) { Color = new Vector3(0.1f, pos.Z * _everyNthTimestep / RedSea.Singleton.NumSubstepsTotal, 0.1f) }); // } // // Finally found it? // // TODOD: DEBUG!!!!!!!!!!!!!!!!111!!!!!!!!!!!!!!!!!!elf!!!!!!!!!!!!!!!! // float timeDistance = Math.Abs((boundaryIndices[numPathlines] * StepSize) - preferredBoundaryTime); // avgTimeDistance += timeDistance; // if (timeDistance < maxTimeDistance || run >= 100) // We reached the spot! Take this line! // { // chosenPathlines[idx] = pathlines[numPathlines]; // if (output) // Console.WriteLine("Line {0} was chosen because it is perfect!", idx); // } // else // { // // Add new seed to seed list. // float scale = boundaryIndices[numPathlines] / preferredBoundaryTime; // float newOffset = offsetSeeds[numPathlines] + ((scale > 1) ? radiusGrowth : -radiusGrowth); // newOffset = Math.Max(minRadius, newOffset); // newOffset = Math.Min(maxRadius, newOffset); // // We are stuck. Take best value we reached so far and go on. // if (newOffset == offsetSeeds[numPathlines] && bestPathlines[idx] != null) // { // chosenPathlines[idx] = bestPathlines[idx]; // continue; // } // if (timeDistance < bestTimeDistance[idx]) // { // bestPathlines[idx] = pathlines[numPathlines]; // bestTimeDistance[idx] = timeDistance; // } // offsetSeeds[numPathlines] = newOffset; // // Recompute position on circle. // float x = (float)(Math.Sin(angleDiff * idx + Math.PI / 2)); // float y = (float)(Math.Cos(angleDiff * idx + Math.PI / 2)); // // Take the selection as center. // seeds.Points[numNewSeeds] = new Point() { Position = new Vector3(_selection.X + x * offsetSeeds[numNewSeeds], _selection.Y + y * offsetSeeds[numNewSeeds], SliceTimeMain) }; // // Count up number of new seeds. // numNewSeeds++; // } // // We do not count up this value if there is a chosen pathline at this index already. // numPathlines++; // } // // We maybe need less seeds now? // if (numNewSeeds < seeds.Length) // Array.Resize(ref seeds.Points, numNewSeeds); // Console.WriteLine("Average time distance: {0}", avgTimeDistance / numPathlines); // } } // ~~~~~~~~~~~~ Get Boundary for Rendering~~~~~~~~~~~~~~~~~~~~~~~~ \\ // The two needes functions. _coreDistanceGraph = FieldAnalysis.GetGraph(_cores[_selectedCore], _selection, chosenPathlines, StepSize, _everyNthTimestep, true); _coreAngleGraph = FieldAnalysis.GetGraph(_cores[_selectedCore], _selection, chosenPathlines, StepSize, _everyNthTimestep, false); // Find the boundary based on angle and distance. _boundaryBallFunction = new LineBall(_linePlane, new LineSet(new Line[] { FieldAnalysis.FindBoundaryFromDistanceAngleDonut(_coreDistanceGraph, _coreAngleGraph, out boundaryIndices) })); // Find the boundary in space-time. int time = SliceTimeMain; _boundariesSpacetime[time] = new Line() { Positions = new Vector3[LineX + 1] }; for (int l = 0; l < LineX; ++l) { Vector3 pos = chosenPathlines[l][boundaryIndices[l]]; _allBoundaryPoints.Add(new Point(pos) { Color = Vector3.UnitY * pos.Z / RedSea.Singleton.NumSubstepsTotal * _everyNthTimestep }); _allBoundaryPoints.Add(boundaryOW.Points[l]); _boundariesSpacetime[time][l] = pos; } _boundariesSpacetime[time][LineX] = _boundariesSpacetime[time][0]; _boundaryBallSpacetime = new LineBall(_linePlane, _boundariesSpacetime, LineBall.RenderEffect.HEIGHT, Colormap); // Pathlines for rendering. _pathlinesTime = chosenPathlines; _pathlines = new LineBall(_linePlane, chosenPathlines, LineBall.RenderEffect.HEIGHT, ColorMapping.GetComplementary(Colormap), Flat); Console.WriteLine("Khalas. Boundary indices:"); float sumTime = 0; for (int i = 0; i < chosenPathlines.Length; i++) { float atTime = chosenPathlines[i][boundaryIndices[i]].Z; sumTime += atTime; Console.WriteLine("\tTime at {0}: {1}", i, atTime); } Console.WriteLine("Average time: {0}", sumTime / chosenPathlines.Length); _boundaryCloud = new PointCloud(_linePlane, new PointSet<Point>(_allBoundaryPoints.ToArray())); LineSet set = new LineSet(_coreAngleGraph); GeometryWriter.WriteHeightCSV(RedSea.Singleton.DonutFileName + "Angle.csv", set); GeometryWriter.WriteToFile(RedSea.Singleton.DonutFileName + ".angle", set); set = new LineSet(_coreDistanceGraph); GeometryWriter.WriteHeightCSV(RedSea.Singleton.DonutFileName + "Distance.csv", set); GeometryWriter.WriteToFile(RedSea.Singleton.DonutFileName + ".distance", set); }
protected void BuildGraph() { // Compute ftle. if (LineX == 0) return; //for (int seed = 0; seed < _numSeeds; ++seed) //{ // // Smaller field: the difference diminishes it by one line. // float[] fx = new float[LineX - 1]; // float[] x = new float[LineX - 1]; // for (int e = 0; e < fx.Length; ++e) // { // // Inbetween graphs, there is one useless one. // int index = seed * LineX + e; // if (_distanceDistance[index].Length <= 1) // { // fx[e] = float.MaxValue; // x[e] = _distanceDistance[index].Offset; // } // else // { // fx[e] = _distanceDistance[index].RelativeSumOver(IntegrationTime);// / _distanceDistance[index].Length; // x[e] = _distanceDistance[index].Offset; // } // } // _errorGraph[seed] = new Graph2D(x, fx); // int errorBound = FieldAnalysis.FindBoundaryInError(_errorGraph[seed]); // if (errorBound >= 0) // _allBoundaryPoints.Add(new Point(_pathlinesTime[seed * LineX + errorBound][0])); //} //// GeometryWriter.WriteGraphCSV(RedSea.Singleton.DonutFileName + "Error.csv", _errorGraph); //Console.WriteLine("Radii without boundary point: {0} of {1}", _numSeeds - _allBoundaryPoints.Count, _numSeeds); //_boundaryCloud = new PointCloud(_graphPlane, new PointSet<Point>(_allBoundaryPoints.ToArray())); ////int errorBound = FieldAnalysis.FindBoundaryInError(_errorGraph[0]); ////_pathlinesTime.Cut(errorBound); //// ~~~~~~~~~~~~ Get Boundary for Rendering ~~~~~~~~~~~~ \\ //_pathlinesTime.Thickness *= 0.1f; //_pathlines = new LineBall(_linePlane, _pathlinesTime, LineBall.RenderEffect.HEIGHT, ColorMapping.GetComplementary(Colormap), Flat); //// _graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphsToCircles(_distanceAngleGraph, new Vector3(_selection.X, _selection.Y, SliceTimeMain)), LineBall.RenderEffect.HEIGHT, Colormap, false); _graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphToSun(_ftle, new Vector3(_selection.X, _selection.Y, 0)), LineBall.RenderEffect.HEIGHT, Colormap, Flat); _rebuilt = false; }
protected override void UpdateBoundary() { if (_lastSetting != null && (_rebuilt || FlatChanged || GraphChanged) && _errorGraph != null) // && (Flat && !Graph)) { //Graph2D[] dist = FieldAnalysis.GraphDifferenceForward(_distanceTauGraph); //Plane zPlane = new Plane(_graphPlane, Vector3.UnitZ * 2); _graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphToSun(_errorGraph, new Vector3(_selection.X, _selection.Y, 0)), LineBall.RenderEffect.HEIGHT, Colormap, Flat); // _graph = new LineBall(zPlane, FieldAnalysis.WriteGraphsToCircles(dist, new Vector3(_selection.X, _selection.Y, SliceTimeMain)), LineBall.RenderEffect.HEIGHT, Colormap, false); _rebuilt = false; } if (_lastSetting != null && (IntegrationTimeChanged || _rebuilt || Graph && GraphChanged && Flat)) BuildGraph(); //_graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphToSun(_errorGraph, new Vector3(_selection.X, _selection.Y, 0)), LineBall.RenderEffect.HEIGHT, Colormap, false); // new LineBall(_graphPlane, FieldAnalysis.WriteGraphsToCircles(_distanceAngleGraph, new Vector3(_selection.X, _selection.Y, SliceTimeMain)), LineBall.RenderEffect.HEIGHT, Colormap, false); //LineSet cutLines; ////if (SliceTimeReference > SliceTimeMain) ////{ //// // _graph = cut version of _coreAngleGraph. //// int length = SliceTimeReference - SliceTimeMain; //// length = (int)((float)length / StepSize + 0.5f); //// cutLines = FieldAnalysis.CutLength(new LineSet(_coreDistanceGraph), length); ////} ////else //// cutLines = new LineSet(_coreDistanceGraph); //cutLines = new LineSet(FieldAnalysis.); //_graph = new LineBall[] { new LineBall(_graphPlane, cutLines, LineBall.RenderEffect.HEIGHT, Colormap) }; }
protected List<Renderable> Map() { List<Renderable> output = new List<Renderable>(2); bool update = false; if (_lastSetting == null || SliceTimeMainChanged) { GeometryWriter.ReadFromFile(RedSea.Singleton.DonutFileName + ".angle", out _loadedAngle); GeometryWriter.ReadFromFile(RedSea.Singleton.DonutFileName + ".distance", out _loadedDistance); _loadedBall = new LineBall(Plane, _loadedAngle, LineBall.RenderEffect.HEIGHT); int[] indices; _boundaryLoaded = FieldAnalysis.FindBoundaryFromDistanceAngleDonut(_loadedDistance.Lines, _loadedAngle.Lines, out indices); _blockData = FieldAnalysis.PlotLines2D(_loadedAngle); _boundaryBlock = FieldAnalysis.FindBoundaryFromDistanceDonut(_blockData.Lines); update = true; } if (update || FlatChanged) { _loadedBall = new LineBall(Plane, _loadedAngle, LineBall.RenderEffect.HEIGHT, Colormap, Flat); _boundaryLoadedBall = new LineBall(_fightPlane, new LineSet(new Line[] { _boundaryLoaded }) { Thickness = 0.2f }, LineBall.RenderEffect.HEIGHT, Colormap, Flat); _blockBall = new LineBall(Plane, _blockData, LineBall.RenderEffect.HEIGHT, Colormap, Flat); _boundaryBlockBall = new LineBall(_fightPlane, new LineSet(new Line[] { _boundaryBlock }) { Thickness = 0.2f }, LineBall.RenderEffect.HEIGHT, Colormap, Flat); update = true; } if (_lastSetting == null || WindowStartChanged || WindowWidthChanged || ColormapChanged || update) { _loadedBall.LowerBound = WindowStart; _loadedBall.UpperBound = WindowWidth + WindowStart; _loadedBall.UsedMap = Colormap; _blockBall.LowerBound = WindowStart; _blockBall.UpperBound = WindowWidth + WindowStart; _blockBall.UsedMap = Colormap; _boundaryBlockBall.LowerBound = WindowStart; _boundaryBlockBall.UpperBound = WindowWidth + WindowStart; _boundaryBlockBall.UsedMap = ColorMapping.GetComplementary(Colormap); _boundaryLoadedBall.LowerBound = WindowStart; _boundaryLoadedBall.UpperBound = WindowWidth + WindowStart; _boundaryLoadedBall.UsedMap = ColorMapping.GetComplementary(Colormap); } output.Add(Graph ? _blockBall : _loadedBall); output.Add(Graph ? _boundaryBlockBall : _boundaryLoadedBall); return output; }
protected void BuildGraph() { float cutValue = 2000.0f; // Compute error. if (LineX == 0) return; _errorGraph = new Graph2D[_numSeeds]; _allBoundaryPoints = new List<Point>(); for (int seed = 0; seed < _numSeeds; ++seed) { // Smaller field: the difference diminishes it by one line. float[] fx = new float[LineX - 1]; float[] x = new float[LineX - 1]; for (int e = 0; e < fx.Length; ++e) { if (_lineDistance[seed][e].Length <= 1) { fx[e] = float.MaxValue; x[e] = _lineDistance[seed][e].Offset; } else { fx[e] = _lineDistance[seed][e].RelativeSumOver(IntegrationTime);// / _distanceDistance[index].Length; x[e] = _lineDistance[seed][e].Offset; if (fx[e] > cutValue) { Array.Resize(ref fx, e); Array.Resize(ref x, e); break; } } } _errorGraph[seed] = new Graph2D(x, fx); _errorGraph[seed].SmoothLaplacian(0.8f); _errorGraph[seed].SmoothLaplacian(0.8f); //var maxs = _errorGraph[seed].Maxima(); //float angle = (float)((float)seed * Math.PI * 2 / _errorGraph.Length); //foreach (int p in maxs) //{ // float px = _errorGraph[seed].X[p]; // _allBoundaryPoints.Add(new Point(new Vector3(_selection.X + (float)(Math.Sin(angle + Math.PI / 2)) * px, _selection.Y + (float)(Math.Cos(angle + Math.PI / 2)) * px, cutValue)) { Color = Vector3.UnitX }); //} //int[] errorBound = FieldAnalysis.FindBoundaryInError(_errorGraph[seed]); //foreach (int bound in errorBound) // _allBoundaryPoints.Add(new Point(_pathlinesTime[seed * LineX + bound][0])); } //_boundariesSpacetime = FieldAnalysis.FindBoundaryInErrors(_errorGraph, new Vector3(_selection, SliceTimeMain)); //_boundaryBallSpacetime = new LineBall(_linePlane, _boundariesSpacetime, LineBall.RenderEffect.HEIGHT, ColorMapping.GetComplementary(Colormap)); //if (errorBound >= 0) // _allBoundaryPoints.Add(new Point(_pathlinesTime[seed * LineX + errorBound][0])); // GeometryWriter.WriteGraphCSV(RedSea.Singleton.DonutFileName + "Error.csv", _errorGraph); Console.WriteLine("Radii without boundary point: {0} of {1}", _numSeeds - _allBoundaryPoints.Count, _numSeeds); // _graphPlane.ZAxis = Plane.ZAxis * WindowWidth; // _boundaryCloud = new PointCloud(_graphPlane, new PointSet<Point>(_allBoundaryPoints.ToArray())); //LineSet lineCpy = new LineSet(_pathlinesTime); //lineCpy.CutAllHeight(_repulsion); //_pathlines = new LineBall(_linePlane, lineCpy, LineBall.RenderEffect.HEIGHT, Colormap, false); //int errorBound = FieldAnalysis.FindBoundaryInError(_errorGraph[0]); //_pathlinesTime.Cut(errorBound); // ~~~~~~~~~~~~ Get Boundary for Rendering ~~~~~~~~~~~~ \\ // _pathlines = new LineBall(_linePlane, _pathlinesTime, LineBall.RenderEffect.HEIGHT, ColorMapping.GetComplementary(Colormap), Flat); // _graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphsToCircles(_distanceAngleGraph, new Vector3(_selection.X, _selection.Y, SliceTimeMain)), LineBall.RenderEffect.HEIGHT, Colormap, false); _graph = new LineBall(_graphPlane, FieldAnalysis.WriteGraphToSun(_errorGraph, new Vector3(_selection.X, _selection.Y, 0)), LineBall.RenderEffect.HEIGHT, Colormap, Flat); _rebuilt = false; }