├── Documentation └── img │ ├── BarPlot Glass.png │ ├── BarPlot Simple x.png │ ├── BarPlot Simple y.png │ ├── BarPlot X.png │ ├── DashArray - 0.png │ ├── DashArray - 5,1,5.png │ ├── DashArray - 5,1.png │ ├── DashArray - 5.png │ ├── Example Plot 5.png │ ├── Example Plot c.png │ ├── Example Plot x.png │ ├── Example Plot y.png │ ├── Grid.png │ ├── Legend1.png │ ├── LegendedCurves.png │ ├── Main.png │ ├── Scatter Plot AHK.png │ ├── Sin Mirrored.png │ ├── Sin Normal.png │ └── Sin On Head.png ├── README.md ├── SVGraph test.ahk ├── SVGraph.ahk ├── SVGraph.html └── SVGraph.js /Documentation/img/BarPlot Glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/BarPlot Glass.png -------------------------------------------------------------------------------- /Documentation/img/BarPlot Simple x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/BarPlot Simple x.png -------------------------------------------------------------------------------- /Documentation/img/BarPlot Simple y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/BarPlot Simple y.png -------------------------------------------------------------------------------- /Documentation/img/BarPlot X.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/BarPlot X.png -------------------------------------------------------------------------------- /Documentation/img/DashArray - 0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/DashArray - 0.png -------------------------------------------------------------------------------- /Documentation/img/DashArray - 5,1,5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/DashArray - 5,1,5.png -------------------------------------------------------------------------------- /Documentation/img/DashArray - 5,1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/DashArray - 5,1.png -------------------------------------------------------------------------------- /Documentation/img/DashArray - 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/DashArray - 5.png -------------------------------------------------------------------------------- /Documentation/img/Example Plot 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Example Plot 5.png -------------------------------------------------------------------------------- /Documentation/img/Example Plot c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Example Plot c.png -------------------------------------------------------------------------------- /Documentation/img/Example Plot x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Example Plot x.png -------------------------------------------------------------------------------- /Documentation/img/Example Plot y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Example Plot y.png -------------------------------------------------------------------------------- /Documentation/img/Grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Grid.png -------------------------------------------------------------------------------- /Documentation/img/Legend1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Legend1.png -------------------------------------------------------------------------------- /Documentation/img/LegendedCurves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/LegendedCurves.png -------------------------------------------------------------------------------- /Documentation/img/Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Main.png -------------------------------------------------------------------------------- /Documentation/img/Scatter Plot AHK.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Scatter Plot AHK.png -------------------------------------------------------------------------------- /Documentation/img/Sin Mirrored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Sin Mirrored.png -------------------------------------------------------------------------------- /Documentation/img/Sin Normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Sin Normal.png -------------------------------------------------------------------------------- /Documentation/img/Sin On Head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CapnOdin/SVGraph/c7a706e77862b86e19211a82f3da9d99420f1022/Documentation/img/Sin On Head.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Main Image Plot](https://github.com/CapnOdin/SVGraph/blob/master/Documentation/img/Main.png) 2 | **How it works:** An ActiveX Control consisting of an IE Object is used to execute and show JavaScript which makes a SVG using the library [D3](https://d3js.org). 3 | 4 | **You will need:** The three source files (_SVGraph.ahk_, _SVGraph.js_, _SVGraph.html_) and the library [D3 v4](https://d3js.org/d3.v4.js) in the same folder, IE11 and maybe the newest [AHK version](https://autohotkey.com/download/). 5 | 6 | **Documentation:** See the [Wiki](https://github.com/CapnOdin/SVGraph/wiki). 7 | -------------------------------------------------------------------------------- /SVGraph test.ahk: -------------------------------------------------------------------------------- 1 | SetBatchLines, -1 2 | #include SVGraph.ahk 3 | #include *i 4 | 5 | Plots := [], locked := False 6 | 7 | Gui, 1:New, +Resize, SVGraph 8 | Gui, Add, Text, x+m y+m, FunX 9 | Gui, Add, Combobox, ys-2 w200 vEditX, x||Math.sin(x)|Math.cos(x)|Math.pow(x,2)/100 + 0.1|Math.pow(x,3)/100 + Math.pow(x,2)/100 + x/100 10 | Gui, Add, Text, ys x+20, FunY 11 | Gui, Add, Combobox, ys-2 w200 vEditY, Math.sin(x)||Math.cos(x)|Math.pow(x,2)/100 + 0.1|Math.pow(x,3)/100 + Math.pow(x,2)/100 + x/100|x 12 | Gui, Add, Text, ys x+20, Colour 13 | Gui, Add, Combobox, ys-2 w70 vColour, #0000FF||#FF0000|#00FF00 14 | Gui, Add, Text, ys x+20, Resolution 15 | Gui, Add, Edit, ys-2 w70 vResolution, 200 16 | Gui, Add, Text, ys x+20, Axis 17 | Gui, Add, Combobox, ys-2 w70 vAxis, x||y|[min,max] 18 | Gui, Add, Checkbox, ys+3 x+15 vOptimize, Optimize 19 | Gui, Add, Button, ys-2 x+5 gPlot, Plot 20 | 21 | Gui, Add, Button, ys-2 x+20 gDeletePlt, Delete 22 | 23 | Gui, Add, Text, xs+m Section, xmin 24 | Gui, Add, Edit, ys-2 w70 vxmin gAxes, -10 25 | Gui, Add, Text, ys, xmax 26 | Gui, Add, Edit, ys-2 w70 vxmax gAxes, 10 27 | Gui, Add, Text, ys, ymin 28 | Gui, Add, Edit, ys-2 w70 vymin gAxes, -1 29 | Gui, Add, Text, ys, ymax 30 | Gui, Add, Edit, ys-2 w70 vymax gAxes, 1 31 | Gui, Add, Checkbox, ys+3 x+15 vboxed gAxes, Boxed 32 | Gui, Add, Checkbox, ys+3 x+15 vGrid gGrid, Grid 33 | 34 | Gui, Add, Button, ys-2 x+5 gShowMsg vScatter, ScatterImage 35 | 36 | Gui, Add, Button, ys-2 x+5 gShowMsg vLable, Labels, Ticks and Margin 37 | 38 | Gui, Add, Button, ys-2 x+5 vSave gSave, Save 39 | 40 | Gui, Add, Checkbox, ys+3 x+20 vScrollvar gScroll, Scroll 41 | 42 | ; Call sequence needed before making any plots 43 | Gui, Add, ActiveX, xs w500 h500 vIE, Shell.Explorer 44 | SVGraph_Attach(IE) 45 | SVGraph_Start() 46 | 47 | SVGraph_Chart(960, 500, 40) 48 | 49 | SVGraph_ShowScrollbar(False) 50 | 51 | Gui, Show, AutoSize 52 | 53 | ;-------------------------------- MSGs ---------------------------------------------- 54 | 55 | Gui, Scatter:+Owner1 56 | Gui, Scatter:Font, s20 57 | Gui, Scatter:Add, Text, , Scatter Image 58 | Gui, Scatter:Font, s9 59 | Gui, Scatter:Add, Text, y+m, Image: 60 | Gui, Scatter:Add, Edit, x+m vScatterImage 61 | Gui, Scatter:Add, Button, x+m gSelectImage, Browse 62 | Gui, Scatter:Add, Text, xs, Resolution: 63 | Gui, Scatter:Add, Edit, x+m w70 vScatterResolution, 200 64 | Gui, Scatter:Add, Text, xs, Circle Size: 65 | Gui, Scatter:Add, Edit, x+m w40 vScatterSize, 3 66 | Gui, Scatter:Add, Text, xs, Background Colour: 67 | Gui, Scatter:Add, Edit, x+m vScatterBackground, #FFFFFF 68 | 69 | Gui Scatter:Add, Button, xs gScatter, Scatter 70 | Gui Scatter:Add, Button, x+m gCancel, Cancel 71 | 72 | 73 | Gui, Lable:+Owner1 74 | Gui, Lable:Font, s20 75 | Gui, Lable:Add, Text, , Labels and Ticks 76 | Gui, Lable:Font, s9 77 | Gui, Lable:Add, Text, xs, x-lable: 78 | Gui, Lable:Add, Edit, x+m w70 vLableXlable, 79 | Gui, Lable:Add, Text, xs, y-lable: 80 | Gui, Lable:Add, Edit, x+m w70 vLableYlable, 81 | Gui, Lable:Add, Text, xs y+40, Axis: 82 | Gui, Lable:Add, Combobox, x+m w70 vLableXorY, ||x|y 83 | Gui, Lable:Add, Text, xs, Approx # of Ticks: 84 | Gui, Lable:Add, Combobox, x+m w70 vLableMajor, 10||[-5,0,5] 85 | Gui, Lable:Add, Text, xs, # of Minor Ticks: 86 | Gui, Lable:Add, Edit, x+m w40 vLableMinor, 0 87 | Gui, Lable:Add, Text, xs, Grid Colour: 88 | Gui, Lable:Add, Combobox, x+m vLableColour, lightgrey||#FF00FF 89 | Gui, Lable:Add, Text, xs, Grid Style: 90 | Gui, Lable:Add, Combobox, x+m vLableDasharray, 5||0|1,1,1,5,3,3 91 | 92 | Gui, Lable:Add, Text, xs y+40, Margin: 93 | Gui, Lable:Add, Combobox, x+m w150 vLableMargin, 40||{top: , left: , bottom: , right: } 94 | 95 | Gui, Lable:Add, Button, xs gLable, Set 96 | Gui, Lable:Add, Button, x+m gCancel, Cancel 97 | return 98 | 99 | ;------------------------------------------------------------------------------------- 100 | 101 | GuiSize: 102 | if(!locked){ 103 | GuiControlGet, top, Pos, Save 104 | GuiControlGet, side, Pos, IE 105 | GuiControl, Move, IE, % "w" A_GuiWidth - 2*sideX " h" A_GuiHeight - (topY + 2*sideX) 106 | SVGraph_UpdateChart(A_GuiWidth - 2*sideX - 20, A_GuiHeight - topY - 2*sideX - 20) 107 | SetTimer, Axes, -100 108 | } 109 | return 110 | 111 | Axes: 112 | if(!locked){ 113 | Gui, Submit, NoHide 114 | SVGraph_SetAxes(xmin, xmax, ymin, ymax, boxed) 115 | SVGraph_RemovePath() 116 | IE.Document.parentWindow.eval("plot.ID = 0;") 117 | for index, plot in Plots { 118 | SVGraph_LinePlot(plot[1], plot[2], plot[3], plot[4], plot[5], plot[6]) 119 | } 120 | } 121 | return 122 | 123 | Scroll: 124 | Gui, Submit, NoHide 125 | SVGraph_ShowScrollbar(Scrollvar) 126 | return 127 | 128 | Grid: 129 | Gui, Submit, NoHide 130 | SVGraph_ShowGrid(Grid) 131 | return 132 | 133 | Save: 134 | Gui, Submit, NoHide 135 | if("D" != FileExist("\SVGs")){ 136 | FileCreateDir, SVGs 137 | } 138 | FileSelectFile, FileName, S16, % "\SVGs\" Get_Unique_Name("SVGs\plot.svg"), Save SVG, *.svg 139 | if(FileName){ 140 | SVGraph_SaveSVG(FileName) 141 | } 142 | return 143 | 144 | Plot: 145 | Gui, Submit, NoHide 146 | SVGraph_LinePlot(EditX, EditY, Colour ? Colour : "#999", Resolution, Axis, Optimize) 147 | Plots.Push([EditX, EditY, Colour ? Colour : "#999", Resolution, Axis, Optimize]) 148 | return 149 | 150 | DeletePlt: 151 | Plots.Pop() 152 | SVGraph_RemovePath(-1) 153 | return 154 | 155 | ;-------------------------------- MSGs ---------------------------------------------- 156 | 157 | ShowMsg: 158 | GuiControlGet, Ctrl, FocusV 159 | Gui, %Ctrl%:Show, AutoSize 160 | return 161 | 162 | Cancel: 163 | Gui, %A_Gui%:Show, Hide 164 | return 165 | 166 | SelectImage: 167 | FileSelectFile, image, , , Select Image 168 | GuiControl, , ScatterImage, %image% 169 | return 170 | 171 | Scatter: 172 | Gui, Scatter:Submit 173 | ScatterImage(ScatterImage, ScatterResolution, ScatterSize, ScatterBackground) 174 | return 175 | 176 | Lable: 177 | Gui, Lable:Submit 178 | SVGraph_UpdateChart(, , LableMargin) 179 | SVGraph_SetLabels(LableXlable, LableYlable) 180 | SVGraph_SetGrid(LableXorY, LableMajor, LableMinor, LableColour, LableDasharray) 181 | return 182 | 183 | ;------------------------------------------------------------------------------------- 184 | 185 | GuiClose: 186 | GuiEscape: 187 | ExitApp 188 | 189 | ScatterImage(image, axisResolution, size := 3, background := "#FFFFFF"){ 190 | Global locked 191 | Static Gdip_Startup := Func("Gdip_Startup") 192 | , Gdip_CreateBitmapFromFile := Func("Gdip_CreateBitmapFromFile") 193 | , Gdip_GetImageWidth := Func("Gdip_GetImageWidth") 194 | , Gdip_GetImageHeight := Func("Gdip_GetImageHeight") 195 | , Gdip_GetPixel := Func("Gdip_GetPixel") 196 | , Gdip_DisposeImage := Func("Gdip_DisposeImage") 197 | , Gdip_Shutdown := Func("Gdip_Shutdown") 198 | 199 | if(Gdip_Startup && Gdip_CreateBitmapFromFile && Gdip_GetImageWidth && Gdip_GetImageHeight && Gdip_GetPixel && Gdip_DisposeImage && Gdip_Shutdown){ 200 | locked := True 201 | icon := {} 202 | pToken := Gdip_Startup.Call() 203 | pBitmap := Gdip_CreateBitmapFromFile.Call(image) 204 | xmax := Gdip_GetImageWidth.Call(pBitmap) 205 | ymax := Gdip_GetImageHeight.Call(pBitmap) 206 | step := (xmax > ymax ? xmax : ymax) / axisResolution 207 | 208 | SVGraph_UpdateChart(xmax, ymax) 209 | SVGraph_SetAxes(0, xmax, 0, ymax, True) 210 | 211 | loop %axisResolution% { 212 | y := (A_Index - 1) * step 213 | loop %axisResolution% { 214 | x := (A_Index - 1) * step 215 | if(((c := ARGBtoRGB(Gdip_GetPixel.Call(pBitmap, x, y)))[1] != 0) && c[2] != background){ 216 | if(!icon.HasKey(c[2])){ 217 | icon[c[2]] := {} 218 | } 219 | if(!icon[c[2]].HasKey(c[1])){ 220 | icon[c[2]][c[1]] := {"x" : [], "y" : []} 221 | } 222 | icon[c[2]][c[1]]["x"].Push(x) 223 | icon[c[2]][c[1]]["y"].Push(ymax - y) 224 | } 225 | } 226 | ToolTip, % (y < ymax ? y / ymax : 1) * 100 . "%" 227 | } 228 | Gdip_DisposeImage.Call(pBitmap) 229 | Gdip_Shutdown.Call(pToken) 230 | 231 | Group := SVGraph_Group("image") 232 | 233 | for colour, list in icon { 234 | for opacity, coords in list { 235 | SVGraph_ScatterPlot(coords["x"], coords["y"], colour, size, opacity, False, Group) 236 | } 237 | } 238 | ToolTip 239 | } else { 240 | MsgBox, Can't find Gdip.ahk in your Library. 241 | } 242 | locked := False 243 | } 244 | 245 | ARGBtoRGB(ARGB) { 246 | ARGB := Format("0x{:08X}", ARGB) 247 | Return [SubStr(ARGB, 1, 4) / 0xFF, Format("#{:06X}", "0x" SubStr(ARGB, 5))] 248 | } 249 | 250 | Get_Unique_Name(FileName, Directory := False){ 251 | SplitPath, FileName, , Dir, Ext, Name 252 | if(!Directory){ 253 | Directory := InStr(Dir, ":") ? Dir : A_WorkingDir "\" Dir 254 | } 255 | if(SubStr(Directory, 0) != "\"){ 256 | Directory .= "\" 257 | } 258 | While Exist(Directory . Name . (index ? " (" . index . ")." : ".") . Ext) { 259 | index := A_Index 260 | } 261 | Return Name . (index ? " (" . index . ")." : ".") . Ext 262 | } 263 | 264 | Exist(path){ 265 | IfExist, %path% 266 | Return 1 267 | Return 0 268 | } 269 | -------------------------------------------------------------------------------- /SVGraph.ahk: -------------------------------------------------------------------------------- 1 |  2 | SVGraph_Attach(ActiveX := False){ 3 | Static IE 4 | if(ActiveX){ 5 | IE := ActiveX 6 | } 7 | return IE 8 | } 9 | 10 | SVGraph_Start(dir := ""){ 11 | SVGraph_Attach().Navigate((dir ? dir : A_ScriptDir) "\SVGraph.html") 12 | While SVGraph_Attach().ReadyState != 4 || SVGraph_Attach().Busy { 13 | Sleep, 20 14 | } 15 | } 16 | 17 | SVGraph_Chart(Width, Height, Margin := 40){ 18 | Width := (__IsNum(Width) ? Width : 500), Height := (__IsNum(Height) ? Height : 500), Margin := (__IsNum(Margin) ? Margin : 40) 19 | SVGraph_Attach().Document.parentWindow.eval("var plot = new Chart(" Width "," Height "," Margin ");") 20 | } 21 | 22 | ;--------------------------------------------------------------------------------------------------------------------------------- 23 | 24 | SVGraph_UpdateChart(Width := "", Height := "", Margin := ""){ 25 | SVGraph_Attach().Document.parentWindow.eval("plot.Update(" __IsDefined(Width) "," __IsDefined(Height) "," __IsDefined(Margin) ");") 26 | } 27 | 28 | SVGraph_RemovePath(index := ""){ 29 | SVGraph_Attach().Document.parentWindow.eval("plot.RemovePath(" __IsDefinedNum(index) ");") 30 | } 31 | 32 | SVGraph_ShowGrid(Bool := True){ 33 | SVGraph_Attach().Document.parentWindow.eval("plot.Axes.Grid(" (Bool = True) ");") 34 | } 35 | 36 | SVGraph_ShowScrollbar(Bool := False){ 37 | SVGraph_Attach().Document.body.style.overflow := Bool ? "visible" : "hidden" 38 | } 39 | 40 | SVGraph_ShowAxes(Bool := True){ 41 | SVGraph_Attach().Document.parentWindow.eval("plot.Axes.HideAxes(" (Bool = True) ");") 42 | } 43 | 44 | ;--------------------------------------------------------------------------------------------------------------------------------- 45 | 46 | SVGraph_SetAxes(xmin := "", xmax := "", ymin := "", ymax := "", Boxed := False){ 47 | xmin := __IsDefinedNum(xmin), xmax := __IsDefinedNum(xmax), ymin := __IsDefinedNum(ymin), ymax := __IsDefinedNum(ymax) 48 | SVGraph_Attach().Document.parentWindow.eval("plot.SetAxes(" xmin "," xmax "," ymin "," ymax "," Boxed ");") 49 | } 50 | 51 | SVGraph_SetGrid(xory := "", Major := "", Minor := "", Colour := "", DashArray := ""){ 52 | SVGraph_Attach().Document.parentWindow.eval("plot.Axes.SetGrid(""" xory """," __IsDefined(Major) "," __IsDefined(Minor) "," __IsDefined("""" Colour """") "," __IsDefined("""" DashArray """") ");") 53 | } 54 | 55 | SVGraph_SetLabels(xLabel := "", yLabel := ""){ 56 | SVGraph_Attach().Document.parentWindow.eval("plot.Axes.SetLabels(" __IsDefined("""" xLabel """") "," __IsDefined("""" yLabel """") ");") 57 | } 58 | 59 | SVGraph_MakeLegend(Data := "", Colour := ""){ 60 | StrColor := Colour ? ObjectToString(Colour) : "undefined" 61 | StrData := Data ? ObjectToString(Data) : "undefined" 62 | 63 | SVGraph_Attach().Document.parentWindow.eval("plot.MakeLegend(" StrData "," StrColor ");") 64 | } 65 | 66 | ;--------------------------------------------------------------------------------------------------------------------------------- 67 | 68 | SVGraph_LinePlot(FunX, FunY, Colour := "#999", Width := 4, Resolution := 0, Axis := "x", Optimize := True){ 69 | Axis := InStr(Axis, "[") ? Axis : """" Axis """" 70 | SVGraph_Attach().Document.parentWindow.eval("plot.LinePlot(""" FunX """,""" FunY """,""" Colour """,""" Width """," Resolution "," Axis "," Optimize ");") 71 | } 72 | 73 | SVGraph_LinePlot2(LstX, LstY, Colour := "#999", Width := 4, ScaleAxes := False, Curve := ""){ 74 | StrX := ObjectToString(LstX), StrY := ObjectToString(LstY), Curve := __IsDefined("""" Curve """") 75 | SVGraph_Attach().Document.parentWindow.eval("plot.LinePlot2(" StrX "," StrY ",""" Colour """,""" Width """," ScaleAxes "," Curve ");") 76 | } 77 | 78 | SVGraph_ScatterPlot(LstX, LstY, Colour := "#999", Size := 4, Opacity := 1, ScaleAxes := False, Group := False){ 79 | Group := Group ? Group : "undefined" 80 | StrX := ObjectToString(LstX), StrY := ObjectToString(LstY) 81 | SVGraph_Attach().Document.parentWindow.eval("plot.ScatterPlot(" StrX "," StrY ",""" Colour """,""" Size """," Opacity "," ScaleAxes "," Group ");") 82 | } 83 | 84 | SVGraph_BarPlot(Data, Colour := "", Width := 10, Axis := "x", Opacity := 1){ 85 | StrColor := Colour ? ObjectToString(Colour) : "undefined" 86 | StrData := ObjectToString(Data) 87 | SVGraph_Attach().Document.parentWindow.eval("plot.BarPlot(""" Axis """," StrData "," StrColor ",""" Width """," Opacity ");") 88 | } 89 | 90 | ;--------------------------------------------------------------------------------------------------------------------------------- 91 | 92 | SVGraph_SaveSVG(Filename){ 93 | Critical 94 | SVG := FileOpen(Filename, "w") 95 | SVG.Write(SVGraph_FormatXML(SVGraph_Attach().Document.getElementById("svg").outerHTML)) 96 | SVG.Close() 97 | } 98 | 99 | SVGraph_Group(ID){ 100 | SVGraph_Attach().Document.parentWindow.eval("var " ID " = plot.NewGroup(""" ID """);") 101 | return ID 102 | } 103 | 104 | SVGraph_FormatXML(XML){ 105 | SvgLst := StrSplit(RegExReplace(XML, "(`r|`n|`t)*"), "<") 106 | SvgLst.Delete(1) 107 | XML := "" 108 | for index, str in SvgLst { 109 | newLn := True 110 | str := "<" str 111 | RegExMatch(str, "<.*?>", tag) 112 | 113 | if(InStr(tag, "") && !InStr(tag, "" 126 | } 127 | preTag := tag 128 | } else { 129 | preTag := "" 130 | } 131 | } 132 | return SubStr(XML, 2) 133 | } 134 | 135 | SVGraph_MiniFormatXML(XML){ 136 | SvgLst := StrSplit(RegExReplace(XML, "(`r|`n|`t)*"), "<") 137 | SvgLst.Delete(1) 138 | XML := "", opacity := True, bool := False 139 | for index, str in SvgLst { 140 | newLn := True 141 | str := "<" str 142 | RegExMatch(str, "<.*?>", tag) 143 | 144 | if(InStr(tag, "opacity=""0""") && !InStr(tag, "/>")){ 145 | opacity := False 146 | if(pos := InStr(tag, " ")){ 147 | opacityTag := SubStr(tag, 1, pos - 1) ">" 148 | } 149 | } 150 | 151 | if(InStr(tag, "") && !InStr(tag, "" 169 | } 170 | preTag := tag 171 | } else { 172 | preTag := "" 173 | } 174 | } 175 | return SubStr(XML, 2) 176 | } 177 | 178 | __IsNum(Num){ 179 | if Num is Number 180 | return 1 181 | return 0 182 | } 183 | 184 | __IsDefined(val){ 185 | if(val = "" || val = """""") 186 | return "undefined" 187 | return val 188 | } 189 | 190 | __IsDefinedNum(Num){ 191 | if Num is Number 192 | return Num 193 | return "undefined" 194 | } 195 | 196 | ObjectToString(obj){ 197 | if(!IsObject(obj)){ 198 | return __IsNum(obj) ? obj : """" obj """" 199 | } 200 | res .= "[" 201 | for key, value in obj { 202 | res .= ObjectToString(value) "," 203 | } 204 | return SubStr(res, 1, -1) "]" 205 | } 206 | 207 | -------------------------------------------------------------------------------- /SVGraph.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /SVGraph.js: -------------------------------------------------------------------------------- 1 | function Chart(width, height, margin) { 2 | if(typeof margin === "object"){ 3 | this.margin = margin; 4 | } else { 5 | margin = margin < 40 ? 40 : margin; 6 | this.margin = {top: margin, left: margin, bottom: margin, right: margin}; 7 | } 8 | this.SvgWidth = width; this.SvgHeight = height; 9 | this.width = width - this.margin.left - this.margin.right; 10 | this.height = height - this.margin.top - this.margin.bottom; 11 | this.svg = d3.select("svg") 12 | .attr("width", width) 13 | .attr("height", height); 14 | this.ID = 0; 15 | 16 | var filter = this.svg.append("filter").attr("id", "dropShadow"); 17 | filter.append("feGaussianBlur").attr("in", "SourceAlpha").attr("stdDeviation", 3); 18 | //filter.append("feOffset").attr("dx", 0).attr("dy", 0); 19 | var feMerge = filter.append("feMerge"); 20 | feMerge.append("feMergeNode"); 21 | feMerge.append("feMergeNode").attr("in", "SourceGraphic"); 22 | 23 | this.Curves = { Ba: d3.curveBasis, 24 | Bu: d3.curveBundle, 25 | Car: d3.curveCardinal, 26 | Cat: d3.curveCatmullRom, 27 | L: d3.curveLinear, 28 | MX: d3.curveMonotoneX, 29 | MY: d3.curveMonotoneY, 30 | N: d3.curveNatural, 31 | S: d3.curveStep, 32 | SA: d3.curveStepAfter, 33 | SB: d3.curveStepBefore}; 34 | 35 | /*this.svg.append("polygon") 36 | .attr("fill-rule", "evenodd") 37 | .attr("fill", "#FF00FF") 38 | .attr("opacity", "0.5"); */ 39 | 40 | this.Axes = new Axes(this.width, this.height, this.margin); 41 | this.Data = new Data(); 42 | 43 | this.Charts = this.svg.append("g") 44 | .attr("id", "charts"); 45 | 46 | this.Legend = this.svg.append("g") 47 | .attr("id", "legend") 48 | .attr("opacity", 0) 49 | .on("mouseup", function(){plot.Legend.move = false;}) 50 | .on("mousedown", function(){plot.Legend.move = true;}) 51 | .on("mousemove", function(){ 52 | if(plot.Legend.move){ 53 | plot.Legend.attr("transform", "translate(" + (event.clientX - plot.Legend.width/2) + "," + (event.clientY - plot.Legend.height/2) + ")"); 54 | } 55 | }); 56 | 57 | this.Legend.rect = this.Legend.append("rect") 58 | .attr("stroke", "black") 59 | .attr("fill", "lightgrey"); 60 | 61 | this.Legend.move = false; 62 | 63 | this.Update = function(width, height, margin){ 64 | 65 | this.SvgWidth = width != undefined ? width : this.SvgWidth; 66 | this.SvgHeight = height != undefined ? height : this.SvgHeight; 67 | 68 | if(typeof margin === "object"){ 69 | this.margin = margin; 70 | } else if(margin != undefined) { 71 | margin = margin < 40 ? 40 : margin; 72 | this.margin = {top: margin, left: margin, bottom: margin, right: margin}; 73 | } 74 | 75 | this.width = this.SvgWidth - this.margin.left - this.margin.right; 76 | this.height = this.SvgHeight - this.margin.top - this.margin.bottom; 77 | 78 | this.svg.attr("width", this.SvgWidth); 79 | this.svg.attr("height", this.SvgHeight); 80 | 81 | /* this.svg.select("polygon") 82 | .attr("points", toPoints([[0, 0] 83 | , [this.width + this.margin.left + this.margin.right, 0] 84 | , [this.width + this.margin.left + this.margin.right, this.height + this.margin.top + this.margin.bottom] 85 | , [0, this.height + this.margin.top + this.margin.bottom] 86 | , [0, 0] 87 | , [this.margin.left, this.margin.top] 88 | , [this.width + this.margin.left, this.margin.top] 89 | , [this.width + this.margin.left, this.height + this.margin.top] 90 | , [this.margin.left, this.height + this.margin.top] 91 | , [this.margin.left, this.margin.top]]) 92 | ); */ 93 | 94 | this.Axes.Update(this.width, this.height, this.margin); 95 | }; 96 | 97 | this.RemovePath = function(index){ 98 | if(index == undefined) { 99 | this.svg.selectAll("#charts > .graph").remove(); 100 | } else { 101 | var graphs = this.svg.selectAll("#charts > .graph"); 102 | index = index < 0 ? graphs._groups[0].length + index : index; 103 | graphs.select(function(d, i){return i == index ? this : null;}).remove(); 104 | } 105 | }; 106 | 107 | this.SetAxes = function(xmin, xmax, ymin, ymax, Boxed) { 108 | xmin = xmin == undefined ? this.Axes.x.min() : xmin; 109 | xmax = xmax == undefined ? this.Axes.x.max() : xmax; 110 | ymin = ymin == undefined ? this.Axes.y.min() : ymin; 111 | ymax = ymax == undefined ? this.Axes.y.max() : ymax; 112 | this.Axes.Set([xmin, xmax], [ymin, ymax], Boxed); 113 | }; 114 | 115 | this.NewGroup = function(ID, InterAct){ 116 | var g = this.Charts.append("g") 117 | .attr("id", InterAct ? ID + "-" + this.ID : ID) 118 | .attr("class", "graph") 119 | .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")"); 120 | if(InterAct){ 121 | ID = "#" + ID + "-" + this.ID; 122 | g.on("mouseenter", function(){d3.select(ID + " > #circles").attr("opacity", 1); 123 | d3.select(ID + " > path").attr("filter", "url(#dropShadow)");}); 124 | g.on("mouseleave", function(){d3.select(ID + " > #circles").attr("opacity", 0); 125 | d3.select(ID + " > path").attr("filter", null);}); 126 | this.ID += 1; 127 | } 128 | return g; 129 | }; 130 | 131 | this.LinePlot = function(FunX, FunY, Colour, Width, Resolution, Range, Optimize) { 132 | if(!this.Axes.Exist){ 133 | this.SetAxes(-100, 100, -100, 100) 134 | } 135 | var Axis = Range; 136 | if(!Array.isArray(Range)){ 137 | Range = [(Range == "x" ? this.Axes.x.min() : this.Axes.y.min()), (Range == "x" ? this.Axes.x.max() : this.Axes.y.max())]; 138 | } 139 | if(Resolution == 0){ 140 | var Resolution = (Math.abs(Range[0]) + Math.abs(Range[1])) / 20000; 141 | } 142 | var Data = this.Data.FromFunction(FunX, FunY, Range[0], Range[1], Resolution, Optimize, Axis); 143 | var line = this.NewGroup("line-graph", true); 144 | 145 | this.DrawLine(line, Data, Colour, Width); 146 | this.DrawDots(line, Data.filter(function(val){return val != null;}), 3, "#000000", 0); 147 | }; 148 | 149 | this.LinePlot2 = function(LstX, LstY, Colour, Width, ScaleAxes, Curve) { 150 | var Data = d3.zip(LstX, LstY); 151 | 152 | if(ScaleAxes || !this.Axes.Exist){ 153 | this.Axes.ScaleToData(Data); 154 | } else { 155 | Data = Data.filter(this.Axes.LimitToOne); 156 | } 157 | 158 | var line = this.NewGroup("line-graph", true); 159 | 160 | this.DrawLine(line, Data, Colour, Width, undefined, Curve); 161 | 162 | this.DrawDots(line, Data.filter(function(val){return val != null;}), 3, "#000000", 0); 163 | }; 164 | 165 | this.ScatterPlot = function(LstX, LstY, Colour, Size, Opacity, ScaleAxes, Group) { 166 | Group = Group ? Group : this.NewGroup("graph"); 167 | 168 | var Data = d3.zip(LstX, LstY); 169 | 170 | if(ScaleAxes || !this.Axes.Exist){ 171 | this.Axes.ScaleToData(Data); 172 | } else { 173 | Data = Data.filter(this.Axes.LimitToOne); 174 | } 175 | 176 | this.DrawDots(Group, Data, Size, Colour, Opacity); 177 | }; 178 | 179 | this.BarPlot = function(Axis, Data, Colour, Width, Opacity) { 180 | var Group = this.NewGroup("graph"); 181 | 182 | this.DrawStacks(Group, Axis, Data, d3.stack(), Width, Colour, Opacity); 183 | }; 184 | 185 | this.Tree = function(Data) { 186 | var Group = this.NewGroup("graph"); 187 | 188 | this.DrawTree(Group, ObjToHierarchy(Data, "")); 189 | }; 190 | 191 | this.DrawLine = function(Group, Data, Colour, Width, Opacity, Curve){ 192 | if(Data.length < 1){ 193 | return; 194 | } 195 | 196 | Width = Width == undefined ? 3 : Width; 197 | Opacity = Opacity == undefined ? 1 : Opacity; 198 | Colour = Colour == undefined ? "#0000FF" : Colour; 199 | Group = Group == undefined ? this.svg : Group; 200 | Curve = Curve == undefined ? d3.curveCatmullRom : this.Curves[Curve]; 201 | 202 | Group.datum(Data); 203 | 204 | var line = d3.line() 205 | .curve(Curve) 206 | .defined(function(d){return d;}) 207 | .x(this.Axes.GetXval) 208 | .y(this.Axes.GetYval); 209 | 210 | Group.append("path") 211 | .attr("class", "line") 212 | .attr("d", line) 213 | .attr("fill", "none") 214 | .attr("stroke", Colour) 215 | .attr("stroke-width", Width); 216 | }; 217 | 218 | this.DrawDots = function(Group, Data, Radius, Colour, Opacity){ 219 | if(Data.length < 1){ 220 | return; 221 | } 222 | 223 | Radius = Radius == undefined ? 3 : Radius; 224 | Opacity = Opacity == undefined ? 1 : Opacity; 225 | Colour = Colour == undefined ? "#0000FF" : Colour; 226 | Group = Group == undefined ? this.svg : Group; 227 | 228 | var g = Group.append("g") 229 | .attr("id", "circles") 230 | .attr("fill", Colour) 231 | .attr("opacity", Opacity); 232 | 233 | g.selectAll("dot") 234 | .data(Data) 235 | .enter().append("circle") 236 | .attr("r", Radius) 237 | .attr("cx", this.Axes.GetXval) 238 | .attr("cy", this.Axes.GetYval); 239 | }; 240 | 241 | this.DrawStacks = function(Group, Axis, Data, Stack, Width, Colour, Opacity){ 242 | if(Data.length < 1){ 243 | return; 244 | } 245 | 246 | Data = Data.map(function(o,i){o.splice(0, 0, i+1); return o;}); 247 | 248 | var rectPerBar = Data[0].length - 1; 249 | var numOfColours = (rectPerBar == 1) ? Data.length : rectPerBar; 250 | 251 | Axis = Axis == undefined ? "x" : Axis; 252 | Width = Width == undefined ? 3 : Width; 253 | Opacity = Opacity == undefined ? 1 : Opacity; 254 | Colour = Colour == undefined ? GetDiffrentColours(numOfColours) : Colour; 255 | Group = Group == undefined ? this.svg : Group; 256 | 257 | bool = Axis == "x"; 258 | 259 | Group.selectAll(".serie") 260 | .data(Stack.keys(d3.range(1, rectPerBar + 1))(Data)) 261 | .enter().append("g") 262 | .attr("class", "serie") 263 | .attr("fill", function(d) {return Colour[d.index];}) 264 | .attr("opacity", Opacity) 265 | .attr("transform", "translate(" + (bool ? -Width/2 : 0) + "," + (bool ? 0 : -Width/2) + ")") 266 | .selectAll("rect") 267 | .data(function(d){return d;}) 268 | .enter().append("rect") 269 | .attr("x", function(d) {return plot.Axes.x( bool ? d.data[0] : (d[1] > 0 ? d[0] : d[1]));}) 270 | .attr("y", function(d) {return plot.Axes.y(!bool ? d.data[0] : (d[1] > 0 ? d[1] : d[0]));}) 271 | .attr("height", bool ? function(d) {return Math.abs(plot.Axes.y(d[0]) - plot.Axes.y(d[1]));} : Width) 272 | .attr("width", !bool ? function(d) {return Math.abs(plot.Axes.x(d[0]) - plot.Axes.x(d[1]));} : Width) 273 | .attr("fill", function(d) {return (rectPerBar == 1) ? Colour[d.data[0] - 1] : null;}); 274 | }; 275 | 276 | this.DrawTree = function(Group, Data){ 277 | if(Data.length < 1){ 278 | return; 279 | } 280 | 281 | Group = Group == undefined ? this.svg : Group; 282 | 283 | var root = d3.hierarchy(Data); 284 | 285 | var tree = d3.tree() 286 | .size([this.height, this.width - 100]); 287 | 288 | var link = Group.selectAll(".link") 289 | .data(tree(root).links()) 290 | .enter().append("path") 291 | .attr("class", "link") 292 | .attr("opacity", 0.5) 293 | .attr("d", d3.linkHorizontal() 294 | .x(function(d) { return d.y; }) 295 | .y(function(d) { return d.x; })); 296 | 297 | var node = Group.selectAll(".node") 298 | .data(root.descendants()) 299 | .enter().append("g") 300 | .attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf") 301 | ; }) // + (d.data.id.search("¤") > -1 ? " node--word" : "") 302 | .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; }); 303 | 304 | node.append("circle") 305 | .attr("r", 2.5); // function(d) { return d.data.id.search("¤") > -1 ? 3.5 : 2.5; } 306 | 307 | node.append("text") 308 | .attr("dy", 3) 309 | .attr("x", function(d) { return d.children ? -8 : 8; }) 310 | .text(function(d) { return d.data.id.replace("¤", ""); }); // 311 | }; 312 | 313 | this.MakeLegend = function(Legend, Colour){ 314 | if(Legend == undefined || Legend.length < 1){ 315 | this.Legend.attr("opacity", 0); 316 | return; 317 | } else { 318 | Colour = Colour == undefined ? GetDiffrentColours(Legend.length) : Colour; 319 | this.Legend.selectAll(".entry").remove(); 320 | 321 | this.Legend.rect.attr("height", 0); 322 | this.Legend.rect.attr("width", 0); 323 | 324 | for(index in Legend){ 325 | var entry = this.Legend.append("g") 326 | .attr("class", "entry"); 327 | entry.append("text") 328 | .attr("y", index * 20 + 21) 329 | .attr("x", -26) 330 | .attr("text-anchor", "end") 331 | .text(Legend[index]); 332 | entry.append("rect") 333 | .attr("x", -20) 334 | .attr("y", index * 20 + 6) 335 | .attr("width", 18) 336 | .attr("height", 18) 337 | .attr("fill", Colour[index]); 338 | } 339 | var bbox = document.getElementById("legend").getBBox(); 340 | 341 | this.Legend.width = bbox.width + 7; 342 | this.Legend.height = Legend.length * 20 + 6 + 4; 343 | 344 | this.Legend.rect.attr("height", this.Legend.height); 345 | this.Legend.rect.attr("width", this.Legend.width); 346 | 347 | this.Legend.selectAll(".entry").attr("transform", "translate(" + (bbox.width + 5) + "," + 0 + ")"); 348 | 349 | this.Legend.attr("transform", "translate(" + (this.width - bbox.width) + "," + 0 + ")"); 350 | this.Legend.attr("opacity", 1); 351 | } 352 | }; 353 | } 354 | 355 | function Axes(width, height, margin){ 356 | this.width = width; 357 | this.height = height; 358 | this.boxed = undefined; 359 | this.x = Range("x", 0, width); 360 | this.y = Range("y", height, 0); 361 | this.xAxis = d3.axisBottom(this.x); 362 | this.yAxis = d3.axisLeft(this.y); 363 | this.Exist = false; 364 | 365 | this.axes = d3.select("svg").append("g") 366 | .attr("id", "axes") 367 | .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 368 | 369 | this.xGrid = new Grid("x", this.axes, this.x, this.boxed); 370 | this.yGrid = new Grid("y", this.axes, this.y, this.boxed); 371 | 372 | this.NewAxis = function(ID){ 373 | this.axes.append("text") 374 | .attr("id", "Label-" + ID) 375 | .attr("class", "Label") 376 | .attr("text-anchor", "middle") 377 | .attr("dy", (ID == "x" ? 25 : -15)); 378 | this.axes.append("g") 379 | .attr("id", "axis-" + ID) 380 | .attr("class", "axis") 381 | .on("mouseenter", function(){d3.select("#axis-" + ID + " > path").attr("filter", "url(#dropShadow)");}) 382 | .on("mouseleave", function(){d3.select("#axis-" + ID + " > path").attr("filter", null);}); 383 | }; 384 | 385 | this.NewAxis("x"); 386 | this.NewAxis("y"); 387 | 388 | this.DomAxisX = document.getElementById("axis-x"); 389 | this.DomAxisY = document.getElementById("axis-y"); 390 | 391 | this.HideAxes = function(bool){ 392 | if(bool != undefined){ 393 | this.axes.attr("opacity", bool); 394 | } 395 | } 396 | 397 | this.SetLabels = function(xLabel, yLabel){ 398 | if(xLabel != undefined){ 399 | this.axes.select("#Label-x").text(xLabel); 400 | } 401 | if(yLabel != undefined){ 402 | this.axes.select("#Label-y").text(yLabel); 403 | } 404 | this.PlaceLabel(); 405 | }; 406 | 407 | this.PlaceLabel = function(){ 408 | if(this.boxed){ 409 | var ticktextX = this.DomAxisX.getElementsByTagName("text"); 410 | var ticktextY = this.DomAxisY.getElementsByTagName("text"); 411 | if(ticktextX.length > 0){ 412 | var Label = document.getElementById("Label-x").getBBox(); 413 | this.axes.select("#Label-x").attr("dy", 10 + Label.height + ticktextX[ticktextX.length/2].getBBox().height); 414 | } 415 | if(ticktextY.length > 0){ 416 | this.axes.select("#Label-y").attr("dy", -10 + ticktextY[ticktextY.length/2].getBBox().x); 417 | } 418 | } else { 419 | this.axes.select("#Label-x").attr("dy", 25); 420 | this.axes.select("#Label-y").attr("dy", -15); 421 | } 422 | }; 423 | 424 | this.Update = function(width, height, margin){ 425 | this.width = width; 426 | this.height = height; 427 | this.axes.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 428 | this.x.range([0, width]); 429 | this.y.range([height, 0]); 430 | }; 431 | 432 | this.Set = function(xlst, ylst, boxed){ 433 | this.boxed = boxed == undefined ? this.boxed : boxed == true; 434 | this.x.ChangeDomain(xlst); 435 | this.y.ChangeDomain(ylst); 436 | this.Axis("x"); 437 | this.Axis("y"); 438 | this.xGrid.Update(-this.height, "0," + this.height, this.boxed); 439 | this.yGrid.Update(-this.width, "0,0", this.boxed); 440 | this.PlaceLabel(); 441 | this.Exist = true; 442 | }; 443 | 444 | this.Grid = function(OnOff){ 445 | this.axes.selectAll(".grid").attr("opacity", OnOff); 446 | }; 447 | 448 | this.SetGrid = function(xory, major, minor, colour, dasharray){ 449 | if(xory == "x" || xory == ""){ 450 | this.xGrid.Set(major, minor, colour, dasharray); 451 | this.Axis("x"); 452 | } 453 | if(xory == "y" || xory == ""){ 454 | this.yGrid.Set(major, minor, colour, dasharray); 455 | this.Axis("y"); 456 | } 457 | }; 458 | 459 | this.Axis = function(xory){ 460 | var axis = this.axes.select("#axis-" + xory); 461 | axis.attr("transform", "translate(" + (xory == "x" ? ("0," + (this.boxed ? this.y(this.y.start()) : (this.y.zero ? this.y(0) : this.y(this.y.mid()))) + ")") 462 | : ((this.boxed ? "0" : (this.x.zero ? this.x(0) : this.x(this.x.mid()))) + ",0)")) 463 | ); 464 | axis.call((xory === "x" ? this.xAxis.tickValues(this.x.GetTicks()) : this.yAxis.tickValues(this.y.GetTicks()))); 465 | 466 | this.axes.select("#Label-" + xory).attr("transform", "translate(" + (xory == "x" ? (this.width / 2 + "," + this.height + ")") 467 | : ("0," + this.height / 2 + ")rotate(-90)")) 468 | ); 469 | }; 470 | 471 | this.ScaleToData = function(data){ 472 | if(data.length < 1){ 473 | return; 474 | } 475 | this.Set(d3.extent(data, function(d){return d[0];}), d3.extent(data, function(d){return d[1];})); 476 | }; 477 | 478 | this.GetXval = function(d){return plot.Axes.x(d[0]);}; 479 | this.GetYval = function(d){return plot.Axes.y(d[1]);}; 480 | 481 | this.LimitToOne = function(a){return (plot.Axes.x.min() <= a[0] && a[0] <= plot.Axes.x.max() && plot.Axes.y.min() <= a[1] && a[1] <= plot.Axes.y.max());}; 482 | this.LimitToTwo = function(x, y){return (plot.Axes.x.min() <= x && x <= plot.Axes.x.max() && plot.Axes.y.min() <= y && y <= plot.Axes.y.max());}; 483 | } 484 | 485 | var Range = function(type, start, end) { 486 | var range = d3.scaleLinear(); 487 | range.type = type; 488 | range.range([start, end]); 489 | range.AproxNumOfTicks = 10; 490 | range.ChangeDomain = function(Lst){ 491 | this.domain(Lst); 492 | this.zero = (d3.min(this.domain()) <= 0 && 0 <= d3.max(this.domain())); 493 | }; 494 | range.GetTicks = function(){ 495 | if(Array.isArray(this.AproxNumOfTicks)){ 496 | return this.AproxNumOfTicks; 497 | } 498 | return this.ticks(this.AproxNumOfTicks); 499 | }; 500 | range.min = function(){return d3.min(this.domain());}; 501 | range.mid = function(){return Math.round(d3.mean(this.domain()));}; 502 | range.max = function(){return d3.max(this.domain());}; 503 | 504 | range.start = function(){return this.domain()[0];}; 505 | range.end = function(){return this.domain()[1];}; 506 | return range; 507 | }; 508 | 509 | var Grid = function(id, axes, range, Boxed) { 510 | this.ID = id; 511 | this.Grid = axes.append("g") 512 | .attr("id", "grid-" + this.ID) 513 | .attr("class", "grid") 514 | .attr("opacity", 0) 515 | .attr("stroke", "lightgrey") 516 | .attr("stroke-dasharray", "5"); 517 | this.Range = range; 518 | this.NumOfMajorTicks = 10; 519 | this.NumOfMinorTicks = 0; 520 | this.Boxed = Boxed; 521 | 522 | this.Set = function(major, minor, colour, dasharray){ 523 | this.NumOfMajorTicks = major == undefined ? this.NumOfMajorTicks : major; 524 | this.Range.AproxNumOfTicks = this.NumOfMajorTicks; 525 | this.NumOfMinorTicks = minor == undefined ? this.NumOfMinorTicks : minor; 526 | this.Grid.attr("stroke", (colour == undefined ? "lightgrey" : colour)); 527 | this.Grid.attr("stroke-dasharray", (dasharray == undefined ? "5" : dasharray)); 528 | this.Grid.call(this.GetAxis()); 529 | this.CleanUp(); 530 | }; 531 | 532 | this.Update = function(tickSize, translate, Boxed){ 533 | this.Boxed = Boxed; 534 | this.tickSize = tickSize; 535 | this.Grid.attr("transform", "translate(" + translate + ")"); 536 | this.Grid.call(this.GetAxis()); 537 | this.CleanUp(); 538 | }; 539 | 540 | this.CleanUp = function(){ 541 | this.Grid.selectAll(".tick").selectAll("line").attr("stroke", null); 542 | this.Grid.selectAll("text").remove(); 543 | this.Grid.select("path").remove(); 544 | }; 545 | 546 | this.GetAxis = function(){ 547 | return (this.ID == "x" ? d3.axisBottom(this.Range) : d3.axisLeft(this.Range)) 548 | .tickFormat("") 549 | .tickSizeOuter(0) 550 | .tickSize(this.tickSize) 551 | .tickValues(this.GetTicks()); 552 | }; 553 | 554 | this.GetTicks = function(){ 555 | var ticks = this.Range.GetTicks(); 556 | var minorticks = []; 557 | 558 | for(i = 0; i < ticks.length; i++){ 559 | var step = Math.abs((ticks[i] - ticks[i+1]) / (this.NumOfMinorTicks + 1)); 560 | minorticks = minorticks.concat(d3.range(ticks[i], ticks[i+1], (ticks[i] < ticks[i+1]) ? step : - step)); 561 | } 562 | 563 | minorticks.push(ticks[ticks.length - 1]) 564 | var index = minorticks.indexOf(this.Boxed ? this.Range.start() : (this.Range.zero ? 0 : this.Range.mid())); 565 | if(index != -1){ 566 | minorticks.splice(index, 1); 567 | } 568 | return minorticks; 569 | }; 570 | }; 571 | 572 | function Data() { 573 | this.Init = function(){ 574 | this.x = plot.Axes.x; this.xmin = this.x.min(); this.xmid = this.x.mid(); this.xmax = this.x.max(); 575 | this.y = plot.Axes.y; this.ymin = this.y.min(); this.ymid = this.y.mid(); this.ymax = this.y.max(); 576 | }; 577 | this.LimitToOne = function(a){return (this.xmin <= a[0] && a[0] <= this.xmax && this.ymin <= a[1] && a[1] <= this.ymax);}; 578 | this.LimitToTwo = function(x, y){return (this.xmin <= x && x <= this.xmax && this.ymin <= y && y <= this.ymax);}; 579 | 580 | this.FromFunction = function(FunX, FunY, from, to, Resolution, Optimize, Axis){ 581 | Axis = Axis == "y" ? false : true; 582 | 583 | this.Init(); 584 | var bool = true, poslst = [], old = 0, old2 = 5, xmin = this.x.min(), xmax = this.x.max(), ymin = this.y.min(), ymax = this.y.max(); 585 | var step = Math.abs((from - to) / Resolution); //(Math.abs(from) + Math.abs(to)) / Resolution; 586 | var varX, varY; 587 | if(!Optimize){ 588 | data = d3.range(from, to, step).map(function(x){ 589 | varX = eval(FunX); varY = eval(FunY); 590 | if(ymin <= varY && varY <= ymax && xmin <= varX && varX <= xmax){ 591 | return [varX, varY]; 592 | } 593 | }); 594 | } else { 595 | data = d3.range(from, to, step).map(function(x, index){ 596 | varX = eval(FunX); varY = eval(FunY); 597 | if(ymin <= varY && varY <= ymax && xmin <= varX && varX <= xmax){ 598 | if(bool){ 599 | poslst.push(index); 600 | old = Axis ? varY : varX; bool = false; 601 | } else if((old2 < old && old > (Axis ? varY : varX)) || (old2 > old && old < (Axis ? varY : varX))) { 602 | poslst.push(index - 1); 603 | } 604 | old2 = old; old = Axis ? varY : varX; 605 | return [varX, varY]; 606 | } else { 607 | if(!bool){ 608 | poslst.push(index - 1); 609 | bool = true; 610 | } 611 | } 612 | }); 613 | 614 | poslst.push(data.length - 1); 615 | poslst = poslst.reverse(); 616 | 617 | var extremum, start, end; 618 | for(i in poslst) { 619 | start = data[poslst[i]-1] != null ? data[poslst[i]-1][Axis ? 0 : 1] : from + (poslst[i]-1)*step; 620 | end = data[poslst[i]+1] != null ? data[poslst[i]+1][Axis ? 0 : 1] : from + (poslst[i]+1)*step; 621 | 622 | extremum = d3.range(start, end, step / 10).map( 623 | function(x){ 624 | varX = eval(FunX); varY = eval(FunY); 625 | if(ymin <= varY && varY <= ymax && xmin <= varX && varX <= xmax){ 626 | return [varX, varY]; 627 | }; 628 | }).filter(function(val){return val != null;}); 629 | 630 | data.splice.apply(data, [poslst[i]-1 > 0 ? poslst[i]-1 : 0, 2].concat(extremum)); 631 | } 632 | } 633 | var x = to; 634 | varX = eval(FunX); varY = eval(FunY); 635 | if(ymin <= varY && varY <= ymax && xmin <= varX && varX <= xmax){ 636 | data.push([varX, varY]); 637 | } 638 | return data; 639 | }; 640 | } 641 | 642 | var GetDiffrentColours = function(num){ 643 | return d3.range(0, 360, Math.round(360 / num)).map(function(v,i){return d3.hsl(v, 1, (i % 2) ? 0.4 : 0.6);}); 644 | } 645 | 646 | var time = function(){return new Date().getTime();}; 647 | 648 | var PrintPointArray = function(array){ 649 | msg = "["; 650 | for (i in array){ 651 | msg += (array[i] != null ? "[" + array[i][0] + ", " + array[i][1] + "], " : ""); 652 | } 653 | alert(msg + "]"); 654 | } 655 | 656 | var IsDefined = function(val){ 657 | return val != undefined ? val : undefined; 658 | } 659 | 660 | var toPoints = function(array){ 661 | var len = array.length, res = ""; 662 | for(i = 0; i < len; i++){ 663 | res += array[i][0] + "," + array[i][1] + " "; 664 | } 665 | return res.substr(0, res.length - 1); 666 | } 667 | 668 | var Demo = function(text){ 669 | if(document.getElementById("demo") == null){ 670 | p = document.createElement("p"); 671 | p.id = "demo"; 672 | document.body.insertBefore(p, document.body.childNodes[0]); 673 | } 674 | document.getElementById("demo").innerHTML = text; 675 | } 676 | 677 | var ObjToHierarchy = function(obj, key) { 678 | var Hierarchy = {"id" : key}; 679 | if((typeof obj === "string" || typeof obj === "number") && obj !== "") { 680 | Hierarchy["children"] = [{"id" : obj}]; 681 | } else { 682 | Hierarchy["children"] = []; 683 | 684 | if(Array.isArray(obj)) { 685 | for(i in obj) { 686 | if((typeof obj[i] === "string" || typeof obj[i] === "number") && obj[i] !== "") { 687 | Hierarchy["children"].push({"id" : obj[i]}); 688 | } else { 689 | Hierarchy["children"].push(ObjToHierarchy(obj[i], "")); 690 | } 691 | } 692 | } else { 693 | for(i in obj) { 694 | Hierarchy["children"].push(ObjToHierarchy(obj[i], i)); 695 | } 696 | } 697 | } 698 | return Hierarchy; 699 | } 700 | 701 | var ObjToString = function(obj) { 702 | if(typeof obj != "object"){ 703 | return (typeof obj === "number" ? obj : '"' + obj + '"'); 704 | } 705 | var bool = Array.isArray(obj); 706 | res = bool ? "[" : "{"; 707 | for(key in obj) { 708 | res += (!bool ? '"' + key + '" : ' : "") + ObjToString(obj[key]) + ", "; 709 | } 710 | return res.substr(0, res.length - 2) + (bool ? "]" : "}"); 711 | } 712 | --------------------------------------------------------------------------------