Esempio n. 1
0
        public void SetupOnce()
        {
            ObjectFactory.Initialize((cfg) => cfg.AddRegistry(new ProcessorRegistry()));

            var eventLogger = Substitute.For<IEventLogger>();
            processor = new PipelineProcessor(ObjectFactory.Container, eventLogger);
        }
Esempio n. 2
0
        List <PipelineStepContainer> _pipelineSteps = new List <PipelineStepContainer>(); // Reference to all steps

        public Pipeline <TPipelineIn, TPipelineOut> AddStepAndStart <TIn, TOut>(IPipelineProcessor <TIn, TOut> processor)
        {
            var step      = new PipelineStep <TIn, TOut>(processor);
            int stepIndex = _pipelineSteps.Count;

            // Alternatively, I can store a list of the Task.Run() handlers here and then run them at a later point
            // Start the step
            Task.Run(() =>
            {
                // Cached next step
                IBufferable <TOut> nextBuffer = null;

                foreach (Item <TIn> input in step.Buffer.GetConsumingEnumerable())
                {
                    // Give us the most up-to-date status on our current position in the pipeline
                    bool isLastStep = stepIndex == _pipelineSteps.Count - 1;

                    TOut outputValue;

                    // Attempt to process value
                    try
                    {
                        outputValue = processor.Process(input.Value);
                    }
                    catch (Exception e)
                    {
                        input.TaskCompletionSource.SetException(e);
                        continue;
                    }

                    // Move to next step
                    if (isLastStep)
                    {
                        input.TaskCompletionSource.SetResult((TPipelineOut)(object)outputValue);
                    }
                    else
                    {
                        nextBuffer = nextBuffer ?? _pipelineSteps[stepIndex + 1].Value as IBufferable <TOut>;
                        try // In the case of the pipeline closing prematurely, catch the error and throw an exception to the task
                        {
                            nextBuffer.Buffer.Add(new Item <TOut>
                            {
                                Value = outputValue,
                                TaskCompletionSource = input.TaskCompletionSource
                            });
                        }
                        catch (InvalidOperationException e)
                        {
                            input.TaskCompletionSource.SetException(e);
                        }
                    }
                }
            });
            _pipelineSteps.Add(new PipelineStepContainer
            {
                Value = step,
            });
            return(this);
        }
Esempio n. 3
0
        public void SetupOnce()
        {
            ObjectFactory.Initialize((cfg) => cfg.AddRegistry(new ProcessorRegistry()));

            var eventLogger = Substitute.For <IEventLogger>();

            processor = new PipelineProcessor(ObjectFactory.Container, eventLogger);
        }
        public static PipelineResult ProcessActionForEvent(this IPipelineProcessor processor, IEvent @event)
        {
            var payloadType = typeof(Payload <>).MakeGenericType(@event.GetType());

            var payloadInstance = Activator.CreateInstance(payloadType, @event);

            var processMethod    = typeof(IPipelineProcessor).GetMethod("Process");
            var genProcessMethod = processMethod.MakeGenericMethod(@event.GetType());

            return((PipelineResult)genProcessMethod.Invoke(processor, new [] { payloadInstance }));
        }
Esempio n. 5
0
        // Token: 0x060008FC RID: 2300 RVA: 0x0001E60C File Offset: 0x0001C80C
        private void OnSessionClosed(object sender, EventArgs e)
        {
            this.m_Connected     = false;
            this.m_LocalEndPoint = null;
            IPipelineProcessor pipeLineProcessor = this.PipeLineProcessor;

            if (pipeLineProcessor != null)
            {
                pipeLineProcessor.Reset();
            }
            EventHandler closed = this.Closed;

            if (closed != null)
            {
                closed(this, EventArgs.Empty);
            }
            this.m_ConnectEvent.Set();
        }
Esempio n. 6
0
        /// <summary>
        /// Construct the PVM task accumulator for the PVM rendering task to contain the values to be rendered
        /// We manage this here because the accumulator context relates to the query spatial bounds, not the rendered tile bounds
        /// The accumulator is instructed to created a context covering the OverrideSpatialExtents context from the processor (which
        /// will represent the bounding extent of data required due to any tile rotation), and covered by a matching (possibly larger) grid
        /// of cells to the map view grid of pixels
        /// </summary>
        private void ConstructPVMTaskAccumulator(IPipelineProcessor processor)
        {
            // Construct the PVM task accumulator for the PVM rendering task to contain the values to be rendered
            // We manage this here because the accumulator context relates to the query spatial bounds, not the rendered tile bounds
            // The accumulator is instructed to created a context covering the OverrideSpatialExtents context from the processor (which
            // will represent the bounding extent of data required due to any tile rotation), and covered by a matching (possibly larger) grid
            // of cells to the map view grid of pixels

            var smoother = (Displayer as IProductionPVMConsistentDisplayer)?.DataSmoother;
            var view     = Displayer.MapView;

            var valueStoreCellSizeX = view.XPixelSize > processor.SiteModel.CellSize ? view.XPixelSize : processor.SiteModel.CellSize;
            var valueStoreCellSizeY = view.YPixelSize > processor.SiteModel.CellSize ? view.YPixelSize : processor.SiteModel.CellSize;

            // Compute the origin of the cell in the value store that encloses the origin of the map view.
            // In the case of tile rendering, OverrideSpatialExtents represents the enclosing rotated bounding box for the tile and
            // is the bounding extent of the data should be requested
            var valueStoreOriginX = Math.Truncate(processor.OverrideSpatialExtents.MinX / valueStoreCellSizeX) * valueStoreCellSizeX;
            var valueStoreOriginY = Math.Truncate(processor.OverrideSpatialExtents.MinY / valueStoreCellSizeY) * valueStoreCellSizeY;

            // Compute the limit of the cell in the value store that encloses the limit of the map view.
            // In the case of tile rendering, OverrideSpatialExtents represents the enclosing rotated bounding box for the tile and
            // is the bounding extent of the data should be requested
            var valueStoreLimitX = Math.Truncate((processor.OverrideSpatialExtents.MaxX + valueStoreCellSizeX) / valueStoreCellSizeX) * valueStoreCellSizeX;
            var valueStoreLimitY = Math.Truncate((processor.OverrideSpatialExtents.MaxY + valueStoreCellSizeY) / valueStoreCellSizeY) * valueStoreCellSizeY;

            var valueStoreCellsX = (int)Math.Round((valueStoreLimitX - valueStoreOriginX) / valueStoreCellSizeX);
            var valueStoreCellsY = (int)Math.Round((valueStoreLimitY - valueStoreOriginY) / valueStoreCellSizeY);

            var borderAdjustmentCells = 2 * smoother?.AdditionalBorderSize ?? 0;
            var extentAdjustmentSizeX = smoother?.AdditionalBorderSize * valueStoreCellSizeX ?? 0;
            var extentAdjustmentSizeY = smoother?.AdditionalBorderSize * valueStoreCellSizeY ?? 0;

            ((IPVMRenderingTask)processor.Task).Accumulator = ((IProductionPVMConsistentDisplayer)Displayer).GetPVMTaskAccumulator(
                valueStoreCellSizeX, valueStoreCellSizeY,
                valueStoreCellsX + borderAdjustmentCells,
                valueStoreCellsY + borderAdjustmentCells,
                valueStoreOriginX - extentAdjustmentSizeX,
                valueStoreOriginY - extentAdjustmentSizeY,
                (valueStoreLimitX - valueStoreOriginX) + 2 * extentAdjustmentSizeX,
                (valueStoreLimitY - valueStoreOriginY) + 2 * extentAdjustmentSizeY,
                processor.SiteModel.CellSize
                );
        }
        private void RunSubPipelines(PipelineContext pipelineContext, Pipeline subPipeline)
        {
            ILogger logger = pipelineContext.PipelineBatchContext.Logger;

            pipelineContext.CurrentPipeline = subPipeline;
            IPipelineProcessor pipelineProcessor = subPipeline.PipelineProcessor;

            if (pipelineProcessor == null)
            {
                logger.Error("Pipeline will be skipped because it does not have a processor assigned. (pipeline step: {0}, sub-pipeline: {1})", "Iterate and Run Async", (object)subPipeline.Name);
            }
            else if (!pipelineProcessor.CanProcess(subPipeline, pipelineContext))
            {
                logger.Error("Pipeline will be skipped because the processor cannot processes the sub-pipeline. (pipeline step: {0}, sub-pipeline: {1}, sub-pipeline processor: {2})", "Iterate and Run Async", (object)subPipeline.Name, (object)pipelineProcessor.GetType().FullName);
            }
            else
            {
                pipelineProcessor.Process(subPipeline, pipelineContext);
                if (pipelineContext.CriticalError)
                {
                    logger.Error("Sub pipeline processing will not abort since it's done async");
                }
            }
        }
Esempio n. 8
0
 protected virtual void OnChannelReady()
 {
     m_PipeLineProcessor = new DefaultPipelineProcessor <PipePackageInfo>(new PipeReceiveFilter(), 1024 * 64);
     StartReceive();
 }
    public virtual void Register(string pipelineName, IPipelineProcessor processorMock)
    {
      this.Pipelines[pipelineName] = processorMock;

      this.Expects(pipelineName, delegate { return true; });
    }
Esempio n. 10
0
 public PipelinesController(IPipelineProcessor pipelineProcesor)
 {
     _pipelineProcessor = pipelineProcesor;
 }
Esempio n. 11
0
 public PipelineStep(IPipelineProcessor <TStepIn, TStepOut> processor)
 {
     Processor = processor;
 }
 public static PipelineResult ProcessAction <TEvent>(this IPipelineProcessor processor, TEvent @event)
     where TEvent : IEvent
 {
     return(processor.Process(new Payload <TEvent>(@event)));
 }
Esempio n. 13
0
 /// <summary>
 /// Initializes a new instance of an immutable Pipeline that either uses given
 /// processor or the default UninterruptibleProcessor as its processor.
 /// </summary>
 /// <param name="processor">Processor.</param>
 public Pipeline(IPipelineProcessor <TPayload> processor = null)
 {
     _processor = processor ?? new UninterruptibleProcessor <TPayload>();
     _stages    = new List <IPipelineStage <TPayload> >();
 }
Esempio n. 14
0
        public virtual void Register(string pipelineName, IPipelineProcessor processorMock)
        {
            this.Pipelines[pipelineName] = processorMock;

            this.Expects(pipelineName, delegate { return(true); });
        }
Esempio n. 15
0
        /// <summary>
        /// Setup pipeline for tile request
        /// </summary>
        /// <param name="siteModelExtent">Site Model Extent</param>
        /// <param name="cellSize">Cell Size</param>
        /// <returns></returns>
        private async Task <bool> SetupPipelineTask(BoundingWorldExtent3D siteModelExtent, double cellSize)
        {
            var requestDescriptor = Guid.NewGuid();

            if (DisplayMode == QMConstants.DisplayModeStandard)
            {
                // Note coords are always supplied lat long
                if (SiteModel.CSIB() == string.Empty)
                {
                    ResultStatus = RequestErrorStatus.EmptyCoordinateSystem;
                    _log.LogError($"Failed to obtain site model coordinate system CSIB file for Project:{DataModelUid}");
                    return(false);
                }
            }

            LLHCoords = new[] { new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.West), MapUtils.Deg2Rad(TileBoundaryLL.South), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.East), MapUtils.Deg2Rad(TileBoundaryLL.North), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.West), MapUtils.Deg2Rad(TileBoundaryLL.North), 0),
                                new XYZ(MapUtils.Deg2Rad(TileBoundaryLL.East), MapUtils.Deg2Rad(TileBoundaryLL.South), 0) };

            // This will change in Part3 once development is complete
            var strCSIB   = DisplayMode == QMConstants.DisplayModeStandard ? SiteModel.CSIB() : DIMENSIONS_2012_DC_CSIB;
            var NEECoords = DIContext.Obtain <ICoreXWrapper>().LLHToNEE(strCSIB, LLHCoords.ToCoreX_XYZ(), CoreX.Types.InputAs.Radians).ToTRex_XYZ();

            GridIntervalX = (NEECoords[1].X - NEECoords[0].X) / (TileGridSize - 1);
            GridIntervalY = (NEECoords[1].Y - NEECoords[0].Y) / (TileGridSize - 1);

            _log.LogDebug($"#Tile#.({TileX},{TileY}) TileInfo: Zoom:{TileZ}, TileSizeXY:{Math.Round(NEECoords[1].X - NEECoords[0].X, 3)}m x {Math.Round(NEECoords[2].Y - NEECoords[0].Y, 3)}m, GridInterval(m) X:{Math.Round(GridIntervalX, 3)}, Y:{Math.Round(GridIntervalY, 3)}, GridSize:{TileGridSize}");

            var WorldTileHeight = MathUtilities.Hypot(NEECoords[0].X - NEECoords[2].X, NEECoords[0].Y - NEECoords[2].Y);
            var WorldTileWidth  = MathUtilities.Hypot(NEECoords[0].X - NEECoords[3].X, NEECoords[0].Y - NEECoords[3].Y);

            double dx = NEECoords[2].X - NEECoords[0].X;

            CenterX = NEECoords[2].X + dx / 2;
            double dy = NEECoords[2].Y - NEECoords[0].Y;

            CenterY = NEECoords[0].Y + dy / 2;

            // Calculate the tile rotation as the mathematical angle turned from 0 (due east) to the vector defined by dy/dx
            TileRotation = Math.Atan2(dy, dx);

            // Convert TileRotation to represent the angular deviation rather than a bearing
            TileRotation = (Math.PI / 2) - TileRotation;

            SetRotation(TileRotation);

            _log.LogDebug($"QMTile render executing across tile: [Rotation:{ MathUtilities.RadiansToDegrees(TileRotation)}] " +
                          $" [BL:{NEECoords[0].X}, {NEECoords[0].Y}, TL:{NEECoords[2].X},{NEECoords[2].Y}, " +
                          $"TR:{NEECoords[1].X}, {NEECoords[1].Y}, BR:{NEECoords[3].X}, {NEECoords[3].Y}] " +
                          $"World Width, Height: {WorldTileWidth}, {WorldTileHeight}");

            RotatedTileBoundingExtents.SetInverted();
            foreach (var xyz in NEECoords)
            {
                RotatedTileBoundingExtents.Include(xyz.X, xyz.Y);
            }


            // Intersect the site model extents with the extents requested by the caller
            _log.LogDebug($"Tile.({TileX},{TileY}) Calculating intersection of bounding box and site model {DataModelUid}:{siteModelExtent}");
            var dataSelectionExtent = new BoundingWorldExtent3D(RotatedTileBoundingExtents);

            dataSelectionExtent.Intersect(siteModelExtent);
            if (!dataSelectionExtent.IsValidPlanExtent)
            {
                ResultStatus = RequestErrorStatus.InvalidCoordinateRange;
                _log.LogInformation($"Tile.({TileX},{TileY}) Site model extents {siteModelExtent}, do not intersect RotatedTileBoundingExtents {RotatedTileBoundingExtents}");
                return(false);
            }

            // Compute the override cell boundary to be used when processing cells in the sub grids
            // selected as a part of this pipeline
            // Increase cell boundary by one cell to allow for cells on the boundary that cross the boundary
            SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MinX,
                                                               dataSelectionExtent.MinY, cellSize, SubGridTreeConsts.DefaultIndexOriginOffset,
                                                               out var CellExtents_MinX, out var CellExtents_MinY);
            SubGridTree.CalculateIndexOfCellContainingPosition(dataSelectionExtent.MaxX,
                                                               dataSelectionExtent.MaxY, cellSize, SubGridTreeConsts.DefaultIndexOriginOffset,
                                                               out var CellExtents_MaxX, out var CellExtents_MaxY);
            var CellExtents = new BoundingIntegerExtent2D(CellExtents_MinX, CellExtents_MinY, CellExtents_MaxX, CellExtents_MaxY);

            CellExtents.Expand(1);

            // Setup Task
            task      = DIContext.Obtain <Func <PipelineProcessorTaskStyle, ITRexTask> >()(PipelineProcessorTaskStyle.QuantizedMesh) as QuantizedMeshTask;
            processor = DIContext.Obtain <IPipelineProcessorFactory>().NewInstanceNoBuild <SubGridsRequestArgument>(requestDescriptor: requestDescriptor,
                                                                                                                    dataModelID: DataModelUid,
                                                                                                                    gridDataType: GridDataType.Height,
                                                                                                                    response: GriddedElevationsResponse,
                                                                                                                    filters: Filters,
                                                                                                                    cutFillDesign: new DesignOffset(),
                                                                                                                    task: task,
                                                                                                                    pipeline: DIContext.Obtain <Func <PipelineProcessorPipelineStyle, ISubGridPipelineBase> >()(PipelineProcessorPipelineStyle.DefaultProgressive),
                                                                                                                    requestAnalyser: DIContext.Obtain <IRequestAnalyser>(),
                                                                                                                    requireSurveyedSurfaceInformation: true, //Rendering.Utilities.DisplayModeRequireSurveyedSurfaceInformation(DisplayMode.Height) && Rendering.Utilities.FilterRequireSurveyedSurfaceInformation(Filters),
                                                                                                                    requestRequiresAccessToDesignFileExistenceMap: false,
                                                                                                                    overrideSpatialCellRestriction: CellExtents,
                                                                                                                    liftParams: LiftParams
                                                                                                                    );

            // Set the grid TRexTask parameters for progressive processing
            processor.Task.RequestDescriptor = requestDescriptor;
            processor.Task.TRexNodeID        = RequestingTRexNodeID;
            processor.Task.GridDataType      = GridDataType.Height;

            // Set the spatial extents of the tile boundary rotated into the north reference frame of the cell coordinate system to act as
            // a final restriction of the spatial extent used to govern data requests
            processor.OverrideSpatialExtents.Assign(RotatedTileBoundingExtents);

            // Setup new grid array for results
            GriddedElevDataArray = new GriddedElevDataRow[TileGridSize, TileGridSize];
            // build up a data sample grid from SW to NE
            for (int y = 0; y < TileGridSize; y++)
            {
                for (int x = 0; x < TileGridSize; x++)
                {
                    var x1 = NEECoords[0].X + (GridIntervalX * x);
                    var y1 = NEECoords[0].Y + (GridIntervalY * y);
                    if (Rotating)
                    {
                        Rotate_point(x1, y1, out x1, out y1);
                    }
                    GriddedElevDataArray[x, y].Easting   = x1;
                    GriddedElevDataArray[x, y].Northing  = y1;
                    GriddedElevDataArray[x, y].Elevation = CellPassConsts.NullHeight;
                }
            }

            _log.LogDebug($"Tile.({TileX},{TileY}) Boundary grid coords:{string.Concat(NEECoords)}");
            _log.LogDebug($"Tile.({TileX},{TileY}) First Easting:{GriddedElevDataArray[0, 0].Easting} Northing:{GriddedElevDataArray[0, 0].Northing}");
            _log.LogDebug($"Tile.({TileX},{TileY}) Last Easting:{GriddedElevDataArray[TileGridSize - 1, TileGridSize - 1].Easting} Northing:{GriddedElevDataArray[TileGridSize - 1, TileGridSize - 1].Northing}");

            // point to container for results
            task.GriddedElevDataArray = GriddedElevDataArray;
            task.GridIntervalX        = GridIntervalX;
            task.GridIntervalY        = GridIntervalY;
            task.GridSize             = TileGridSize;
            // Tile boundary
            task.TileMinX        = NEECoords[0].X;
            task.TileMinY        = NEECoords[0].Y;
            task.TileMaxX        = NEECoords[1].X;
            task.TileMaxY        = NEECoords[1].Y;
            task.LowestElevation = LowestElevation;

            Azimuth       = 0;
            StartNorthing = NEECoords[0].Y;
            StartEasting  = NEECoords[0].X;

            // Commented out for purposes of demo until relationship between TRex mediated skip/step selection and the quantised mesh tile vertex based selection are better understood
            //    processor.Pipeline.AreaControlSet =
            //     new AreaControlSet(false, GridIntervalX, GridIntervalY, StartEasting, StartNorthing, Azimuth);

            if (!processor.Build())
            {
                _log.LogError($"Tile.({TileX},{TileY}) Failed to build pipeline processor for request to model {DataModelUid}");
                return(false);
            }

            return(true);
        }
Esempio n. 16
0
        /// <summary>
        /// Perform rendering activities to produce a bitmap tile
        /// </summary>
        public RequestErrorStatus PerformRender(DisplayMode mode, IPipelineProcessor processor, IPlanViewPalette colourPalette, IFilterSet filters, ILiftParameters liftParams)
        {
            // Obtain the display responsible for rendering the thematic information for this mode
            Displayer = PVMDisplayerFactory.GetDisplayer(mode /*, FICOptions*/);

            if (Displayer == null)
            {
                processor.Response.ResultStatus = RequestErrorStatus.UnsupportedDisplayType;
                return(processor.Response.ResultStatus);
            }

            // Create and assign the colour palette logic for this mode to the displayer
            if (colourPalette == null)
            {
                if (mode == DisplayMode.CCA || mode == DisplayMode.CCASummary)
                {
                    Displayer.SetPalette(Utilities.ComputeCCAPalette(processor.SiteModel, filters.Filters[0].AttributeFilter, mode));

                    if (Displayer.GetPalette() == null)
                    {
                        processor.Response.ResultStatus = RequestErrorStatus.FailedToGetCCAMinimumPassesValue;
                        return(processor.Response.ResultStatus);
                    }
                }
                else
                {
                    Displayer.SetPalette(PVMPaletteFactory.GetPalette(processor.SiteModel, mode, processor.SpatialExtents));
                }
            }
            else
            {
                Displayer.SetPalette(colourPalette);
            }

            // Create the world coordinate display surface the displayer will render onto
            Displayer.MapView = new MapSurface
            {
                SquareAspect = IsWhollyInTermsOfGridProjection
            };

            var view = Displayer.MapView;

            // Set the world coordinate bounds of the display surface to be rendered on
            view.SetBounds(NPixelsX, NPixelsY);

            if (IsWhollyInTermsOfGridProjection)
            {
                view.FitAndSetWorldBounds(OriginX, OriginY, OriginX + Width, OriginY + Height, 0);
            }
            else
            {
                view.SetWorldBounds(OriginX, OriginY, OriginX + Width, OriginY + Height, 0);
            }

            // Provide data smoothing support to the displayer for the rendering operation being performed
            ((IProductionPVMConsistentDisplayer)Displayer).DataSmoother = DIContext.Obtain <Func <DisplayMode, IDataSmoother> >()(mode);

            // Set the rotation of the displayer rendering surface to match the tile rotation due to the project calibration rotation
            view.SetRotation(TileRotation);

            ConstructPVMTaskAccumulator(processor);

            // Displayer.ICOptions  = ICOptions;

            // Set the skip-step area control cell selection parameters for this tile render. Note that the floating point
            // skip step algorithm is requested, and a user origin is configured that offsets the sampling grid by half a pixel
            // size to matching the skip-stepping the PVM accumulator will use when transcribing cell data from sub grids into
            // the accumulator. Note that the area control set is not configured with a rotation - this is taken into account
            // through the mapview rotation configured above
            processor.Pipeline.AreaControlSet = new AreaControlSet(false,
                                                                   view.XPixelSize, view.YPixelSize,
                                                                   view.XPixelSize / 2.0, view.YPixelSize / 2.0,
                                                                   0.0);

            processor.Pipeline.LiftParams = liftParams;
            // todo PipeLine.NoChangeVolumeTolerance  = FICOptions.NoChangeVolumeTolerance;

            // Perform the sub grid query and processing to render the tile
            var pipelineProcessorStopWatch = Stopwatch.StartNew();

            processor.Process();
            _log.LogInformation($"Pipeline processor completed in {pipelineProcessorStopWatch.Elapsed}");

            if (processor.Response.ResultStatus == RequestErrorStatus.OK)
            {
                // Render the collection of data in the aggregator
                var consistentRenderStopWatch = Stopwatch.StartNew();
                (Displayer as IProductionPVMConsistentDisplayer)?.PerformConsistentRender();
                _log.LogInformation($"Consistent render complated in {consistentRenderStopWatch.Elapsed}");

                PerformAnyRequiredDebugLevelDisplay();

                if (DebugDrawDiagonalCrossOnRenderedTilesDefault)
                {
                    // Draw diagonal cross and top left corner indicators
                    view.DrawLine(view.OriginX, view.OriginY, view.LimitX, view.LimitY, Color.Red);
                    view.DrawLine(view.OriginX, view.LimitY, view.LimitX, view.OriginY, Color.Red);

                    // Draw the horizontal line a little below the world coordinate 'top' of the tile to encourage the line
                    // drawing algorithm not to clip it
                    view.DrawLine(view.OriginX, view.LimitY, view.OriginX, view.CenterY, Color.Red);
                    view.DrawLine(view.OriginX, view.LimitY - 0.01, view.CenterX, view.LimitY - 0.01, Color.Red);
                }
            }

            return(processor.Response.ResultStatus);
        }