Two straight lines, sine, and arctan.
This set of functions can be used to make a pseudo-control that graphs simple equations of the form 'y = f(x)' (it's not actually a control, but just a bitmap drawn onto the GUI). Nearly everything is configurable, including the colors of all graphical elements, the viewable range of the graph, and the resolution of the grid.
;;;;;;;;;;;;;;; ; ; Function Grapher by jonny ; ; ;;;Functions;;; ; ;GraphCreate(id,x,y,w,h,pre="") ; Initializes the graph, sets its options, ; and draws it at the specified location. ; Returns 1 if it's successful, 0 if there's ; a logical error (e.g. bad option), and blank ; if there's a problem allocating memory. ; ; id: ; The window ID (ahk_id) of the window to ; draw the graph on. ; ; x: ; The x-coordinate to draw the graph at. ; ; y: ; The y-coordinate to draw the graph at. ; ; w: ; The width of the graph. ; ; h: ; The height of the graph. ; ; pre: ; The prefix for the global option variables, ; explained further below. ; For example, if pre = "GraphOpts_", then it ; will read GraphOpts_xScl for the xScl option ; ; ;Graph(equation,color=defGraphColor) ; Computes and draws the given equation on ; the graph. The equation must be in terms ; of the variable 'x', and it can contain any ; standard AHK operators and math functions. ; Returns 1 if it's successful, and 0 if ; the graph doesn't exist or if there's a ; problem with the computation. ; ; equation: ; The equation to use. This must be in the form ; of a string, NOT a bare expression; however, ; it will eventually be parsed, so it must be ; valid as an expression. ; ; color: ; The color to draw the equation in. This must ; be in RGB form (0xRRGGBB), not an HTML color. ; If this parameter is unspecified or not a valid ; RGB color, the defGraphColor option will be used. ; ; ;GraphClear() ; Clears all equations off the graph. ; ; ;GraphDraw() ; Redraws the graph. Shouldn't be necessary to ; use, since GraphCreate binds this to WM_PAINT. ; ; ;GraphDestroy() ; Frees all memory associated with the graph. ; You MUST call this before exiting the script, ; or there will be a memory leak. ; Note that this will not remove the graph's image ; from the display. The window must be redrawn ; to visually clear it. ; ; ; ;;;Options;;; ; ; To use these, set any of them as global variables ; with a consistent prefix (required). Then pass ; the prefix to GraphCreate as a string, via the ; 'pre' parameter. Any options left unset will get ; their default values. ; ; ;paperColor (0xFFFFFF - white) ; The color of the graph's background, or the ; "graph paper." Must be a full RGB color. ; ;axisColor (0x000000 - black) ; The color of the x and y axes, if visible. ; Must be a full RGB color. ; ;gridColor (0xDFDFDF - light grey) ; The color of the grid beneath the graph. ; Must be a full RGB color. ; ;defGraphColor (0x0000FF - blue) ; The default color for Graph(), if the 'color' ; parameter isn't valid or specified. ; ;lineWidth (3) ; The width of the lines generated by Graph(). ; Must be greater than or equal to zero. ; ;xScl (1) ; The scale of the x-plane of the grid, in ; logical units. For instance, a value of 10 ; would draw a vertical line wherever x is ; a multiple of 10, and a value of 0.5 would ; draw two vertical lines for every integral ; x-value. Must be greater than zero. ; ;yScl (1) ; The scale of the y-plane of the grid, in ; logical units. Same as above, except for ; the horizontal lines with y. Must be greater ; than zero. ; ;xMin (-10) ; The smallest x-value displayed on the graph, ; or the left edge of its viewable area. Must ; be less than xMax. ; ;yMin (-10) ; The smallest y-value displayed on the graph, ; or the bottom edge of its viewable area. Must ; be less than yMax. ; ;xMax (10) ; The largest x-value displayed on the graph, ; or the right edge of its viewable area. Must ; be greater than xMin. ; ;yMax (10) ; The largest y-value displayed on the graph, ; or the top edge of its viewable area. Must be ; greater than yMin. ; ; ; ;;;Remarks;;; ; ; Use this at your own risk. I've tested it a lot, ; but there's still a chance it'll cause a serious ; memory leak. ; ; Avoid naming any global variables with the prefix ; '__graph_' while the graph is created, since these ; functions use it to communicate. ; ; Graph() uses two temporary files which it creates ; in A_Temp, so don't be alarmed if you notice that ; your script is suddenly opening weird files. (It ; deletes them when it's finished, so they'll only ; consume space for a few milliseconds.) ; GraphCreate(WindowID,xCoord,yCoord,Width,Height,OptPre="") { global local paperColor,axisColor,gridColor,xScl,yScl,xMin,yMin ,xMax,yMax,Pen,Brush if __graph_Exists return 0 ;options ;background color (white) paperColor := %OptPre%paperColor ? %OptPre%paperColor : 0xFFFFFF ;color for axes (black) axisColor := %OptPre%axisColor ? %OptPre%axisColor : 0x000000 ;grid color (grey) gridColor := %OptPre%gridColor ? %OptPre%gridColor : 0xDFDFDF ;default color for new equations (blue) __graph_color := %OptPre%defGraphColor ? %OptPre%defGraphColor : 0x0000FF ;width of equations' graphs __graph_lineWidth := %OptPre%lineWidth ? %OptPre%lineWidth : 3 ;scale of grid for the x plane xScl := %OptPre%xScl ? %OptPre%xScl : 1 ;scale of grid for the y plane yScl := %OptPre%yScl ? %OptPre%yScl : 1 ;lowest x-value shown on graph xMin := %OptPre%xMin ? %OptPre%xMin : -10 ;lowest y-value shown on graph yMin := %OptPre%yMin ? %OptPre%yMin : -10 ;highest x-value shown on graph xMax := %OptPre%xMax ? %OptPre%xMax : 10 ;highest y-value shown on graph yMax := %OptPre%yMax ? %OptPre%yMax : 10 ;sanity checks if (paperColor > 0xFFFFFF || paperColor < 0 || axisColor > 0xFFFFFF || axisColor < 0 || gridColor > 0xFFFFFF || gridColor < 0 || __graph_color > 0xFFFFFF || __graph_color < 0 || __graph_lineWidth < 0 || xScl <= 0 || yScl <= 0 || xMin >= xMax || yMin >= yMax) { __graph_color = __graph_lineWidth = return 0 } __graph_X := xCoord __graph_Y := yCoord __graph_W := Width __graph_H := Height ;compute how many units on the graph each pixel ;corresponds to __graph_xUnit := Width / (xMax - xMin) __graph_yUnit := Height / (yMax - yMin) ;how far, in graph units, the left ;edge is from the origin __graph_leftDist := xMin * __graph_xUnit ;how far the top edge is from the ;origin, this time in terms of y __graph_topDist := yMax * __graph_yUnit ;Device Context for the window to be drawn to __graph_WindowDC := DllCall("GetDC", UInt,WindowID) if not __graph_WindowDC return ;first memory DC, containing the "graph paper," ;the background of the graph __graph_PaperDC := DllCall("CreateCompatibleDC", UInt,WindowDC) if not __graph_PaperDC return ;create a bitmap on the DC __graph_PaperDC_BM := DllCall("CreateCompatibleBitmap" , UInt,__graph_WindowDC, UInt,Width, UInt,Height) DllCall("SelectObject", UInt,__graph_PaperDC , UInt,__graph_PaperDC_BM) ;second Memory DC, containing the finished product ;of the paper with the graphed equations on it __graph_MemoryDC := DllCall("CreateCompatibleDC", UInt,WindowDC) if not __graph_MemoryDC return ;create a bitmap on the DC __graph_MemoryDC_BM := DllCall("CreateCompatibleBitmap" , UInt,__graph_WindowDC, UInt,Width, UInt,Height) DllCall("SelectObject", UInt,__graph_MemoryDC , UInt,__graph_MemoryDC_BM) ;set up the graph paper ;first the background and border Pen := DllCall("CreatePen", UInt,0, UInt,0, UInt,paperColor) DllCall("SelectObject", UInt,__graph_PaperDC, UInt,Pen) Brush := DllCall("CreateSolidBrush", UInt,paperColor) DllCall("SelectObject", UInt,__graph_PaperDC, UInt,Brush) DllCall("Rectangle", UInt,__graph_PaperDC, UInt,0, UInt,0 , UInt,Width, UInt,Height) DllCall("DeleteObject", UInt,Pen) DllCall("DeleteObject", UInt,Brush) ;now the grid Pen := DllCall("CreatePen", UInt,0, UInt,0, UInt,gridColor) DllCall("SelectObject", UInt,__graph_PaperDC, UInt,Pen) ;vertical lines (x-plane) Loop { if (A_Index >= ( (xMax - xMin) / xScl )) break DllCall("MoveToEx", UInt,__graph_PaperDC , UInt,Round(A_Index*__graph_xUnit*xScl), UInt,0, UInt,0) DllCall("LineTo", UInt,__graph_PaperDC , UInt,Round(A_Index*__graph_xUnit*xScl), UInt,Height) } ;horizontal lines (y-plane) Loop { if (A_Index >= ( (yMax - yMin) / yScl )) break DllCall("MoveToEx", UInt,__graph_PaperDC , UInt,0, UInt,Round(A_Index*__graph_yUnit*yScl), UInt,0) DllCall("LineTo", UInt,__graph_PaperDC , UInt,Width, UInt,Round(A_Index*__graph_yUnit*yScl)) } DllCall("DeleteObject", UInt,Pen) ;axes Pen := DllCall("CreatePen", UInt,0, UInt,0, UInt,axisColor) DllCall("SelectObject", UInt,__graph_PaperDC, UInt,Pen) if (xMin < 0 and xMax > 0) { DllCall("MoveToEx", UInt,__graph_PaperDC , UInt,Round((-xMin)*__graph_xUnit), UInt,0, UInt,0) DllCall("LineTo", UInt,__graph_PaperDC , UInt,Round((-xMin)*__graph_xUnit), UInt,Height) } if (yMin < 0 and yMax > 0) { DllCall("MoveToEx", UInt,__graph_PaperDC , UInt,0, UInt,Round(yMax*__graph_yUnit), UInt,0) DllCall("LineTo", UInt,__graph_PaperDC , UInt,Width, UInt,Round(yMax*__graph_yUnit)) } DllCall("DeleteObject", UInt,Pen) __graph_Exists := true GraphClear() ;WM_PAINT: 0x0F OnMessage(0x0F,"GraphDraw") return 1 } Graph(Equation,Color = -1) { global local EquDC,EquDC_BM,Pen,FirstVal,GraphSpec,R,G,B if (!__graph_Exists or !A_AhkPath) return 0 if (Color < 0 or Color > 0xFFFFFF) Color := __graph_color ;CreatePen expects a BGR-formatted COLORREF R := Color & 0x0000FF G := Color & 0x00FF00 B := Color & 0xFF0000 R <<= 16 B >>= 16 Color := R | G | B ;px/py: Positional coordinates used to ; place the graph ; ;x/y: Actual coordinates that the function ; is computed in ; ;Equation must be in terms of 'x' FileAppend, ( LTrim #NoTrayIcon Loop %__graph_W% { px := A_Index x := (px + %__graph_leftDist%) / %__graph_xUnit% y := (%Equation%) * %__graph_yUnit% py := Round(%__graph_topDist% - y) GraphSpec .= py . ";" } FileDelete gspec FileAppend,`%GraphSpec`%,gspec ),%A_Temp%\tempGraph.ahk RunWait,"%A_AhkPath%" "%A_Temp%\tempGraph.ahk",%A_Temp% FileRead,GraphSpec,%A_Temp%\gspec FileDelete %A_Temp%\tempGraph.ahk FileDelete %A_Temp%\gspec Pen := DllCall("CreatePen", UInt,0, UInt,__graph_lineWidth, UInt,Color) DllCall("SelectObject", UInt,__graph_MemoryDC, UInt,Pen) StringTrimRight,GraphSpec,GraphSpec,1 FirstVal := InStr(GraphSpec,";") DllCall("MoveToEx", UInt,__graph_MemoryDC, UInt,1, UInt,SubStr(GraphSpec,1,FirstVal-1), UInt,0) StringTrimLeft,GraphSpec,GraphSpec,FirstVal Loop,Parse,GraphSpec,; DllCall("LineTo", UInt,__graph_MemoryDC, UInt,A_Index + 1, UInt,A_LoopField) DllCall("DeleteObject", UInt,Pen) GraphDraw() return 1 } GraphClear() { global if not __graph_Exists return DllCall("BitBlt", UInt,__graph_MemoryDC, UInt,0, UInt,0 , UInt,__graph_W, UInt,__graph_H, UInt,__graph_PaperDC , UInt,0, UInt,0, UInt,0x00CC0020) GraphDraw() } GraphDraw() { global if not __graph_Exists return DllCall("BitBlt", UInt,__graph_WindowDC, UInt,__graph_X , UInt,__graph_Y, UInt,__graph_W, UInt,__graph_H , UInt,__graph_MemoryDC, UInt,0, UInt,0, UInt,0x00CC0020) } GraphDestroy() { global ;WM_PAINT: 0x0F OnMessage(0x0F,"") DllCall("DeleteObject", UInt,__graph_PaperDC) DllCall("DeleteObject", UInt,__graph_PaperDC_BM) DllCall("DeleteObject", UInt,__graph_MemoryDC) DllCall("DeleteObject", UInt,__graph_MemoryDC_BM) DllCall("ReleaseDC", UInt,0, UInt,__graph_WindowDC) __graph_PaperDC = __graph_PaperDC_BM = __graph_MemoryDC = __graph_MemoryDC_BM = __graph_WindowDC = __graph_X = __graph_Y = __graph_W = __graph_H = __graph_color = __graph_lineWidth = __graph_xUnit = __graph_yUnit = __graph_leftDist = __graph_topDist = __graph_Exists = }
There are a few quirks with it. The way you set options is kind of awkward, I might replace that with a function sometime... also, it uses several global variables. They have an obscure prefix, but this is still fundamentally bad style. They could probably be replaced with another function.
In any case, this is just the first version and there could be any number of things wrong with it, so don't rely heavily on its stability.
The GUI you see in the screenshot is not a direct product of these functions. You have to do everything else but the graph, including any kind of border you want around it (I used one-pixel wide Progress controls, though it didn't work out very well). Here's a script almost identical to the one I used to make the screenshot, except that you can specify the functions at runtime, rather than hardcoding Graph() calls:
#include grapher.ahk DetectHiddenWindows On OnExit GuiClose Gui Add,Progress,w402 h1 x10 y10 Gui Add,Progress,w1 h402 x10 y10 Gui Add,Progress,w402 h1 x10 y411 Gui Add,Progress,w1 h402 x411 y10 Process Exist WinGet ScriptID,ID,ahk_pid %ErrorLevel% Gui Show,w422 h422,Function Grapher GraphOpt_defGraphColor := 0xFF0000 ;red GraphCreate(ScriptID,11,11,400,400,"GraphOpt_") return GuiContextMenu: InputBox,Equation,Function Grapher,Enter an equation in terms of x: Graph(Equation) return GuiEscape: GraphClear() return GuiClose: GraphDestroy() ExitApp
Right-click on it to enter a new equation to graph, and press Escape to clear it. And of course, you need grapher.ahk in the same directory (change the #include if you use a different name).
Comments, critiques, and modifications welcome.