using System; using System.Drawing; using System.Windows.Forms; using HalconDotNet; namespace HDisplay.ViewROI { /// /// This class allows you to print a function into a Windows.Forms.Control. /// The function must be supplied as an array of either double, float or /// int values. When initializing the class you must provide the control that /// actually draws the function plot. During the initialization you /// can also decide whether the mouse event is used to return the (x,y) /// coordinates of the plotted function. The scaling /// of the x-y axis is performed automatically and depent on the length /// of the function as well as the settings for the y-axis scaling: /// * AXIS_RANGE_FIXED /// * AXIS_RANGE_INCREASING /// * AXIS_RANGE_ADAPTING /// /// Constraints of the function plot class: /// So far only functions containing a positive range of y-values can /// be plotted correctly. Also only a positive and ascending x-range /// can be plotted. The origin of the coordinate system is set to /// be in the lower left corner of the control. Another definition /// of the origin and hence the direction of the coordinate axis /// is not implemented yet. /// public class FunctionPlot { // panels for display private Graphics gPanel, backBuffer; // graphical settings private Pen pen, penCurve, penCursor; private SolidBrush brushCS, brushFuncPanel; private Font drawFont; private StringFormat format; private Bitmap functionMap; // dimensions of panels private float panelWidth; private float panelHeight; private float margin; // origin private float originX; private float originY; private PointF[] points; private HFunction1D func; // axis private int axisAdaption; private float axisXLength; private float axisYLength; private float scaleX, scaleY; public const int AXIS_RANGE_FIXED = 3; public const int AXIS_RANGE_INCREASING = 4; public const int AXIS_RANGE_ADAPTING = 5; int PreX, BorderRight, BorderTop; /// /// Initializes a FunctionPlot instance by providing a GUI control /// and a flag specifying the mouse interaction mode. /// The control is used to determine the available space to draw /// the axes and to plot the supplied functions. Depending on the /// available space, the values of the function as well as the axis /// steps are adjusted (scaled) to fit into the visible part of the /// control. /// /// /// An instance of the Windows.Forms.Control to plot the /// supplied functions in /// /// /// Flag that specifies whether or not mouse interaction should /// be used to create a navigation bar for the plotted function /// public FunctionPlot(Control panel, bool useMouseHandle) { gPanel = panel.CreateGraphics(); panelWidth = panel.Size.Width - 32; panelHeight = panel.Size.Height - 22; originX = 32; originY = panel.Size.Height - 22; margin = 5.0f; BorderRight = (int)(panelWidth + originX - margin); BorderTop = (int)panelHeight; PreX = 0; scaleX = scaleY = 0.0f; //default setting for axis scaling axisAdaption = AXIS_RANGE_ADAPTING; axisXLength = 10.0f; axisYLength = 10.0f; pen = new Pen(System.Drawing.Color.DarkGray, 1); penCurve = new Pen(System.Drawing.Color.Blue, 1); penCursor = new Pen(System.Drawing.Color.LightSteelBlue, 1); penCursor.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash; brushCS = new SolidBrush(Color.Black); brushFuncPanel = new SolidBrush(Color.White); drawFont = new Font("Arial", 6); format = new StringFormat(); format.Alignment = StringAlignment.Far; functionMap = new Bitmap(panel.Size.Width, panel.Size.Height); backBuffer = Graphics.FromImage(functionMap); resetPlot(); panel.Paint += new System.Windows.Forms.PaintEventHandler(this.paint); if (useMouseHandle) panel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.mouseMoved); } /// /// Convenience method for constructor call. For this case the /// useMouseHandle flag is set to false by default. /// /// /// An instance of the Windows.Forms.Control to plot the /// supplied function in. /// public FunctionPlot(Control panel) : this(panel, false) { } /// /// Changes the origin of the coordinate system to be /// at the control positions x and y. /// /// /// X position within the control coordinate system /// /// /// Y position within the control coordinate system. /// public void setOrigin(int x, int y) { float tmpX; if (x < 1 || y < 1) return; tmpX = originX; originX = x; originY = y; panelWidth = panelWidth + tmpX - originX; panelHeight = originY; BorderRight = (int)(panelWidth + originX - margin); BorderTop = (int)panelHeight; } /// /// Sets the type of scaling for the y-axis. If the /// y-axis is defined to be of fixed size, then the upper /// limit has to be provided with val. Otherwise, /// an 8-bit image is assumed, so the fixed size is set /// to be 255. /// /// /// Class constant starting with AXIS_RANGE_* /// /// /// For the mode AXIS_RANGE_FIXED the value /// val must be positive, otherwise /// it has no meaning. /// public void setAxisAdaption(int mode, float val) { switch (mode) { case AXIS_RANGE_FIXED: axisAdaption = mode; axisYLength = (val > 0) ? val : 255.0f; break; default: axisAdaption = mode; break; } } public void setAxisAdaption(int mode) { setAxisAdaption(mode, -1.0f); } /// /// Plots a function of double values. /// /// /// Y-values defined as an array of doubles /// public void plotFunction(double[] grayValues) { drawFunction(new HTuple(grayValues)); } /// /// Plots a function of float values. /// /// /// Y-values defined as an array of floats /// public void plotFunction(float[] grayValues) { drawFunction(new HTuple(grayValues)); } /// /// Plots a function of integer values. /// /// /// Y-values defined as an array of integers /// public void plotFunction(int[] grayValues) { drawFunction(new HTuple(grayValues)); } /// Plots a function provided as an HTuple private void drawFunction(HTuple tuple) { HTuple val; int maxY,maxX; float stepOffset; if (tuple.Length == 0) { resetPlot(); return; } val = tuple.TupleSortIndex(); maxX = tuple.Length - 1; maxY = (int)tuple[val[val.Length - 1].I].D; axisXLength = maxX; switch (axisAdaption) { case AXIS_RANGE_ADAPTING: axisYLength = maxY; break; case AXIS_RANGE_INCREASING: axisYLength = (maxY > axisYLength) ? maxY : axisYLength; break; } backBuffer.Clear(System.Drawing.Color.WhiteSmoke); backBuffer.FillRectangle(brushFuncPanel, originX, 0, panelWidth, panelHeight); stepOffset = drawXYLabels(); drawLineCurve(tuple, stepOffset); backBuffer.Flush(); gPanel.DrawImageUnscaled(functionMap, 0, 0); gPanel.Flush(); } /// /// Clears the panel and displays only the coordinate axes. /// public void resetPlot() { backBuffer.Clear(System.Drawing.Color.WhiteSmoke); backBuffer.FillRectangle(brushFuncPanel, originX, 0, panelWidth, panelHeight); func = null; drawXYLabels(); backBuffer.Flush(); repaint(); } /// /// Puts (=flushes) the current content of the graphics object on screen /// again. /// private void repaint() { gPanel.DrawImageUnscaled(functionMap, 0, 0); gPanel.Flush(); } /// Plots the points of the function. private void drawLineCurve(HTuple tuple, float stepOffset) { int length; if (stepOffset > 1) points = scaleDispValue(tuple); else points = scaleDispBlockValue(tuple); length = points.Length; func = new HFunction1D(tuple); for (int i = 0; i < length - 1; i++) backBuffer.DrawLine(penCurve, points[i], points[i + 1]); } /// /// Scales the function to the dimension of the graphics object /// (provided by the control). /// /// /// Function defined as a tuple of y-values /// /// /// Array of PointF values, containing the scaled function data /// private PointF[] scaleDispValue(HTuple tup) { PointF [] pVals; float xMax, yMax, yV, x, y; int length; xMax = axisXLength; yMax = axisYLength; scaleX = (xMax != 0.0f) ? ((panelWidth - margin) / xMax) : 0.0f; scaleY = (yMax != 0.0f) ? ((panelHeight - margin) / yMax) : 0.0f; length = tup.Length; pVals = new PointF[length]; for (int j=0; j < length; j++) { yV = (float)tup[j].D; x = originX + (float)j * scaleX; y = panelHeight - (yV * scaleY); pVals[j] = new PointF(x, y); } return pVals; } /// /// Scales the function to the dimension of the graphics object /// (provided by the control). If the stepsize for the x-axis is /// 1, the points are scaled in a block shape. /// /// /// Function defined as a tuple of y-values /// /// /// Array of PointF values, containing the scaled function data /// private PointF[] scaleDispBlockValue(HTuple tup) { PointF [] pVals; float xMax, yMax, yV,x,y; int length, idx; xMax = axisXLength; yMax = axisYLength; scaleX = (xMax != 0.0f) ? ((panelWidth - margin) / xMax) : 0.0f; scaleY = (yMax != 0.0f) ? ((panelHeight - margin) / yMax) : 0.0f; length = tup.Length; pVals = new PointF[length * 2]; y = 0; idx = 0; for (int j=0; j < length; j++) { yV = (float)tup[j].D; x = originX + (float)j * scaleX - (scaleX / 2.0f); y = panelHeight - (yV * scaleY); pVals[idx] = new PointF(x, y); idx++; x = originX + (float)j * scaleX + (scaleX / 2.0f); pVals[idx] = new PointF(x, y); idx++; } //trim the end points of the curve idx--; x = originX + (float)(length - 1) * scaleX; pVals[idx] = new PointF(x, y); idx = 0; yV = (float)tup[idx].D; x = originX; y = panelHeight - (yV * scaleY); pVals[idx] = new PointF(x, y); return pVals; } /// Draws x- and y-axis and its labels. /// Step size used for the x-axis private float drawXYLabels() { float pX,pY,length, XStart,YStart; float YCoord, XCoord, XEnd, YEnd, offset, offsetString, offsetStep; float scale = 0.0f; offsetString = 5; XStart = originX; YStart = originY; //prepare scale data for x-axis pX = axisXLength; if (pX != 0.0) scale = (panelWidth - margin) / pX; if (scale > 10.0) offset = 1.0f; else if (scale > 2) offset = 10.0f; else if (scale > 0.2) offset = 100.0f; else offset = 1000.0f; /*************** draw X-Axis ***************/ XCoord = 0.0f; YCoord = YStart; XEnd = (scale * pX); backBuffer.DrawLine(pen, XStart, YStart, XStart + panelWidth - margin, YStart); backBuffer.DrawLine(pen, XStart + XCoord, YCoord, XStart + XCoord, YCoord + 6); backBuffer.DrawString(0 + "", drawFont, brushCS, XStart + XCoord + 4, YCoord + 8, format); backBuffer.DrawLine(pen, XStart + XEnd, YCoord, XStart + XEnd, YCoord + 6); backBuffer.DrawString(((int)pX + ""), drawFont, brushCS, XStart + XEnd + 4, YCoord + 8, format); length = (int)(pX / offset); length = (offset == 10) ? length - 1 : length; for (int i=1; i <= length; i++) { XCoord = (float)(offset * i * scale); if ((XEnd - XCoord) < 20) continue; backBuffer.DrawLine(pen, XStart + XCoord, YCoord, XStart + XCoord, YCoord + 6); backBuffer.DrawString(((int)(i * offset) + ""), drawFont, brushCS, XStart + XCoord + 5, YCoord + 8, format); } offsetStep = offset; //prepare scale data for y-axis pY = axisYLength; if (pY != 0.0) scale = ((panelHeight - margin) / pY); if (scale > 10.0) offset = 1.0f; else if (scale > 2) offset = 10.0f; else if (scale > 0.8) offset = 50.0f; else if (scale > 0.12) offset = 100.0f; else offset = 1000.0f; /*************** draw Y-Axis ***************/ XCoord = XStart; YCoord = 5.0f; YEnd = YStart - (scale * pY); backBuffer.DrawLine(pen, XStart, YStart, XStart, YStart - (panelHeight - margin)); backBuffer.DrawLine(pen, XCoord, YStart, XCoord - 10, YStart); backBuffer.DrawString(0 + "", drawFont, brushCS, XCoord - 12, YStart - offsetString, format); backBuffer.DrawLine(pen, XCoord, YEnd, XCoord - 10, YEnd); backBuffer.DrawString(pY + "", drawFont, brushCS, XCoord - 12, YEnd - offsetString, format); length = (int)(pY / offset); length = (offset == 10) ? length - 1 : length; for (int i=1; i <= length; i++) { YCoord = (YStart - ((float)offset * i * scale)); if ((YCoord - YEnd) < 10) continue; backBuffer.DrawLine(pen, XCoord, YCoord, XCoord - 10, YCoord); backBuffer.DrawString(((int)(i * offset) + ""), drawFont, brushCS, XCoord - 12, YCoord - offsetString, format); } return offsetStep; } /// /// Action call for the Mouse-Move event. For the x-coordinate /// supplied by the MouseEvent, the unscaled x and y coordinates /// of the plotted function are determined and displayed /// on the control. /// private void mouseMoved(object sender, MouseEventArgs e) { int Xh, Xc; HTuple Ytup; float Yh, Yc; Xh = e.X; if (PreX == Xh || Xh < originX || Xh > BorderRight || func == null) return; PreX = Xh; Xc = (int)Math.Round((Xh - originX) / scaleX); Ytup = func.GetYValueFunct1d(new HTuple(Xc), "zero"); Yc = (float)Ytup[0].D; Yh = panelHeight - (Yc * scaleY); gPanel.DrawImageUnscaled(functionMap, 0, 0); gPanel.DrawLine(penCursor, Xh, 0, Xh, BorderTop); gPanel.DrawLine(penCursor, originX, Yh, BorderRight + margin, Yh); gPanel.DrawString(("X = " + Xc), drawFont, brushCS, panelWidth - margin, 10); gPanel.DrawString(("Y = " + (int)Yc), drawFont, brushCS, panelWidth - margin, 20); gPanel.Flush(); } /// /// Action call for the Paint event of the control to trigger the /// repainting of the function plot. /// private void paint(object sender, System.Windows.Forms.PaintEventArgs e) { repaint(); } }//end of class }//end of namespace