pax_global_header00006660000000000000000000000064136503763510014523gustar00rootroot0000000000000052 comment=ee1c86d98424685736eedb6c9924a493c3a96319 nim-d3-0.1.3/000077500000000000000000000000001365037635100126135ustar00rootroot00000000000000nim-d3-0.1.3/.gitignore000066400000000000000000000000211365037635100145740ustar00rootroot00000000000000nimcache/ docs/ nim-d3-0.1.3/CHANGELOG.md000066400000000000000000000016621365037635100144310ustar00rootroot00000000000000## [currently in development](https://github.com/hiteshjasani/nim-d3/compare/v0.1.3...master) ## [v0.1.3 - 2020-04-23](https://github.com/hiteshjasani/nim-d3/compare/v0.1.2...v0.1.3) * Add selection.join * Add ability to change axis scale * Add support for generic scale forward and backward transforms ## [v0.1.2 - 2019-07-26](https://github.com/hiteshjasani/nim-d3/compare/v0.1.1...v0.1.2) * Add band scale type and functions * Add example column chart ## [v0.1.1 - 2019-07-25](https://github.com/hiteshjasani/nim-d3/compare/v0.1.0...v0.1.1) * Removed external dependency on jsbind package * Added routines interfacing to d3.scaleOrdinal * Added d3-scale-chromatic schemes that work with d3.scaleOrdinal * Improved ex3 to work with ordinal scales * Added `docs` task to generate documentation * Refactored filenames to more align with D3 modules to make maintenance easier ## v0.1.0 - 2019-07-24 * Initial release with example graph nim-d3-0.1.3/LICENSE000066400000000000000000000020561365037635100136230ustar00rootroot00000000000000MIT License Copyright (c) 2019 Hitesh Jasani Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. nim-d3-0.1.3/README.md000066400000000000000000000012231365037635100140700ustar00rootroot00000000000000# nim-d3 Nim Foreign Function Interface (FFI) bindings to [d3.js](https://d3js.org). This is in development and there is still a lot to be written. There is the ability to create a simple graph today. See [examples/](examples/) for usage. ![Image of example 5](img/ex5.png) ![Image of example 6](img/ex6.png) ## Install nim-d3 is available in [nimble](https://github.com/nim-lang/nimble). `nimble install d3` or if you have a `.nimble` file then add the following. `requires "d3 >= 0.1.2"` NOTE: Run `nimble search d3 --ver` to find out the most recent version of the library. ## Documentation Clone the repo and generate the docs `nimble docs` nim-d3-0.1.3/d3.nimble000066400000000000000000000006071365037635100143140ustar00rootroot00000000000000# Package version = "0.1.3" author = "Hitesh Jasani" description = "Nim bindings to D3js" license = "MIT" srcDir = "src" backend = "js" # Dependencies requires "nim >= 0.20.0" # Tasks task docs, "Build documentation": mkDir("docs/d3") exec "nim doc --project --index:on --outdir:docs src/d3.nim" exec "nim buildIndex -o:docs/theindex.html docs" nim-d3-0.1.3/examples/000077500000000000000000000000001365037635100144315ustar00rootroot00000000000000nim-d3-0.1.3/examples/.gitignore000066400000000000000000000000251365037635100164160ustar00rootroot00000000000000examples public/js/ nim-d3-0.1.3/examples/README.md000066400000000000000000000005261365037635100157130ustar00rootroot00000000000000## Quickstart See the code for [example 5](src/pages/ex5.nim) for an example of drawing a full chart. ![Image of example 5](../img/ex5.png) ## Building and Running 1. `nimble build` - build server 2. `nimble pages` - build js client pages 3. `./examples` - run server 4. Load [http://localhost:9000/](http://localhost:9000/) in browser. nim-d3-0.1.3/examples/examples.nimble000066400000000000000000000012541365037635100174410ustar00rootroot00000000000000# Package version = "0.1.0" author = "Hitesh Jasani" description = "Examples using nim and D3" license = "MIT" srcDir = "src" bin = @["examples"] # Dependencies requires "nim >= 0.20.0" requires "karax" from os import splitFile, addFileExt task pages, "Build javascript pages": if dirExists("public/js"): rmDir("public/js") mkDir("public/js") for file in listfiles("src/pages/"): let fileparts = splitFile(file) if fileparts.ext == ".nim": let opts = "--hints:off" jsfile = "public/js/" & fileparts.name.addFileExt("js") cmd = "nim js " & opts & " -o:" & jsfile & " " & file exec cmd nim-d3-0.1.3/examples/nim.cfg000066400000000000000000000000161365037635100156720ustar00rootroot00000000000000path="../src" nim-d3-0.1.3/examples/src/000077500000000000000000000000001365037635100152205ustar00rootroot00000000000000nim-d3-0.1.3/examples/src/examples.nim000066400000000000000000000045131365037635100175460ustar00rootroot00000000000000import asynchttpserver, asyncdispatch, strformat, strutils type Example = ref object id: int name: string proc link(ex: Example): string = &"""Example {ex.id}: {ex.name}""" proc index(): string = let examples = @[Example(id: 1, name: "Selecting elements and applying styles") ,Example(id: 2, name: "Appending elements") ,Example(id: 3, name: "Scaling using domain and range") ,Example(id: 4, name: "Simple plot of circles") ,Example(id: 5, name: "Sample scatterplot") ,Example(id: 6, name: "Seattle rainfall column chart")] result = """
    """ for x in examples: result.add("
  • " & link(x) & "
  • ") result.add("""
""") proc loadJS(jspath: string): string = let style = """ """ &""" {style}
""" proc routeHandler(req: Request) {.async.} = case req.url.path of "/": await req.respond(Http200, index()) else: if startsWith(req.url.path, "/ex/"): let ex = req.url.path[4..^1] path = "/js/" & ex & ".js" await req.respond(Http200, loadJS(path)) elif startsWith(req.url.path, "/js/"): let jsfilename = "public" & req.url.path try: await req.respond(Http200, readFile(jsfilename), newHttpHeaders([("Content-Type","application/javascript")])) except: await req.respond(Http500, "Could not load " & jsfilename) else: await req.respond(Http404, "Resource not found!") when isMainModule: var server = newAsyncHttpServer() echo "Point your browser to http://localhost:9000/" waitFor server.serve(Port(9000), routeHandler) nim-d3-0.1.3/examples/src/pages/000077500000000000000000000000001365037635100163175ustar00rootroot00000000000000nim-d3-0.1.3/examples/src/pages/ex1.nim000066400000000000000000000011221365037635100175150ustar00rootroot00000000000000include karax/prelude import d3 proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 1" p: text "Select elements and apply style" tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea"): p: text "para element" p(class = "colorable"): text "Colorable para element" button(`type` = "button"): text "Change color" proc onclick(ev: Event, n: VNode) = discard d3.select("#testarea p").style("color", "red") discard d3.select("#testarea .colorable").style("color", "green") setRenderer createDom nim-d3-0.1.3/examples/src/pages/ex2.nim000066400000000000000000000014251365037635100175240ustar00rootroot00000000000000include karax/prelude import d3 proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 2" p: text "Select elements and append SVG" tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea"): p: text "para element" p(class = "colorable"): text "Colorable para element" button(`type` = "button"): text "Add SVG" proc onclick(ev: Event, n: VNode) = discard d3.select("#testarea") .append("svg") .attr("width", "500") .attr("height", "300") .append("rect") .attr("width", "500") .attr("height", "300") .attr("fill", "yellow") .attr("border", "solid 1px blue") setRenderer createDom setForeignNodeId("testarea") nim-d3-0.1.3/examples/src/pages/ex3.nim000066400000000000000000000113301365037635100175210ustar00rootroot00000000000000include karax/prelude import d3 const orig1 = 5 let scaleLin: D3ContinuousScale = d3.scaleLinear() .continuousDomain(0, 100) .continuousRange(200, 500) scaleLinR: D3ContinuousScale = d3.scaleLinear() .continuousDomain(0, 100) .continuousRange(500, 200) scaleLinColor: D3ContinuousScale = d3.scaleLinear() .continuousDomain(0, 100) .continuousRange("brown", "steelblue") scalePieces: D3ContinuousScale = d3.scaleLinear() .continuousDomain(@[-1, 0, 1]) .continuousRange(@["red", "white", "blue"]) scaleOrd: D3OrdinalScale = d3.scaleOrdinal() .ordinalDomain(@[0,1,2,3]) .ordinalRange(schemeRdBu(4)) proc runOnce() = echo "runOnce" proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 3" p: text "Scales - Linear ..." tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea"): table: thead: tr: th: text "Type" th: text "Domain" th: text "Range" th: text "Original Value" th: text "Scaled Value" th: text "Expected Value" tbody: tr: td: text "linear continuous" td: text $continuousDomain[int](scaleLin) td: text $continuousRange[int](scaleLin) td: text $orig1 td(id = "scaleLin"): text "-" td: text "215" tr: td: text "linear continuous (invert)" td: text $continuousDomain[int](scaleLin) td: text $continuousRange[int](scaleLin) td: text "215" td(id = "scaleLinInv"): text "-" td: text "5" tr: td: text "linear continuous" td: text $continuousDomain[int](scaleLinR) td: text $continuousRange[int](scaleLinR) td: text $orig1 td(id = "scaleLinR"): text "-" td: text "485" tr: td: text "linear continuous" td: text $continuousDomain[int](scaleLinColor) td: text $continuousRange[cstring](scaleLinColor) td: text $orig1 td(id = "scaleLinColor"): text "-" td: text "rgb(160, 46, 49)" tr: td: text "linear continuous" td: text $continuousDomain[int](scalePieces) td: text $continuousRange[cstring](scalePieces) td: text "-0.5" td(id = "scalePieces1"): text "-" td: text "rgb(255, 128, 128)" tr: td: text "linear continuous" td: text $continuousDomain[int](scalePieces) td: text $continuousRange[cstring](scalePieces) td: text "0.5" td(id = "scalePieces2"): text "-" td: text "rgb(128, 128, 255)" tr: td: text "ordinal" td: text $ordinalDomain[int](scaleOrd) td: text $ordinalRange[cstring](scaleOrd) td: text "0" td(id = "scaleOrd1"): text "-" td: text "#ca0020" tr: td: text "ordinal" td: text $ordinalDomain[int](scaleOrd) td: text $ordinalRange[cstring](scaleOrd) td: text "1" td(id = "scaleOrd2"): text "-" td: text "#f4a582" tr: td: text "ordinal" td: text $ordinalDomain[int](scaleOrd) td: text $ordinalRange[cstring](scaleOrd) td: text "3" td(id = "scaleOrd3"): text "-" td: text "#0571b0" button(`type` = "button"): text "Scale values" proc onclick(ev: Event, n: VNode) = discard select("#testarea #scaleLin").text($exec(scaleLin, orig1)) discard select("#testarea #scaleLinInv").text($invert(scaleLin, 215)) discard select("#testarea #scaleLinR").text($exec(scaleLinR, orig1)) discard select("#testarea #scaleLinColor").text($exec(scaleLinColor, orig1)) discard select("#testarea #scalePieces1").text($exec(scalePieces, -0.5)) discard select("#testarea #scalePieces2").text($exec(scalePieces, 0.5)) discard select("#testarea #scaleOrd1").text($exec(scaleOrd, 0)) discard select("#testarea #scaleOrd2").text($exec(scaleOrd, 1)) discard select("#testarea #scaleOrd3").text($exec(scaleOrd, 3)) setRenderer createDom setForeignNodeId("testarea") #-------------------------------------- redrawSync() runOnce() nim-d3-0.1.3/examples/src/pages/ex4.nim000066400000000000000000000027541365037635100175340ustar00rootroot00000000000000include karax/prelude import d3 type DataPt = ref object x: float y: float let rawdata: seq[DataPt] = @[DataPt(x: 10.0, y: 12.1) ,DataPt(x: 22.0, y: 32.5) ,DataPt(x: 101.3, y: 5.0) ,DataPt(x: 196.2, y: 8.5) ,DataPt(x: 2.7, y: 174.3) ,DataPt(x: 100.0, y: 100.0) ,DataPt(x: 125.4, y: 184.9)] proc runOnce() = let width = 200 height = 200 let svg = select("#testarea") .append("svg") .attr("width", width) .attr("height", height) discard svg.append("rect") .attr("width", width) .attr("height", height) .attr("fill", "yellow") .attr("fill-opacity", 0.3) discard svg.selectAll("circle") .data(rawdata) .enter().append("circle") .attr("cx", proc(d: DataPt): float = d.x) .attr("cy", proc(d: DataPt): float = d.y) .attr("r", 12.5) proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 4: Simple plot circles" tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea") ul: li: text "Circles are only drawn once on page load and will not be redrawn even if karax redraws the vdom" li: text "No scaling has been applied yet so circles are plotted with their y values upside down" setRenderer createDom setForeignNodeId("testarea") #-------------------------------------- redrawSync() runOnce() nim-d3-0.1.3/examples/src/pages/ex5.nim000066400000000000000000000101171365037635100175250ustar00rootroot00000000000000from sequtils import map import strformat include karax/prelude import d3 type DataPt = ref object x: float y: float let rawdata: seq[DataPt] = @[DataPt(x: 10.0, y: 112.1) ,DataPt(x: 22.0, y: 132.5) ,DataPt(x: 101.3, y: 15.0) ,DataPt(x: 196.2, y: 18.5) ,DataPt(x: 2.7, y: 1174.3) ,DataPt(x: 100.0, y: 1100.0) ,DataPt(x: 125.4, y: 1184.9) ,DataPt(x: 145.0, y: 575.0)] proc runOnce() = let svgWidth = 400 svgHeight = 300 insetLeft = 80 insetBottom = 50 gWidth = svgWidth - insetLeft gHeight = svgHeight - insetBottom minX = min(map(rawdata, proc(d: DataPt): float = d.x)) maxX = max(map(rawdata, proc(d: DataPt): float = d.x)) minY = min(map(rawdata, proc(d: DataPt): float = d.y)) maxY = max(map(rawdata, proc(d: DataPt): float = d.y)) xScale: D3ContinuousScale = d3.scaleLinear() .continuousDomain(minX, maxX) .continuousRange(0, gWidth) yScale: D3ContinuousScale = d3.scaleLinear() .continuousDomain(minY, maxY) # Use with origin in top-left corner .continuousRange(gHeight, 0) xAxis: D3Axis = axisBottom(xScale) yAxis: D3Axis = axisLeft(yScale) let svg = select("#testarea") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight) discard svg.append("rect") .attr("width", svgWidth) .attr("height", svgHeight) .attr("fill", "yellow") .attr("fill-opacity", 0.3) let g = svg.append("g") .attr("width", gWidth) .attr("height", gHeight) # Origin is top-left corner, use with yRange [gHeight 0] .attr("transform", translate(insetLeft, 0)) discard g.append("g") .attr("class", "x-axis axis") .attr("transform", translate(0, gHeight)) .call(xAxis) discard g.append("g") .attr("class", "y-axis axis") .call(yAxis) discard svg.append("text") .attr("text-anchor", "middle") .attr("transform", translate(insetLeft.float + gWidth/2, svgHeight.float - insetBottom/4)) .text("x axis label") discard svg.append("text") .attr("text-anchor", "middle") .attr("transform", translateAndRotate(insetLeft/3, gHeight/2, -90)) .text("y axis label") let tooltip = select("body") .append("div") .attr("class", "tooltip") .style("opacity", 0) discard g.selectAll("circle") .data(rawdata) .enter().append("circle") .attr("cx", proc(d: DataPt): float = exec(xScale, d.x)) .attr("cy", proc(d: DataPt): float = exec(yScale, d.y)) .attr("r", 12.5) .attr("fill-opacity", 0.2) .attr("stroke", "blue") .attr("fill", "blue") .on("mouseover", proc(d: DataPt) = discard tooltip .html("x: " & $d.x & "
" & "y: " & $d.y) .style("left", $(d3.event().pageX + 25) & "px") .style("top", $d3.event().pageY & "px") discard tooltip.transition() .duration(200) .style("opacity", 0.9)) .on("mouseout", proc(d: DataPt) = discard tooltip.transition() .duration(500) .style("opacity", 0.0)) proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 5: Sample scatterplot" tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea") ul: li: text "Circles are only drawn once on page load and will not be redrawn even if karax redraws the vdom" li: text "Input data is scaled" li: text "Circles have styling applied" li: text "Graph axes and labels" li: text "Hover tooltip on data points" setRenderer createDom setForeignNodeId("testarea") #-------------------------------------- redrawSync() runOnce() nim-d3-0.1.3/examples/src/pages/ex6.nim000066400000000000000000000113351365037635100175310ustar00rootroot00000000000000from sequtils import map import strformat include karax/prelude import d3 type DataPt = ref object day: int rain: float # data is rainfall in Seattle from Feb 1, 2019 - Feb 14, 2019 # https://www.ncdc.noaa.gov/cdo-web/datasets/GHCND/stations/GHCND:USW00024233/detail let rawdata: seq[DataPt] = @[DataPt(day: 1, rain: 0.68) ,DataPt(day: 2, rain: 0.03) ,DataPt(day: 3, rain: 0.09) ,DataPt(day: 4, rain: 0.13) ,DataPt(day: 5, rain: 0.0) ,DataPt(day: 6, rain: 0.0) ,DataPt(day: 7, rain: 0.0) ,DataPt(day: 8, rain: 0.46) ,DataPt(day: 9, rain: 0.16) ,DataPt(day: 10, rain: 0.16) ,DataPt(day: 11, rain: 1.22) ,DataPt(day: 12, rain: 0.86) ,DataPt(day: 13, rain: 0.02) ,DataPt(day: 14, rain: 0.12)] proc runOnce() = let svgWidth = 600 svgHeight = 300 insetLeft = 80 insetBottom = 50 gWidth = svgWidth - insetLeft gHeight = svgHeight - insetBottom minY = min(map(rawdata, proc(d: DataPt): float = d.rain)) maxY = max(map(rawdata, proc(d: DataPt): float = d.rain)) domX = map(rawdata, proc(d: DataPt): cstring = cstring($d.day)) xScale: D3BandScale = scaleBand() .bandDomain(domX) .bandRange(0, gWidth) .paddingInner(0.1) .paddingOuter(0.2) .align(0.5) .round(true) yScale: D3ContinuousScale = scaleLinear() .continuousDomain(minY, maxY) # Use with origin in top-left corner .continuousRange(gHeight, 0) xAxis: D3Axis = axisBottom(xScale) yAxis: D3Axis = axisLeft(yScale) let svg = select("#testarea") .append("svg") .attr("width", svgWidth) .attr("height", svgHeight) discard svg.append("rect") .attr("width", svgWidth) .attr("height", svgHeight) .attr("fill", "yellow") .attr("fill-opacity", 0.3) let g = svg.append("g") .attr("width", gWidth) .attr("height", gHeight) # Origin is top-left corner, use with yRange [gHeight 0] .attr("transform", translate(insetLeft, 0)) discard g.append("g") .attr("class", "x-axis axis") .attr("transform", translate(0, gHeight)) .call(xAxis) discard g.append("g") .attr("class", "y-axis axis") .call(yAxis) discard svg.append("text") .attr("text-anchor", "middle") .attr("transform", translate(insetLeft.float + gWidth/2, svgHeight.float - insetBottom/4)) .text("Feb 2019 (day)") discard svg.append("text") .attr("text-anchor", "middle") .attr("transform", translateAndRotate(insetLeft/3, gHeight/2, -90)) .text("Rainfall (inches)") let tooltip = select("body") .append("div") .attr("class", "tooltip") .style("opacity", 0) discard g.selectAll("rect") .data(rawdata) .enter().append("rect") .attr("fill", "steelblue") .attr("x", proc(d: DataPt): int = exec(xScale, d.day)) .attr("width", xScale.bandwidth()) .attr("y", proc(d: DataPt): float = exec(yScale, d.rain)) .attr("height", proc(d: DataPt): float = gHeight.float - exec(yScale, d.rain)) .on("mouseover", proc(d: DataPt) = discard tooltip .html("day: " & $d.day & "
" & "rain: " & $d.rain & " inches") .style("left", $(d3.event().pageX + 25) & "px") .style("top", $d3.event().pageY & "px") discard tooltip.transition() .duration(200) .style("opacity", 0.9)) .on("mouseout", proc(d: DataPt) = discard tooltip.transition() .duration(500) .style("opacity", 0.0)) proc createDom(): VNode = result = buildHtml(tdiv): h1: text "Example 6: Seattle Rainfall Column Chart" tdiv: text "<< " a(href = "/"): text "home" tdiv(id = "testarea") ul: li: text "Rainfall in Seattle from Feb 1, 2019 to Feb 14, 2019" li: span: text "Data sourced from " a(href = "https://www.ncdc.noaa.gov/cdo-web/datasets/GHCND/stations/GHCND:USW00024233/detail"): text "noaa.gov" setRenderer createDom setForeignNodeId("testarea") #-------------------------------------- redrawSync() runOnce() nim-d3-0.1.3/img/000077500000000000000000000000001365037635100133675ustar00rootroot00000000000000nim-d3-0.1.3/img/ex5.png000066400000000000000000001205501365037635100146010ustar00rootroot00000000000000PNG  IHDR;Gn! HiCCPICC ProfileHWXS[RIhH RK E*I ĐD.*v*ZY+Z*oR`]{}|9){ :5<4 OR aMJMcL g_.eE(CQޯ(9_EO @ y/Ke}zfT@l B,U,5.Q 5T$s ɲnzV!? h߂U"K!CGB<*/oC;O839yaE%P\˛K^b(4,2^Y3ۭQJLW >=(ULRۣ|9 0!vB 68\gdù ByXF6#>vg8loO?Ibko!7ŢuP6LyNB)qbldxe6 %!j~lZ,<^c/˓Ջ-1\U JTA,xICaڱv$IS/)-iq07BT^ T1҂DuxF6o|:D,#@ g@f#E5#P H~!Y!(Zdf U9 y d8Z2x 5D\sPSdžhF1$Cp#nx4CeOxB <"\'tnO/}S L0B振k '@~ȍ3qx M[ _u]cGq`÷Nڞ,ʞ~!u} |U%;Nb#X`aDZfvTWc*'ܵ޵z@X|? ,8KTb7ŕGb_S¼.ePg '0~m+dj~ 10?a`< L],3rU` v=`?hGI+upn`ABG1b"Έ;"aH4"H"A;YT![:g0r9t H b( 5@P;t ꃲ(4fh1Z.C+Zt7ڈD/N9ڏL cb qX, d< jWNq] ēp>×UN?_}`Jp&I,LB)pp>M݄D"I'zç1MM\J@KsZ>iN4m MA[FA;AM{M4z}~^=Z-О]ݨ}EEV3MXBe^].G;OZM~=^^R]z%[Ow1053clcat { &V5dbL;&\ϼ8l{pĒ #xg4(HhTfGcqqJ&&D&MΘ4??lwLQS'x٦[M/EI֛25gg1?fc[8n;ːf*YY} -mVVIVZݷZXgZn`3Ǧ-Vd;;{s9j9}s78;NN"jΨys((QQn\...G3GG^4i16cƬsvWO\mwƻ-rkq{wvArX؍coy2<'xKm]}'g9_o|# 8qquXt7vYj[ ?e;ٻ/B\Cd!Bq8s9'BЈвж0VY}#ND""WFq:nxsǟE%DUE=vEL@'z½ILS,ƮgDĸĻω?H+mbHĻIId)uRBSVtN3i&4RZraN9tʍSf2-w:ySwjy܌>>\,X#W fd|:G$9*Mrbsv #Kr$g(!uJ;ɢd|n/)+V<@^,YKf=-/i6>?u圅se21u "\H]EV-z]w-%f% JTTVz6--X~ɗ2AمrOKK/c參2-ZqqdōA+w[UkՍkXkּY;}Q)uVFW6Yb*Q55Kjml1xc&M>no%bKc]mV­O%o;OuMoCsguuuLw-G=na^}`b?|c>98Tֈ4jk5u66wſ/qHQãˏQ<^|Dɬ][t鉧D9kβ?py/\huCyvͫvqǮ]9y5׸.^q#ƭSnvzv;;w.GWv_~rNΣC^zncO%OO*Z<{HOxO~.}>[5/^3K}_^Z7cߴ?xv]{;?|81ӁH*?;~n`ࠔ'㩶hf&v@O{v< 곩 gAx#D=F8l!r PyO< Y688ap6mNϗJ!³f{%1V ~#p pHYs%%IR$iTXtXML:com.adobe.xmp 630 908 1 ؾ@IDATx]`U^M! ^DbAXkGq{ٵa/`[um`׺ Y ("""4R^{3 Rܹsof7sqq."("`p+"("Pc%("@QE@PE %(0tSPE@Pbg@PE@Bfxv^We(C(؏p\x=]?xv""e.}dRBܗSMp_ߕ]ŰfetmlYb !HR}֣cf_q]xr ش>2EAmW,ªQ^rpFxVxf$iH@usrE1;G-ssؼ+֬Kiw+ݒ=&~\.9nW! jzt9\.Jkz(j|;K8hQ vw1Cel̔-װ,z6!Q 22x f"* EP| 5z)Yܖ5 4uN6&˔r-]]A@%P-JT?Tؖ\s䲼X4nXLk_q`"VCޱjkn#ĸ]]OHRE/mS)H/&eJr L*zj"BHQ*EW~FǷ]+v!HK. <$%^^sVF-jM |V"fNi6NdE`؎*uʭ'K#_?+i l)[p|̯/|z\.(7~ (.0D} ѧW652qIcj[Ȫ߾*-}sP\)ķ߷iJо],\YE (1nS|D .Gqg/R>}-kbMCxR`Ҧxv4- .}0tYUfA1fcyk\qf>N;y5Z( bb>1c5m0\e^C*֧IMplH.@Pbp}{oD-i}ΥK',m!_13VNPE@'Qtq5p-Ck$F!o#1̍IӖ }l=;jE:qE#iU^So4+ot/ǟo!a*눗F.9,K*Ѹv1`*_ <OyQ6N1[/|)Kʔr]"PFf//$_}'exQT7Z-Be-Fb3B!7+]O.¿A晓g2-ǍYf':>*F7} 㼳k*k%?VaСU;-%=+eHYR-/Jr3a! UCA)1`0Do0Q%m:F0mv3xTلFHk0]S#9r!eIz# s/@ m)z) ]a9@I^MN ɱ0d!?+cG5"/Z6/7Y2 8fԂә€FOWVN&qHӾhÏGbFeG<1+؃Xt:do֭k_kUk}my()nhE  o@zcQs#eRrо7]WjF \Îzp1q,{S̞}M>$=jڶb!Ÿ554̎)FzIDKL6*[-i.t}sG8LZr_& ڶ)Itk;AtH.:u(DJJȚ u |3O$`5pHHI#-6~ZգrPh|=jX&. =3R\1HqγާWSf3z"־2~j? e^o2R$ؐUv  'OgGRl܏"zX0%tT:Bm: /ףEBKodž\]m!`%'VopSVp>+Ĉ0ՐHc銿aCǍDf[v=uaPڋS;pEkV$F`,p䑇q,/3 9Q_[oŎ!p#/o3RSWUx睩~dUWĩK-"B~]1F=KMFΎ{>EaJr}ؠڈsDU_~(H9c@BaLO)'l4T2EcqvECÆ!Xe9Uri"bLPvzc*K_c`w/D̏byvOp{`Gq`)tնDX=8bɒ쬜q)TH󘱟}zbС&f~Zj!e **;)E'JhӺXn=&+ڋTG,k/\~^wlͭ zCnt|7gI1*в.jQF/SJҢkpӦaDO0r̙cƜN1-[6EEV1H~2:̎+WTBQJVVe/@X?GlLbV94"P+UUbb"$-&V'6jfX"pԐQ*3Dv-lhф_ז%b ĥ>7 Q|l<,K1 uQv0'玅'pWK f< Z1/i%>&ˇHvaW $B0}KJ4] *7Vs**e[#ZV"peC";p:a$;muS !֨*&( zJ5kWR96aW`M$. 83fAE߾]ѧOWL&^=w`_RHp:GkN\޽ )v2ߝE>9$C U-lq7eN^7Y/Ml/bz32FdIJυx ?{*b;Xݔ/㏿ӧ3d3c0th\߾:9^LfDdzKHVE-Bo)VH1"zJ8Yc!&.ӑ^4*5/%yLÐֻwgrUcǶ<Y֜!*c'e>pZ2MʎʶNEIbR"+20o#vһ(,(IuVQC}sKb7"-:c"p@tP/cg%2=&''U/d!c۷ocH"ѿ@orX1?hF D2LsbāA:ciBQ-1P ɱ_}h~;/~`Hy֕D͐cեs}&1,<ÔF%L<|+hw[#hlGݗ^ Ң@v^.ROP x#KxUyN *iDYQjpOkZLx&rsYҹ;ѯO=5^B-Su_d<{h4֣Dv%"z=Ij[SZ[XE@[E!`]e!V~;.%=}p4!$HIsTI!])x8㱋Q`צ4Pf=;oj.kg̣L X3KOSJ9?[ˊkJuFi5!Dz2+<8r:̞ӾlOџo؍ ے'R9yݺeG$X\Zu_E@1E@cDhh*}G^*UIz{5u([#֘h"P'&)F70gqu$Aa?xQ)N^D@qVb3! t#!و E~#Po_[@9fPv3y_BUIvv=n+֥j"P \#_ *UP-DdBSqYbq&ePDvGPE!ChV%%~@zz*z܋g;337Og6>-'o s.69! YtZtDZ/@F("TALwHoL'8˱zu&6N]\\\K.yyy&PgށP_={7灵?/{Xڼ?EEM9*6v :bUpE@P:@E ys 6lA-*{aӏkHqԨ3$o9y4 2d84kG cԕ3 &L 1pWl #!''|sHtQE PoQEڵk\~y"2YI 6m*С3Q-FHr}I""k۶9 мy_>y3 eHsQR:TZ0iGPE#PoQp֥ Jc`0T-IJy-<ؖ%#RH.urV ɵM#l,RE@+bt@L۰d|%̎ ?G}0rr .]Rqc1{ѤIU_U9bZt;fBrR|Of!]E@Pj59jFi`~8찾6!28p_Cx<`\8ƏĈ70GDzߝ h-F"%ܯJ!."(u Gkƣ)z-Qs5%KҰ)-{X -'ɓc eRR#\&fF==wO$X97Rjٮ("PpQ%X)TU Sfq(8^6ozOq!9]y]J;;Fأ]ԬDze-#I%r?&i"/A鱮¸eu_Pm" J?Z7JH) |?"r҈g!˪"e\$ʐF[."( zKS_Bp~BZGuFy[6o={9"(ԯ1vU%~OI$E@PE DQME@PE`(1nMVE@h(16VE@JFE@P&J kE@P6dE@PcüjE@Pm `QlG|qLVy&+"m1:\./'{HzIOqnr"(hPhIN߸{1$< _A7n1/|6oukE@Pz+uɏ.a<$|ش:a`0oFjj2}*j3oW]5:diH'vUݪ2("P[h0!k] 9s6I|$zs~>-T_<{ӍZ'ǠeFK<C6Vo}':unGɐ7""" QNk$$"(@CE_e^Q0fqb9TTb•T6F-m!^TkdQښR scժ<4i}f y%%%f $OS`F5YPR׺;H'$(c{`Jmzrr2uե$0M'V_d."(Er,9,`8G%jEB$?GUJIOTιfC("[1ʝ4_ԭI[<+H2*n*"OUj}*E@PX#kDG߀I1Cirg|_Nm"*"n*u]; Tv1o=Zcp k\{7uL8g1 [>u3 XvHrtb2mݫ:VnWY PE@4ђU!2pG`91c..84iifᨣ-H0r̙cƜ8hٲ9.8,ZyDjb%\%-pX {K*"6 FbH1(1Z$'"_r))I !gKKh׮,hф$hO%)Qf!Kۅ͛QTTaceHQE `Q$<ˏo&~|mn)܊MjUB-rr fMx!lw1C&(X\\qw$$("Ph0!M HaxnCb)!èW{fT}vE>]מa,U_xcvy,*l0`__!TT$UE"`{c_a jd#eڷo44 2-?QE@P!!FJMtCpmؐӣZv]dU…bh#$i8I~OeᏨNͦf"(@}D hT. /ՏVQ+.b"F06 rbuV ⋺rBuZaTE@GĔ#$67D̛:N4+EPE@ĔBp{v `ʌڔ~"1ʼ/5~ "4tbLr,Cjj2"X;u\JW2h٤K"(Dq:X0Gu0%,;kSzrMKpĐAcx (E@P]F E\4s7Pz&C7݈ V0-ۇ#Nr\|J9]E@P=@b o] ^\> "^?ѴH}͖ԣVK*"(D c["j9f|p~}96ne6㈙1iWtgJÌ["(5@$F9aN?Xi"Vx.t-8!5&o^KPEzG׋ħ~θ+LW܃޽59n,9J,Ú'*SC* ÎXq]5oӜ|M E@Pz@Uf-&M_!?3^}vk2a<bCYhoԐnEney<45Wלx[VE@9J͐M_n#OgF,$ QR\d~7:` .>=zt^{udK 9҇LrtLwhaHT@E@wUH\6lȑ`flp͌bq~%cM,R`0lԩpgݺl8ᄛѹs\|pc["XLwW7Z-rG=[k("8K"7?'޺>T{1++:>p h-)vڕJ̙{. Apqc^n>C1M$2f_3>SH_~?AVyy"YJR("61'FG}He$xw?j #FƏ8w9A3 . mC dZG*"P)1Z5em۶_KpEAfiqE]"cqF&jfetOG'l$F0ywx4p_8]c7Ј;{\Hz[2guZ"(u%jDJAuɤI$!D䄃-fz1u sgHa'/3UT\[R_g"uviq .~MĀ%kȫQE`! V B{:xJNc͛ANNg? rժLZr:ՙGg9FR~BBnB"pԤII9䵙ʳyG_OE@Pj4>-|Oa eru+dqۊH djBcShE@PD n(G}JVHiƳj.u ғԌŠhk'E@P Wp2&{9ЋO?Rb}=c_A&`qfzY3c}i("qE`opxHG =uTVܡE@P@\Q,?cH{00+9xq'5^b D+"(!sb!cOon)oN[SN0sJ8^L|."H/+sL\C2%Sw}-#SbtHqʵm2$$Q3*-n 1eL<)E} -WPb@$4,!=F&f+un5t]1&F1Du#gS9BO'pL"S(mY cÆd,^36sZ)=8אES׃1%F;0=;KOuםƜ/ᙪ"1zq(~]c=x @AJV:5&of'`N}'i C)C(9~|7 JN,ȓ֠Mi#=ؘϾl{╱S>*qUl-jV%NXEz q.]:𡳾EtUܲо}k#=Z}+✱P3-b!+z mI25YPj).X &M|z4 !p^S}f?q72Z㼳㳗wp"/GR ^7sF`e)iX. !\9#lCuSi\63iIunn𞋘ke^Ew/хKWRJs.`Qn|8rh06n#o [ljJ}0g"<{tSw4Ku@3Bs:4âEGE2'PD+`KJPXXo."PcXefgcڜpJlVui$vхey-n -U&X!tҔPyJuYVռU D+_MA9.ԯbm-\fxJ*bҤ/ !uE$?0l58h۶}nm&4[o}ߗxRIgc޼޽=5bPa(NH!%%h׮ТE\P (q>s9bcqy+9WE@7VM2S20I a3#MzЭkڷn3[a$F?ODhucND::H>DR ]|A@f,֏nZ?~x8F*Qi.`CU1P_֭[[UdG4;Ԅ$d7p`_N9'z/bIy9.<%%S1uf&G(/i^TT+|/)iP (@}(S2Ā1 y8/GOI^AE}wbqdUJlξ,em][^&11˖,DCA]McD;%e~^˗, 1.ͩ(5p zi(՝UkA}Qov~)~[uHgTLu.ǻR 1ʚdF$+p5_~2y刐Qy ^XCXA0Ww&`҄(s5w}s#DYy?T+xyPUyRZIO!ۥVI" /x[̴38YgIҞ$?zr Z8g`Ѧo}':unLJ1jۯFG͋ ]E@YO6m|QF[#+䛒`K+V%obXM9Ol˯&&΍Yx<ݻ5c\w?CK>c/>^z6do #q!ĊS>x~rITx'v18ÙHbXry.j4Դ2;Qp}CVf rOwg7\!-NV(hIQN&No8TjDd#ˍhժ,<0r'ԛVi5_۶/UI؅2po/ɳ/?"Pkp#k5]U8\A/}?!LKo^t$4ۊ"gH]:p!rDR {9yI1җ8|6Gsys YZQuV~Nl1AN qŊ7w_æLnݰvnmҴ9X9ō޽#oSU;c__nG#U-)41v XEtQE`p#OlNT zpLоJiwyxɸcСSW =$lXb|ӓf6hDS!B5aح_7CkV,Ÿ'^4Rk^۪KL"*w%F0|EcU.Jh97_x-E@Xթ=dsТy1?0f.Z!ƹ1"N+UXO+8ӯo,S}1¾|Z|x7)iQF+-fifC>N/c-jX!]!&- ~5`q[/cRk%\oB|UԖO!8p_8\ra91]d?b,O&~%P~%^/q?ot[NKJPι^yl%Jh!тv!9QkJ7$Vv:YUG)~KBtLN';d*̕\N YMie8kA~?=߷-svہ_yHW\<+|B56ߠOҫL v ,"lq8kpT[߈fv耐_bb%'~}x^cQKri;C,LdbHQr(Z!BJ%L_*Cգ7Ld.ceTז?K?;k[|"8>煸ノ8ACǎ1ٜ<'&PA}_wVͦ( `>#^mwg㑧;`G.|_BmH1!5k˚cGX9)d @t@k+ u<[ SUK֗)q%FPZrF\Sti9/a}rjȗ=~S@E/W;sX|J!lcuŻ^]rHT֎;%'tB+>UHGEy5l9rX| 9:e)u'hڴf18Д)kD!vMDtA]%:iZi(㡇wqo<|Wr$&1 ۺoܲgqFR~VW@6̾{a^g^BuHQxo}:hi>xLѨfkDU!&q%F[C=up3^NԶL.3Mwqw0(IYWP]A@H/kzWHwzxƵ؋$JF7ˑ(G,Oe-)`mT$EKbQϼ&=u8!-gU#m]C˘zw[l1TmVGR8 hզ5l9hg>Nk8ł<ϘNk&<]~f@zCwH+~J㲭OH?~;:v6đxtt  d҅O9v6_{+yÌ/kot]B$ǯL|%?{,Z܄`ci8(6a̼sB +PMQ͚9+Wm0G%jƈ:6J~ܹK Œ!sC>]Y S=9^ޒɮE`#=#?c40\=K4vfZG%h֙/թء˻QLP_9x?S4 Qu^9xMkyyIY&gG,Ni_L~s 8hxґ6F!)ZlN]=$o9C7]=%GF9#oT񔼍EbcuOoO~/j6FEmp/*&/Brf>({kTـ)"c8ңIKol :t- ZCZG\zh;> z1WqvX8WCh#;-[Af?(eӾ6f|s|oCaq{9&Ԃy?'Up1`2gϦ?h=ϙS2Ut9a„9Fxf׋^‹$ QZR!!ШQS)NK/݂Ad|xi|I|HG])9]̞^|/1UJ~8o -S5u`@]!#"2|D ~?2]%t:%Db|Ęt(ᘓ 2b <Fb(οF\yXdU8< ue~fZ3Q?~h޼1CS=Kq>wI'>)ۍk0~;Xv#,vY 8lq4a nڈV`cC7o @|ñlx rןgg3prb􀎝Q4Fط_|'㮈fJ-9L6}%}TB$Ãhh3ΐEӦMh4~ t>t-;לRs#͚5f\Æ$UiiX>ظ1Rרy'Mf#GXѡC5*IP5J_c?W*3(u4%|Nnbf"dw@Lxqp^ٔk s>}1{h:x-Tݏ>v W(-Az<"JX7#Ͻn5Ȍa 2MS {\ܢkl]/eEEQw-qex-"tH=KM|ȍsҽcƌ#WaqT^z***>H7fEP 1oM9}!4;q=`˓@+S!LCjTn]CRlވ yFk:O7ҧ(5NTNVkHw+xHhLrYbD"v©9t%E[ & {O7%oQ w1<; H,R%?qf}FOSV߲U[SX9E/!d<ы]#IS 3qÝN!toK(mQ5j-y˴h-RHzBjzsnaW2r&K,RQy$Vcڣ{@G@G?/jىHkHPShf|9s߀C8$L؁VMäO,ƫ%k\v(̃6MRvUkZ#:VEڵ:Wm.D+ʜ%ңǻW%nabSt[P2B 8mRҰl]B K%Պ) 5C!8$CnZ:%;t}!rvf*?GˆUHBܗ0/'mI[&Z\j]Xb5>[I]|Ş"CD9`c#{E@H7cyhմ볓Sc4Q]J׼.> >PbM^۩БhjڂOЦUFF}*5URsJ)xY:eN`TyiQeұ|Ѱe8]E@83(㎲-1"t$Os!3]B,B#8;Ǖm"7k]o%Kpaf_6e©hwâtQE`! H-!"ّk?wTGWbtN=u() <\|8 9ctߝ鹊" b1zA,q/1#C3M94eWp<ɢ$~䓇?9csE@PE`/1eDӐ5{}t?}Ci?{_=?w3oLp>>v=SPE@-D|V p90E"O>J_8b*'ւ~ V>E+"*1:2f8u#&Я˕@p5`,Qxbi#&Ev-x! Ni#s.32Z(9hኀ"Yk:c4gz~Ll.>ė_d ο`,ܸ?7|h)׊U.Rk8-\PE ԈZb") |<8̡&Mw"-ȹG26| cԕ.L0g/H|ņ dpcQ֐Xi"(#Рθ0n8#92bLiim4v-hMi2L!q! KgM~)+pN&µ-[׍Rd--YGPE#`Qԝn3f~ ^z"!\:hT.=[&&*Q_ É4xϰa&#]Zԍ͛ՙY"(@% -?R`?;?/Bn(%{a[1~0'Fѭ]SпwT#_m Pc:&QE@C4btvi.XGgk#L .\&^}%'8$-2ZPr*8_E@Py5z> :L؊'(spy sYLw>GL*+;EPE!PK{uߩGfnWfΚ1cB0h8|`g V|6ƍ{A3@u7a22N=еkG8/w<."&NUN3+"h+vZ_%;w F: ))lڏ8ꨃxc0xp?̟s,$V2вes\tqXhx* w @ r5wY+("4Q;ؾ}{+O=泸_FHI !gKK$8E&,CԥvQHʕ`nQ$HߜE@Pj= be8piC.OIOO !'dShMx추zq]Um!~'A+("`h0T3O p0+k[ 0=3o߮ӧ+{rs7Z5/1zt`լHڵAm+Vҥ: fn_E@Pڏ@ դGy>;wu<ͦa8!Ƴκ|0II~<hgCGS:D* oxʂwBEjn("\ J/dc`*յHLBmtkd25~?ƪU~%m,$=Ksk$=YNqdE@Pq4QnHee!=zteĐ} р& ݺu1ieea_H}6&M."(ErJaTOiN dGPM9R]Zf?"(G&7T jy*!n+"Ph0VjE@Pbc,ԲE@P<JujE@Pbc,ԲE@P<JujE@Pbc,ԲE@P<JujE@Pbc,ԲE@P< rLz1L kQ8KQۼr\dABE@P/ #q Neq*.gc/ L30(NeKi!%r4GPǶm&'0u(((ˆb}"?a&!C,|:m)khMrIS$N]E@P. fђdXB8af\Zr:wn/~_~UX?6iSTYw\WPE `$F;ŢEp}/q$Q"̚;fx 21}xiHH! cԕ3 &L xbCp3:U.x<{@v3Un("Ph0hiӧ+^|sfΜz.>kׂh<9:t?I82 ͹b#|R]$Kk#99c[R9=fee.^E" #J8-YnbV-HPz@!Fk:u 0ssٳVJ8('hB0VE*D/&%7$8i丈!D+* H>`D8Ą^k\x&!Q%{ۊ"D#Pѩpc1+|$\vmrѱc/lܘgHI4L{09re G ǃcEDLcI5D$U+E Vrz:fw؃R!K, /!bС97p<("TE|L8zL6`ذxpeJq5c1`@OZƏPZ GP"+DblQQ18 +M| @jjQAF QZ"X>[ۢS˶vZ<`aycXE-JP:@!F;1?dN?CDA 27rH1hTGrxih瞻9I$ʹ2X>iLT>Yd_O,ÒbYɸXX]!L" |)_0zvWM8j7!=5գ"Q(9Z"(Ur("jǮ*UʘaST~vQ:%,OT"-Z2IJ%%%zDz4XզEZԠb#ב_7 -I6]=._7,`Xw=BRPȋ[CO[o,C;C;{mͯ(@]@@1NwI`T&;9`QRKJUΔ֭pK1bhp;|Q"x)U2{sA7lxxxꑍ;%} 6-VP=@IK^_IRiZZ MJVmՙB\7-N/9'ظ1&Mb(`~9?/M硰!j'*"Pb]ɯIL|I{s'Ih0"M/$Hh4*]Pl9N,(*gE,J/ mfcBBv.e7_yo7oޛ7#ކQ %%hI}Ƥ$ :DVlPoMh2 @vᒵ7cKb 2,JX(C!=]217d͈/QYinA z>: AJd[䱧P #k^B˃ 剔z.&8Mr9[IKA `j6rs/^+76#Ĝ<Ƨo{F)ٹ3g \0 iC8vܐ&n%o'upF <ڤs2|8&#InTGvoҥ.dy pFF53IߗSNHU7n*1W_Gzd!$4~L&O~ie͸' Zl1CA-)+_ZfB1J--sK ĸ(?Bs9H Fd4Kƅ&#c0`T:3b2kR\4SH*3ʀ!/}vUKɨQWKӦMqT喋e-Z#uݲf\N23 ^$* HƴPCʑ'6Tp =ҽFuMLbu[=yj|8t3P3A L1 a@ "- >CiH.G4HDx4Fq(^=.ː# ǔqi,+sB@s'G `%>nd# I]p)LiQs:[F]W_Tx+7PvN |R7ۋk#An#P#;H2UR@X+v;Mv.1-|rrqa֌g@ıeيT[q{6˃E^Z4U>YSPlNk"OjvŇ9AR(W\jGd臶i0en3HQ pcN_e^g6ҹsyꩉgO>Ud„]pK MyF"RKmɥ.+ڙmTkdIfff : *BPctIHpȎr|y>-1%r8p8JXQ!֢} 8rD_Q?|.47v4nX^}w'O?}RZn{ F-UtB<8BἔL7޽U^z6W .j*c jt.8|:|6)rműyҫ.QzfP 4M+/NfNgIcr9-kBWe4sX`ie? <yͱ֢:1F$t.}b%CB`?FU :МJs(!iqqlۖ+p<Ύ-[vBNc}h"I+ͰG"KVrq,jVLew IK%&LVMk!gJv֩kv6DCӍ_5rErysjb.Q#Zؑ?-dfrb`(572D;4y8NVi L%b0JKqz^#aJ'΂p ?<PPE]{Oʠ{yry '(8%;dF[eˊUҢyG!j 9M[%sU#Y$^27 Kuk;I.Eҥs4N+kJN5l9XJ:JSŚCyĨ m7g)ګj&& gdDMkui!_~Z.D~h GpԮΙ5F C}X 8O$f}G(3 qz3$'BPұ`US4;k+;l#Y0cqݢQElO"i/HFƌ=Q{ G4S jc T}쎦2,ILp*ı<&Sj-pR$-N>:ͣmOK"~6&Z4虻SVk$P}\)N(X/vr~0PXV]喷͐dž핖- npEk|<2#Cj5f;-?5AOk'Q {2$5Yji.?6Ɇ-=۶'˛Se99R`<7Vzg R=:t:)_n!CL**]8q',Z*n)vqӹ&pAp)CgF@R.Y\8;T@ R #H(JB#4i E$售k{]ƚ2M#?E<~י> ǂd R?/NJtQݑ(=OhS 3y5ѭ M:!̣kIber bojځܴ-p/#5w7i+64hTQzM1rz|pZ$#ZUq_ppjp (xZ4~DFIۚi=_u4ZUKC:Ԇ @u. oT|R3{)JBV)fW.3 XEXOUokpi ;z q4 툤B.͘"/&eբJӃl,N8xWoG/HiP:U8cHɒ$?t }yh3* 11Ov+cti08e2zXލ*庌 LyYguU]$drj{ii j)5 6ٷ1:S,GH_EG& EZ,JJJ%?XHC `\Sl$?.#Z.1%AW4kڰ){! '*-Ɋ?ޝGep+8vu(99%5Ν(?3Γz/&+&$眮TtoŬ,bmP @!e3y1 E6&f:ji#F(݋tQ|\АwMrFDD>XfO޽\)b]G`}*ppڵҿ7 ^#="Ky8 i9j4? Ԙi/wο/8I ..'y|t9o9 )s9$2lD3]B g2NkՋ4qv**_ {Q -6)M4AZ4/|t'Sc5V"8\AnYx?Ѕ;6O!vB^gվ)Xo<E? )II>bh\{}u GAiV;Sfh^tk4ODy11A??t$y0$Tրܡ4x^7goj[j! )3}K ?}< EAش)¢D06Ɏ3S]G:wڅuCU&in֬ :wj-Z4c^=5ڿE䨵ҢE4Vl&J[GAj]| wx`$ GhS]HGLѣ2j(7=NTA';іW ki8\_pc~2 N ⤁ޱ1_J],S8Nk&-p/Azu[ E+7Uڴ)+. -\g6_]c!> /2 52&NX-r (ϸֹ@~}wmAMVϏG hZ溎Ei l&;4Ќe!A[6+i!N;e=?zvE#y,^}4nQi8Sbߖ'ljM˫<¶[_mh]uB;:燭3x)#eh9rP`*'J./B՞h@a@$*;R?uboE%R4OB, & A~|OgG?5>ş#j#ZQ&ԗ3;ȋsY"``XN9Voމ#@A+շ=H{?% OGya% &xf?ȎrqM0Y%׮C7x QB3_ϗ>!M9ٻddIށ9piPBfeҤ`Pm39J'9C-5k2d~R&x AMRJJ2gϋ)"d-7@W6aQOv+Y2A>))HV)+&@͖$7fIfF`kF|~t)'mQg>cf/0""E7$'RaN$mQ~ҺuShi8xŸSF2`Q Ӗog +``wD6)LZ)ully22Deek~rxH QTO.pwx"e˖R;4 wF.wHᇅ8TXX,7 xZ Mݷޚ*{wzv0(ba`iQJ_X(̚27ey*#E.d];ۯ$…+eܫG:Izc|2Q Ūpcفl r0sEX=@]"bʗ_2ƎtK%ɫ/3&;_/v‚5@)Q_{ jaPtwOpipʧBdn݇j3!KeĈk{L,^N8TPW 7\(g_{Ez|< j 8\{od!)dPjt99Y~]~m$I>H3%K\&/FɉBqХ)ֽj3?Qk ?Ο\#V`ՋWO'nZgC&7t!H*%|7wysOS>h)FҲecLJ0tayß .84 Y/(n*f4?k,; \ҡ[/+#è,FB Va0~(XWi'*Q.N<بQҔ9jj|\)cރc]¡;-4(fŒ˵29t<~bE p*h݃IB+oH8ģq&v9&!7HϞAoɲaVׯ; < J CCII'W]=X9G=}\$Rml,?̋½ʆ t&i vȨrB7A_K!4KՋ5)hP\s`В&ėQwMEc4ĉk+>Egt.4|is|:4-as(!-['k4 z繞{탥K^K;, (^+[J=yϬocJMg4aR }4+aS֯ u)<а =@"]4Z 2v7E6.IDATk>p JA޽J+;wjL iEVMLN7+܃x̘@ I텃Ch@ܖ"L.vd(id:@ujI IhLiNM.w1&~!+Wfa6abhJ(RBK}/%?W$-OBSטz}5)T:˗V{BFѐ"S9~~/^;XQE|\Hs 9Jnn>^0c~ mK3-Rc АkGa"/L:thaܻG (&dX~MhMBc5z+G* O9Fl&&͡)u`E eӦ0% پ-mvJz:PE'd$X9C*4I_Cβs.ETZw ¾` &LII&R91z?Iӫ3?=5H:y<7D.P]N.̆_hk&\xJ|_Txd΃%LCR3@diI'N>2gBx-/UA:`ʔРJ%t㏿͠\phʩn8LS3xwoH?u5:ШQWC]E2l_)so0h$(/ota+ hc1Q Jŗ엋_`CϕjֶmK/  &X@>;vocL,9\#7Ή5dW!o=}AhUjYߚ?_?_0ҹ&"j,4cӧJy4i,:pc@x0\QAAcA"۟0>`M?&JӚ4Iu@hػ1IYԈófFPOe~M'϶6)Mu8b%@|/Fԋ<tff j@X\ s ~ax>n> }$6 8PSxß1(ϟ\oğA_[MRo__oX}OI,ɟ9>DρE/+OI2&@l,zR4X89U^OEi0qb6 l`9X4P:u/ڤjvnw `0<dž`Nk|o A 00A `00 `0 F00 F0a~z6ͼOoNWnncz@fS 0AB%umŠ{*ZVR2}e(P a>)7W&:ňHJ>[DيQLs BvC_[[H֭?g6m:_c62F0r CWj 6^~x/s+իIyuuEA צDd'e.%nZDx%}f4I&醝NJA5`=f9H)PDPCS!Cx҄>XP+>vt42o -n` #HQm5n)Z!s 3iآiiM亡Kbbx q7paΓznwuh4,:\*#b(T@(!`D F ̎eВ(lr!(3bT*sDab24561t beuXhRM$?-))A 1 *_o'QZ,BˠM(z֢iwQ]S:kGzK=Py2 n)`Qm5W1f'Ȭ1\x5doȑG/?NP["w5_TB^_ZZ?T׀waM1[]w+B!F*KLAޯ̦j|R\ٖK/}fS@<i6.R1"cȹHc 4&w24ʍ7>\[սO_ 1WjCmtIW"/bYp54U~[B@g'A ~s?I.qC9>W"z݇MVRXO9LJLlYr?nj:(ie)܆{a{^#n||˖l}~ XO/P9'N|T:8*)n @#`cw!0CЍ _!^Pl-7R-y3~9X Ob4l4rkCRRAuis<b z;G]_^#0+AJi3*i&?f X[Vje^(ܽ;3Ke_&gbRPOKk Tgu;U_o6Ɣdh il׮}Vu:k<ҥ ©eq~=͏4=_Y:K+K/9WjgJ=~hh~c!8SjJJ}T+YCrbt!O<1Af!nZZZ/Twhls6{ݠ{JݧOg9Ns:EGg< ރ zz}b( 3<LJgOJEQ`P9kv։>B]-C\Z!w* N:>`-!gWkbb<ٔw-MHqڵqm{رM7=]seKff3޸ѪH́J+7 5CU8 #_㏿ +WMj9眂c6hN&AzC3M /x:ubztЬ7NspY_NS&KjSj,]܅k"=ۦ],KSff!:Q7ҸqClߣz:I:w:^];y\%Vì1h3I=Ǩc("/2`@OSnoGnMSoA e`EhJ8< P(APiz%99AN92A;F):f(7oڴ]f\YYYۤu Mhޤ<= VڜQOE T\ ̠8/tiѹuNYbyiv8mOX=ijf-=;15,fH9M4 B)LѲqfx.Q4: W5%^"@q}=E#<oAJ&JmNԤqp<_ ,|0X blX9a5X(\ }YuBvy_X|ō׉gq*.Z47 [4 SE/ߒc3 z<ŪCq{-y >hWu${AJ0xYBIרۤP!鰮њCϻZQ[u{cZ}KҢ4j|B#CW Ma%,,-f]ۺB*~q|]Zɪ۪Nn Ssyko߽PG݄:>A `0 F0 ̦A `` 2t @ `cl1 pAp)CA `0 F0 ̦A `` 2t @ P>Fn ޿V1 @8#plȍJul g65 Cђj[:DT6s`@7l3 c~ CA `0&z`0 !!A `` ^04 @ `ct! `0B#C A 22]a1 P@cQlTIENDB`nim-d3-0.1.3/img/ex6.png000066400000000000000000001135471365037635100146120ustar00rootroot00000000000000PNG  IHDR`7  HiCCPICC ProfileHWXS[RIhH RK E*I ĐD.*v*ZY+Z*oR`]{}|9){ :5<4 OR aMJMcL g_.eE(CQޯ(9_EO @ y/Ke}zfT@l B,U,5.Q 5T$s ɲnzV!? h߂U"K!CGB<*/oC;O839yaE%P\˛K^b(4,2^Y3ۭQJLW >=(ULRۣ|9 0!vB 68\gdù ByXF6#>vg8loO?Ibko!7ŢuP6LyNB)qbldxe6 %!j~lZ,<^c/˓Ջ-1\U JTA,xICaڱv$IS/)-iq07BT^ T1҂DuxF6o|:D,#@ g@f#E5#P H~!Y!(Zdf U9 y d8Z2x 5D\sPSdžhF1$Cp#nx4CeOxB <"\'tnO/}S L0B振k '@~ȍ3qx M[ _u]cGq`÷Nڞ,ʞ~!u} |U%;Nb#X`aDZfvTWc*'ܵ޵z@X|? ,8KTb7ŕGb_S¼.ePg '0~m+dj~ 10?a`< L],3rU` v=`?hGI+upn`ABG1b"Έ;"aH4"H"A;YT![:g0r9t H b( 5@P;t ꃲ(4fh1Z.C+Zt7ڈD/N9ڏL cb qX, d< jWNq] ēp>×UN?_}`Jp&I,LB)pp>M݄D"I'zç1MM\J@KsZ>iN4m MA[FA;AM{M4z}~^=Z-О]ݨ}EEV3MXBe^].G;OZM~=^^R]z%[Ow1053clcat { &V5dbL;&\ϼ8l{pĒ #xg4(HhTfGcqqJ&&D&MΘ4??lwLQS'x٦[M/EI֛25gg1?fc[8n;ːf*YY} -mVVIVZݷZXgZn`3Ǧ-Vd;;{s9j9}s78;NN"jΨys((QQn\...G3GG^4i16cƬsvWO\mwƻ-rkq{wvArX؍coy2<'xKm]}'g9_o|# 8qquXt7vYj[ ?e;ٻ/B\Cd!Bq8s9'BЈвж0VY}#ND""WFq:nxsǟE%DUE=vEL@'z½ILS,ƮgDĸĻω?H+mbHĻIId)uRBSVtN3i&4RZraN9tʍSf2-w:ySwjy܌>>\,X#W fd|:G$9*Mrbsv #Kr$g(!uJ;ɢd|n/)+V<@^,YKf=-/i6>?u圅se21u "\H]EV-z]w-%f% JTTVz6--X~ɗ2AمrOKK/c參2-ZqqdōA+w[UkՍkXkּY;}Q)uVFW6Yb*Q55Kjml1xc&M>no%bKc]mV­O%o;OuMoCsguuuLw-G=na^}`b?|c>98Tֈ4jk5u66wſ/qHQãˏQ<^|Dɬ][t鉧D9kβ?py/\huCyvͫvqǮ]9y5׸.^q#ƭSnvzv;;w.GWv_~rNΣC^zncO%OO*Z<{HOxO~.}>[5/^3K}_^Z7cߴ?xv]{;?|81ӁH*?;~n`ࠔ'㩶hf&v@O{v< 곩 gAx#D=F8l!r PyO< Y688ap6mNϗJ!³f{%1V ~#p pHYs%%IR$iTXtXML:com.adobe.xmp 622 1216 1 <[2&@IDATx`TUƿiI& :DP "(UqEWDE.buo6(X@z%Ls߼d d2!d޻wMyߜ{$0 $3a5"    Co   H0`|c:׉Ց "} ݭ{*y=ziWmG1)^%۱N)),n @66S"6p:#1jRuȇLiI<۶@Ӧ9f? `sy|Ӌظi'RS=""F_z|V1VkIH n[>6@K[S٫/Fɧ1|0ytLTxY:r=poE[{)JHH0_/_>,͏ @8A~(Nb[a݃Z8dũS`?*>ZÊ.lmÝwUֿ 7EәE ᢋŦM 3Ӌ"za~a,2t|\O>pHHP$Q;):A+d#!͵ŅeE&M i+B */mpPNx*{l7_'R0: #=#3g5zlę5@SUPb0\% 8$ UqZ~Lu1mJHXI $z<.Fݺuzf;|">!y/%~bn76*9b|L>^$@$@$@$P`qbҕhذ ΝۢHC|sÓ-cA3" VwIHHJ$i ֈ+uo+dfdK3|5~qyJ[ i?a4)ԺD$@$@$@@YtaIG)ۖsn4CU\[jSQ(&"5N+g   `6 rY!,5hMuRe 'Һe3 @$G `Zҿ1$@$@$@$ X{c ^+& %   #Ng @<PŃ"A$@$@$@@`( ăX<($@$@$@$p( @<PŃ"A$@$@$@@`( ă@ 0]bh_k7j0}|^HHH 9 $ %Zrܓ HHHJ $.tfaٲ59%RƺtfBd_:P"v%|l^"  H"I#T ].[vpȢGtZUd,^Z^E @ ++J    H:fYF<\.A;ʳʲ^U+==?V<7Ə?$PX1+zu. Ց(,,6`b%쩧_,֬>*"D_u,) TD -`j g!WCS~G4ic0Zj/|Ա>fd "[HHH*$LWm۶ cRt}e?dK~ T|ػe˒]_HHH`? $S.j-z FےMc"   xHJUhŎLd/{$@$@$@$ph9Xr<HHHAi$@$@$@$p(#   $@vx , %HHHH P$8F$@$@$@K`<   8H+$PEt]GX T@R 0#L ֊pǾDZe|   $P,r`|' C ē@ 0X~a4'*j]׿С7`ĈӢeb%zvFLn ^$Z*ԚmvyKx 7 O7,ǝ,_gy6lx>dƧbNh׮Y,()g;liYÍHHH@0#UNxj{X>OYCjr5lɒ50L4iHE0vp׮][`~)?/!>b.KZAn @F}x\HIFݺuzf;a+"7_:Tٰa=sL o?OsT]ac*((43VsHHHb$"BF0,nlZ`۶ȶ5yvcrzQA0y:XT?_"ִ< TD I, RKY[ѡC.~uر[o:r!\jsiFh޼)5k"Md Zl,5qaMkIHHH,Yݏ 55EKh<58| 4z 8n s/Zi!_zB$,9O}bӡKke!n @ENY}t"z KLOD$qgJ2:ԪmAf Nw CXÇGLrsc5h ā@ 0[x0x!GMlY<HHH ~XX:hPx]   HYq ;`d<HHH*Grx6 0 FHHHHr(*Ǐg ;`d<HHH*Grx6 0[*PDK\0g-$1   (%tLr:SmEmf"rDh|]d;Ȉn T@R 0#n,Z ӧEf 0hP/ ʈ-/_)SGVVN>76̡ @9t_Z;FӦ9:u|rr*Xnڵrr~VL4|b9f ð!}x-En ĉ@X,.f &]#G㺣[ qDpn̝]43RB,Xݻ.g"rHOwJ=[%  8I#NucΜ>lME-q6`%/"B*M'ߟ]"Ȗ`9f9뇂!|tŗLbSDQ;v2MD$@$@$@{#L/K7o7:R#??;Ə? '4-[62TYʁ\cMؽH,^V`0h_$@$@$@$W!D_Y>`)26 / b͚⌟#–-y:{ڽۇqIqK,)e1orSOT+]   $Z>`a}k{EhDݺcb^s ~;86+PKYIҐa2C*HHHHzq$ H]駳 H\3l5:u'-fΜW_L[^vf/M5i3 @Y4ڼ3FXMP~W{oXQW/QݻwGgv# B+vH߿<}է+~IFd.idsHHHJ r:YEÄi# Ye``8\dڨ4ǥm*4ѺUE//K$@$O٢!Wf^f k}yC98&4S'[tX_~bߊXCI>Ne3 @" ~osUq`JŦMqtGϞP~ ۰xj1e'齟 Iª. ,jЅVm $ω6DmYckŮ6._)j r&N<O=w/)/I$@$n{s [BA;6rxo\㒓Ѫt+"$ 6|$?}% ~^fO5uOx.x~1tGD'j  +,K7O,2u)cs% ̕l_)s rRGl{ u案- zzMM T}(i%  ZG@jcC=ԗ꒶J 0paC&3}ҵie>;;Sbk?/Zcǖ"#Ës 󐴯;N$@$@P1d.‚q̴!/MQުJE/o)>|~z,Y)q.F6M@W]}u~%HHHj5mPes Ν>m'4N#L#݂֬"}~{s$@$@$@ucAA!VXW>Yե[K Pֺ"b|uu_ @$@$@F L"%Ԅ˕ ."^7x.8cfϞefJvV] #Z/}< ͋ T@|4 %đGvĶmKX˖MWZZ*}Ib*9 # !== ^o`-9d -N"PXF2!'l E$@$@$ aU6bÆF̞ dPdَ/]_1HT0g-"tb. L_5yz`׊>dnk'Q6&   M 02kh|aԧдi.zf~ŋ_ZMf@EX9^̜#f}Lyݍy^zd{!&Lsy%PYl#ebttf`IHHC jv'O',fu޻w񒄩3j@U,g0~q!Ə?]Dмy]mڴ݉1'lҘ1CL3LKhSfu%ZI}$@$@$@$ 0SHWoƂ+uXG;5۟DǗUL4`ls.EU|v.%Ńlz]o #TyꪧL7UYZb TH L D:LEϞeq?nƉ~ F݆:!*Tb*DaNNVBT^}3D9K#q)7⫯~lj*6C>p, bRcV[&E`V~"> @`%{?\w3k .xM)1qb5J5DAӍz7}_\"vleVI#֯VBj=[V0'2)zVE8   N A.KT~]<|l޲? LėHjR{/` &/x=8A8>y/N usߚ4j/WԤY^ɚi/ @UQ;S' v k|4`Z0N0 I23׊k,߰IAC^#-[oD[۶Ea)#̅^Zː fq`V 8ᥗ>Gaa2)8省 j0}n߾*M_-_vJ2+'   8hq`vKzSvۋ3%WC"jgmҘ^}iiܲ_U> ă@ *D(ד_Le֠J}/G_*9\;8^R$@$@$@$P9q`xp5Ig F n8kl}ըURSϳIHHHA7ܲzydǵy&Z}dcؙupc X|,KtUD>tcx" `vqws/:.t-`1lV Xuan!4bp8\yL$@$@$@$/q`j54t3`|4įѣū'yJ"mJ3D`+=px$?C%ͳg5,F$@$@$@{%PY_9zȬ9bD?,]˿Mq9*j Ga ,7-ZI 0hP/K׀"ݫ󑞞fښu!sZLh{+f\   N 0aYBS'rƍ;f5aɒ߱ė]'ֹh4S“ON.H BfR>)%V!ʐ WЪJV%wHHHB,`\AFF9dq /,55D裯0dHo_%"8ҥf &]#G㺣[ q壐S_DT۷G;iRp}#z OMN9,в#   = ]Y|.޵f~A :С}֨JdHS+cΜb+EzSeh6n&iF8֭ЧeXRt" bڴ/i95 Z3?|&61R*g"  .l5}" )qSWDW!1l' pQd@b&{_ƿz*涐i۰{wHS/be$@$@$@5@|KD FpRS,ifମ@MeYRн{{lؐ'zE4m5i͚vhU@rI-f!v]iX5YXz,w+DdY-`$@$@$+Dr:fY_]&tp{3~ CqS0XrLGiPVvX*LTYN=]r3*B?Q`9hvmg   '&bEEÑ*-3X,+SWvHVWo0-][Jx GJXvV*I@ėO.y$RiZ7 @%qf^#hgN|OfRŌKjY۶~42AHHH@.`EŏY) <<? tEO SUȉ$@$@$@@5t'  K\Hp Ggl"᫙KnݺCblMYaE̚=hYj'bHHH+\DKd=hܸ r5hP,Rp+*&   H ewe<$ޗDwH̯˖"Y'(yAn.w̒/#=$=)^VvHHHP&Pif9мy3I~GdgVt=2&yօm*Ó㧟cР^RN,dU9P~6   #!H[ tju8Db Dz׬K|3S_kX89&/ j%vHHH E)7H`ӓO/'KN=&ӽCgP2HHH&TY"̏fq`]X -Ô )?Kjy ag%0Ei茽 @]z\vr4"  ZO ~,T񒞞#Yðz~O/KmM7m?%IY^v $Z5"    &w-ﲓ =}ʟˁ@ {~;OB`WP]QG<=;ZG_62&Zdh;$@$@$@$7 k .[f:B)9|<|c2`;ne%VzX5|L,,U^FI :s"t)AfHHHH`TU\>U ?tNb$~i vfi+vbҵ8$DPtFYæjbLtFŗZŒe&LHHH8 0#3_)`knNGJ*tq%޽QO>_P|JN s_? {eoKL~¼暧Em#TkhYkxU TLL>Ng [$3ԩl_6u[P\ʽs8_gaj^3de~wb%;W=1guvf|ˍґH2󦛞ZB0D$@$@$PJ 0!*6T?t )RS=5\2X*hOeYBэz7I2E6/_ Ѷms|/2 rtL 2}Ʃ%~z1T唲j]i1& @m%Pi &-{.ןkĘF7>R%RfYR].:%&YB1{' K"ovLq$ wN?6ԭ)>`'o_]d\|ĂI&#"C!zr6 @*-_2I7)5PH8:޽5 ~4 SuϖXH,Uxkl:4jTO#CGq饣Z 73* A!{C:2A:D2 @TZYą[cـcڹsq¿.IvE>oF*Xt LD<6o3~HHHF L-BnX^,[a^:du.d[" %cRoz!40Z:H$@$@$@{MP"ƷƐ!d&8V?̔LIQ=V+xIAn @AiY;}t}=!ލ;֋8ShrGD$@$@$@@GF|AxsnhpS-MO[o}fGʅ8W] @$k[2R._Jj0=E !H?7+{g) @$Wfb! xZ /*xq J3孥{j'\HHH*"WHg\.,3 ‘Gvdс/];rWy$@$@$@@|:kFMR2>>,d'p9ט3gzXÊE9KfHVrL$@$@$@5@,`j .W ?0Z>[/ȉv]l<5b1,vaSIHHA̎sgs=O<Z5Ӣmt⪫BGi XfH29kFWX^ZKL$@$@$@$/V*~u94BέL4Da"f!=qƍyb-ŭՙWgi:it!d]L/y1ǹI$@$@$@!Pi -NtwAӦME\all3QpkW^~cLxn;^kezZ:7-ZY4Әd*"='^\Uƞ|roYd\pt?Q >TZhfXqY"bW;3ri& M))̜+ƏeēV+h|R~囦bRnY\q(ԯ%K'fjp5yn TD LcN9m={vBÆuEԄLn 77Te#Å3~¤Ia2ݺ]/zR}@ȴ{ذ' j_hM06drN@| *-] \ɉ{!7y>d(V0+_`,}-ETtB|Ҷˑ֮ۂ?X~k=z ^]Rz"br/̢ꜯ5]Hv(0,:L$@$@$@$7q`zq*)=\t0ʶe2\Ӧ 9oY!Gۥ]stFժhԨ. 5oq6?nSa $P j_uɿjUnIFFNlx'3ws8ݻ<^O) ĪaKƾkp "f,^GPD}\bEѱG,Yڎ?ϩ }a,?^!垖~ L"`bzZ'tHGuxԏJX >} 0.$eT3(w['ܝa榞<g#Ev~|KD"Q{l`D.ڒ*x"H9I+{$ru{1HUqq T}Cډ`LuT^T=:_:)3 oa 6E`B;Qs~l)n @&7T;H I^-yr=>fnqJzh۩+4l@tE-*PmE-XmLVW`$y_zC;q_#rh5-՛OzŪ4WTYEΊ1Hj#u7>_aC)i2 2i٢hظW*R:7KpHT:Bb9R+&t|HII5"4NO~Nj|GbD?OFNUn 3 /y'l]v:d MTX8oN_eFd.dB0&O&Yɇ+嬈 $XԮغLUX.֪. R:Q嫲(,u6m܀MI1bLWqq[6oB9Hde쯿@mШ6XjLEnvb) OO5I8L~8\8V.] hQ=V2uf!3e9ΝBkk<qmJu:$ 6ZƖOrc|l۶C[Ij7&~' u+N6"L_8^{ JOwy+~zsf4^$zNXl!V-_% Avh,a{"  ]Kub,wDvV_&}Qz҇0{, t4z3ifGSsY~YEŗ?+b{#F$ 6W_+ޅޏo'ɷfhm#f" D@`}s׮xq0; jc붝p/`1k3iȩgoݻ ^ICGň37z?yXՎ4|\yqh3Rv(rQwC96i9Ĝo;oN>zԭ[G^Jf?^mM|7jD|/f|3cλ1ok+JE4ߍޚ>W_}֞3.Y9.~Ϣ! em޸MA82d7VN]_bZj[2ˇښׯ]mK8MhV n=s|˖Z|BqeqQرm n{*0} E@*t;4iyj6*`F(ƍs0wBGߪyvEAnnT4IYf0~q8]Dмy]mڴ_A~1K{E?/kݎ|_&ɻ'xQkkk\|~$ʰ āZ\F=;:z=ucg`U^FȿGå;ŔySLipm:tJB&dEfV]SnޜLmrO1WXFh^'n_q\߉ VME.RrɓNJDlB99YѢR|^3$OgY&\>htO~?s[cSūiV& O=-tDA6:ٸɸ 0÷b\ ="{)dgx: ׺pƙa֬ܢ Y'N0fXǢ⨤EUÃNdb@_\$"6Q,"~|?"$u"2Ѱ XË';qvcR8';$@$@ ߻!}hש &5SBMlИ`:3%>_w'F"ԑ9 a3lE%(hEf /PQֶÏ썗?M›aO5b"8̱a1uda^QyiQSm8Qei_С$D^|ff23 ~2d 5k"0Ê*.7o-e͈vJi/HHȗLBv4#nӈ&Lj-^I_˭Z7[7au~-4eGc BÎ0t+AL߷*E9q?B>!l#OR)S^ ?VėVmԠudX|P@IDATgŲeL,0k[̰h$[Yr*_ֵ_5/'*/ T U!+{(^Zƴ:7iw!+«ե׳)--89bɇB,@G{w1-RYghСd["u<.&VgȎ|߾}r#AJZu9ę^i1 T/LF4嚣Pҵ"ZH3λҤ?͚'7k+_$PYq`2n.fY':\,kg=/4۵o!a 0yWluaD%b̔7y: TfT,]Q}nw.37^H;%5Uf>k슮Q.>UxJL mj5Zh58v#M!f8ܹ̌\p%?(ceϭg[M+W n (/,듕g) 2fƻl}cs Hoꉱ\QTh-b"Y\2бK7ԍh,mP_Β .&AZu&^sobÏ艜F!i Exp&iV"|rLlԇ$XZ?/}Î8Zgɏ9ǺTkiI]IVseMS񿀭[w`ذa+Vڈ͛_Lyj@vH-r4F4AME*M-L|)>G.W)',}%Ȫ%`rx)#cEH/!YZH'G6[^;,7\G~ غcVcL8/%hu Ki^BKMڪVTg-}Բ.麌Lj,n6C-)E}k٢Mk9-E:{k!8YƁ?L3Cxֳx≷̯}3 ,(ZjJb8O^~YDڴCT~7h23XBC,oP,QK ]D&ĉ'SK4hD[A#Yl;i\hvBwp 4:]*v@z3epwbT]o.&  C:˖,e7mD]|`+D+F% +.-"D-S]u۸ ~q}L3k;;uÌ2'pUrFy۲w_;<ĝ؞LiӬ0Ƅ'*Igsg}WƋkZ~c!{տ2q妜!o\09bw՘^' XD~-_ nƻ+0yWSv5d @!`~7wQK{g6n*K{ 7fןiv4T7=nʭ]/B۝oia(*عTx"4n*NC:Ì?͚G'M'cDm| ]5/7\L\+^3sGjjChp3v.=}וpI#p,=ۍ` q7_a>ѵ]35){kSuz7OvӤiѤ.:8X/) 'NLѨ`i|i)G~隌F;]  B@5wkcPW|>|h+/ V*1CN=XTptI <ڋuRLSb;_[&3"a7bqqS*';duBuF-fҸ2/T9v*o`na*0bOS OEbm$o}qc;q pk]MHm[7s>ƺLSXK^Øaq)#1ŒצG2iL.7k8 0k( T/]C-1;.3 -䗱jbsUhҴ, $3 MؙBl\rMZ:f9_yrM lXى\+.@{~T_ޖMܥŸ?yM3_EeȞǠqL_dɢd&hSi!m`}[x{ mebX3U$+!V*X\1 NM7܎.i2bC\eI/w5Ìz[,ಖg_x9}v\X`nnIaxWڏt#kAF4C7wI|:ٸɷ=Kf5~|Zl1Qcz\41˃rMk|Of9/[{Y$T#LNk"u2Ap/EG`ɯqGxK#?7^: YncDjG,p2tǮbiX Koi1ypț$bҳޏnD?O# 65'b=n#q#kUgwbUE&#++P譲xlqXH ϻtq|;`Z&ndМ_TSnEdP8Z^U|lD{OeC7]ePcTanjc^;:4X>&ibASIV?9{nY; CsԲÌT ^O-tEElpj;h0K:ܩIC;0  +,a+f80W; 2>˷FY2p<ʆJ* %Of`Р^F0Y?[^)S7_F6A` %HHzMS۵6ޔ`,^ݪIJFME@A*Zerfeb*'LkݾQ$o(ɏ '=]ʨhq <{_imS+蘠塥 [X zZ k`!K=G}4xm; Q7]D} aw0JaIndz?jZIn [j}=:wMs$j,<-vFo.6%.B)hj!_AKH|wA C@t$:Gbb9mN&dD<:úNvzLŐՂ4s5TxKvdT9s;Sco_KIM-<99ZVMg.ImSUЪ쾩ڰ3mץY},=V*=.&PmPKk~) qHAm1gb W:ƍDI؇tE~S͸瞋+ӄ|t3ױg3 @!`=>aaՖdďL?k_ǺY0\Ҽ, IR>5/z>_ڦD+}O:Cɛ[-UG wo 4WV[͚Ⱦ9>Bĕgy:?|L,XR:Zcg+O.W?zj\ @%"x˕ udۈ'"|BjY꾠{2GJ4@mb=C륿Kcذ5̀|fu^dc3^ lۖ_TP: 2%ɊV?%TA$PwhmL*,-À"T`CZ'"iZ #KUd'I#F0յk;̝2}sq?d$@$PmbUb~ꆉ2u&F.y]Y5(Na~KH <}3MQz-%qHHHE Xr7lf6Ф:gk[Ux fOII#   $T*}\XL(IHHHRcXFd   M(jӫɾ `5eb#IHHj j/$@$@$@5XxH   D6 @ @V#^&6HHH6HZsujZǹ0^0HHHOE5 NaDju܊S[H%xT   HbjZ&yyPT,=l۶ذai%.$@$@$@5@g*_=+ CW7nN<nBxiZf^+d Sfv&IHHH`F#aYۋ37n>;n̛<EpEcӦEF`#'L-f2j)W#Đt%h>WS >|<4"N*y ,`Ÿ暧6S&)¢D$@$@$P`qFT!5`@4 W#!?H,^|r#ζm+u3,x;Eqqy*p/*#C[lr#.B6 ^$3#b^9:t(/,, ƍ; +*^z_BUc5x 8昣DeN*"dHHHH@ 0N!ԩg˖Gzʒ!I S!_.Mş7ΐT0k,sQ͓e2D$@$@$@H}Uc)i7H V%-  C@ ߍ( !KYi\}Z~j9^,6/1;H$@jvIwF->kg8<ہbTߡOH (Iu @5+TF1ZJ[:G.oIPUY^HA!Q]:!DZL(C=K&R`eH@n7X/e{ ~ `5eb#L^ T]Zr (jU@PG9E6,(-$/,DpWV#' 93U&IR 0]N"֋LF:/t lp]'N? tM i+! @SH:)fX>~*}trDGN3%C: =6!n;Ŷ3 $+"X <-Ła}z"%XhOf`Р^HKK6"L Ngv`Y1?\fmȃW}[x[>B1xWSz7IԺHHJ أ*i1la I6zN v#I#,Ukѹh|F`R~%/ ~ Gؼ}K +5NFP}yD$@$@$PubGZJpabInAW$`Œ?aҤ0rw\wtv!.|rr˻%dDԆyfX{Tr:%_՘HOwK 1HH@@!2Z`Z]4iVcΜ>0+כZ`m"+(Ï:rڵm.{\DerP0|>ucGA-*$1jh_j$+% V!TUDdSW}e,ݶƖF`͢ E%"B!zf_O~-/mHHH@r0#tc wo [ffq1DT&ns]4Y8#p]8vvY?D7j\ѶaA1!H_z8Tp4y^-! 8X!m6}13 {o:Ǝu'_1tA= K_/d"%ON[±$ٶdZ-(ė22\8T3ˬ2em)"4HH@03 kv;e{+d8hD-a*hժ1R5DfeX3%bS5oj,uHQY-EfT J$@$ Fh CvGg`r0p`?V?9иqCy4<<.*btYC?H~1~ eբujL$@$@$PKEi2QH+..b9bL}HHHH RٳTγUٳ#<> @<м  Ţ$@$@$@$`k ;X,J$@$@$@ NWa2̅Ff  H  >Ԫ[Hg=YմN-USJ$@$@ CHhkTxYJXL ǸO$@$phI^`֢bܵ=.z}kXAy~whKWe#E}d" 8$ أ{g&v[YgUԕ|LG^ph#b vٛ2Ej! USGYU9N}f" 8 #&yaC'z_d9k=9b+/oK,' LNVV<=SVl*GQI}R|b'HHɭcMA^CM i\{gUq"KDc bTOH ?F#J4kTl4)YD~g+ve8}̽3~g9sF Ȅ!ΈCӔyu-αzhtS-q<.m~ &O(<ӝV#k5ީ.-K/F$~ r!৖*-*DBc-NQq{%~@yn:2$1S {cԊ LÆa(1ȒsyoO?]*_>J,,ii"UV^'+7ҬdժҩS;Tb4А`)KP)TR U^ĘM"A|A;ELA;boM.0nC|bK[Tc.+}L59/ e+ 2IOEW }zŶD>EkSyjSR+6y_|-z|uVˆ 9ҡC"+W;$͛7qp@sSlE1 aX%/PJJJ͖eeeT/\hvK Я;q+:&/^7KW V6OM>U_VI*q0_VC[frc{@U'_T>If6۵ `x~0jjpQxtuٳ'Dڴ䋠5iҠmXGldd! bvII̞Pnm,.S vXH;/r`^ZZ&W/Gwl()кU]az@- }PyB8 ݿ=m9;Ipj /PG߯P8/-/-Ūw3b$̝Nt;]wx0!ѨjL04_. %NO|;̘ΊD3"1T÷cҀv#2a?#ݴ?-2!8qDP M[MhJnk;>zSS{m={{8+vKK+ꏢs{;D/^wx0<(T9̞3s{a8r9f;kqrQmg?qjM}Uff&F$_錯<ؿ'3\R2$3+lSϾJTTć2׭zZ=*{P O"">:!H6~y"}PS#@2')09;,W_Owh gOؠ sXF`mH3CnʥZyW>s}F前;[y{چTF+֦Ry*#PL;B ` '9mJ^#'o(Mac|\?̳ O`fdg5|LC r7@߯]WTR|>VS=MHBQ :|,UbF!%S;wLl-/1ҬՃiS>,CE`^6~峯<#(>#P}ŧzsp쁳wZ`uMRfV&u29 `ZD$p<,F}~rAk/'fcGOei-wV'mm e[ #l a5/8L*c8,Lj,Y e?1+N\a+_.#eYGgn'Oˮ !|P<[V:r-EyQ.j!l(͟KPmX|MϱaGF Ԗ#reDb|Hzd ـldJpǎ,ѿU@=m^>Ѣ'VrJvo~3,vqo*+ ٵ=%%rːf&8/;_&mB=$\eKL=KmEVc1ay Mk+0}dK,1eKu(O:ڠpspCK->ZTcEձ,c!HWk6/ݦ~Wyb_X977OJ_:+ CgUYجÀm)sN֭bg`srQ'UO6߀&U^,Բ"hWD>4*eeK>bSTT(C It1w$̺)n_g ׁ>vb]ǒgYܳwRwoo:ФgĔ:bٴi;LdžЖQ1HU[MGoQ!Rj H>ieH#[ڡX.vld$#Ēª)~ձz4qnVE[]:/Hж5Nonp4*v = !6c,)#`]b{GVdwu dCw?>@b͛,9ģ`!!?ˌQǎ-0962C~/=nqk;RNM7 _Yk(++C`hmw,seռ<!켼\Q~$eH6ͤn,O, iFFU"`90 r^.qPqd2s?:e7&Əh V;y6L,Xqj'ŧr[=rV+U&(b*:)2;cƜdefXk_mm9ҥ H#1q'S>Hf;bٳ2`2_-Z#wq1:tX LEC8'1[ Fo"|Ҋ[cR,|-%F=#\r\|(+_1ٽk*:bwf(`Tڵk!:n˦.;6\s5w%ӆmw:*=ܶr2grX%Sgv ò,\|*%LrZ/YP6q9yfdpXu;Zb_M7ڤ"?+3gjP",jꊀ"PC󯰶_Y-ZJ{ ֭doeƸs$HVX-klBXO#Lj{m$(_J/_'udizdx(In.b.XS2gU,]^7A *ҏއJKSSIB3K5jĴat1*|yp)1:8qR >ⓚ("@JEq}&,13K3 $\s͟ߟ,`&e{S9alZ$ȋ%)/K\eO:wiXip>ߖ дA輼/99M\ANnC;y1*h$צMS6.y͏=-]154q3g~iҿq\Eb;yvk"8mxnyӀyv.^|U~QQQ"#GR~1^ܿؗEV(@\P5E@/XbD2`UGvʕc_֭efk I]w=!g})mˢE@*IݺT ZBb(Y6^c;QԬ! l[iɹ$4g[r Pe%/նQĉ;v1Җ$Sk%f(Gp/"I2iH\eB3f}nzCn6m('/w̞=$dy=쩶F0!3TIgS 4L{f Ta>ml0h R[L=M`_l4\rsW" %`qRQ" K- ȅ I?rZƀ>/Dj_!=4$L]i-]cÉpL< SO@z`xK 9J9g)A:݌֮o@̄Q sQf]n4LAٴ;gP0&+WĄk(Qt,s Qh<}/6q}2d@V߀Ƭ5Ȝ9K su*nn4vfΒB?"G\$5)E@PgDw'x,>MF|bhu.SOaxWhhΐIrCb9؊1mfdKep f̓k3 f&&w_Մ#P\^l0o孷ʳϾcrkR&ҥ+\ߘ#vmجq=YkIDATI!/|A"kfKl %vPUvLM%WXR~Fi$ܞ#ASd##$<$_K0*l8#阑JG~ڶmfiuHKE>݊xê#1ć,4 lz^O;5;Y8yL%R% ݥeˆ-f<,Ļ$[ЌN@Ù(qE@ &(A 6O8MIn8w'o8GmML14@<}1n!R!k|wQ6:X%Ys69uhn1RZ|-n b4c mh :Cڵk#j80c~e\!Kk S{GJY3kGG_d,RaaŊu0~dW cy88^wEt&=zt~9#pT(@P XܠԄE xmڸNLr$@; 5PKH;.-x6Z#'ʻ-V+6K:,K5;u$ۊ-|Nt,>z Iie8 ܸaĿxv)C Oa3a~-@~dnI8vl_glqbIㆢk׶psLN{u Ku Gp2\gXå>SqQ57" OϯM["(kϞ\ɢfMHϓz4[6 p[mUzW4<9ˍTvv=4Pn ! Hfz׹÷sn٢Z1jHZ\^ܧL5Pe|UXn{ )Elw ekܖ8 9Cw΅ gˬ.Wwl'd@KV/6+2"XV%W}DmBl ڀ#z"%[?}MB˲* */BDUY0HQ S Pk4hư@GB[&0|ܠ9k ;I |u@L.'.<||AP⅀A IMGP8#4Ynx5\V?2qC6Y)"("@i=PE@PEglhi 5}CSE@PECj^EܡZkb5@GPE@P|Gq2"/2=RE@PE@?.kde/\E@PE@P5?P~E@PE@Pj+"("p(;P~E@PE@Pj+"("p^yh%IENDB`nim-d3-0.1.3/src/000077500000000000000000000000001365037635100134025ustar00rootroot00000000000000nim-d3-0.1.3/src/d3.nim000066400000000000000000000007431365037635100144210ustar00rootroot00000000000000## D3js Foreign Function Interace (FFI) ## ## .. code::nim ## import d3 ## ## d3.select("body").selectAll .... ## ## select("body").selectAll .... ## ## The `d3` prefix is optional and really just represents the module ## name in Nim. import d3 / [types, transforms, d3_axis, d3_format, d3_scale, d3_scale_chromatic, d3_selection, d3_time_format] export types, transforms, d3_axis, d3_format, d3_scale, d3_scale_chromatic, d3_selection, d3_time_format nim-d3-0.1.3/src/d3/000077500000000000000000000000001365037635100137105ustar00rootroot00000000000000nim-d3-0.1.3/src/d3/d3_axis.nim000066400000000000000000000024201365037635100157450ustar00rootroot00000000000000## See ## ## * https://github.com/d3/d3-axis ## import sequtils import ./types proc axisTop*(scale: D3AnyScale): D3Axis {.importc: "d3.axisTop".} ## \ ## Horizontal axis with tick marks on the top proc axisRight*(scale: D3AnyScale): D3Axis {.importc: "d3.axisRight".} ## \ ## Vertical axis with tick marks on the right proc axisBottom*(scale: D3AnyScale): D3Axis {.importc: "d3.axisBottom".} ## \ ## Horizontal axis with tick marks on the bottom proc axisLeft*(scale: D3AnyScale): D3Axis {.importc: "d3.axisLeft".} ## \ ## Vertical axis with tick marks on the left proc scale*[T](axis: D3Axis): T {.importcpp.} ## \ ## Return the current scale object proc scale*(axis: D3Axis, scaleObj: D3AnyScale) {.importcpp.} ## \ ## Set the axis scale proc ticks*(axis: D3Axis, count: int): D3Axis {.importcpp.} ## \ ## Set number of ticks proc ticks*(axis: D3Axis, count: int, specifier: cstring): D3Axis {.importcpp.} ## \ ## Set number of ticks and format specifier proc tickFormat*(axis: D3Axis, format: D3Format): D3Axis {.importcpp.} ## \ ## Set tick label formatter ## ## .. code::javascript ## // equivalent javascript ## axis.tickFormat(d3.format(",.0f")); ## ## .. code::nim ## # in nim ## let axis = axisLeft(yScale).tickFormat(d3.format("~s")) nim-d3-0.1.3/src/d3/d3_format.nim000066400000000000000000000012451365037635100162750ustar00rootroot00000000000000## See ## ## * https://github.com/d3/d3-format ## import sequtils import ./types proc format*(specifier: cstring): D3Format {.importc: "d3.format".} ## \ ## Create a format ## ## .. code::javascript ## // in javascript ## d3.format(",.0f") ## d3.format("s")(1500); // "1.50000k" ## d3.format("~s")(1500); // "1.5k" ## ## .. code::nim ## // in nim ## d3.format("~s").exec(1500) # "1.5k" proc formatPrefix*(specifier: cstring, value: int | float): D3Format {.importc: "d3.formatPrefix".} proc exec*(format: D3Format, value: int | float): cstring {.importcpp: "function(f, v){ return f(v) }"} ## \ ## Run the format on the value nim-d3-0.1.3/src/d3/d3_scale.nim000066400000000000000000000170611365037635100160770ustar00rootroot00000000000000## See ## ## * https://github.com/d3/d3-scale ## import sequtils import ./types proc exec*(scale: D3AnyScale, v: int): int {.importc: "function(scale, v){ return scale(v) }".} ## \ ## Given a value in the domain, map to a value in the range ## ## .. code::javascript ## // in javascript ## var x = d3.scaleLinear() ## .domain([10, 130]) ## .range([0, 960]); ## ## x(130); // 960 ## ## .. code::nim ## # in nim ## let x = d3.scaleLinear() ## .continuousDomain(@[10, 130]) ## .continuousRange(@[0, 960]) ## ## x.exec(130) # 960 proc exec*(scale: D3AnyScale, v: float): float {.importc: "function(scale, v){ return scale(v) }".} ## \ ## Given a value in the domain, map to a value in the range proc exec*[T](scale: D3AnyScale, v: T): int {.importc: "function(scale, v){ return scale(v) }".} ## \ ## Given a value in the domain, map to a value in the range proc invert*(scale: D3AnyScale, v: int): int {.importc: "function(scale, v){ return scale.invert(v) }".} ## \ ## Given a value in the range, map to a value in the domain ## ## .. code::javascript ## // in javascript ## var x = d3.scaleLinear() ## .domain([10, 130]) ## .range([0, 960]); ## ## x.invert(960); // 130 ## ## .. code::nim ## # in nim ## let x = d3.scaleLinear() ## .continuousDomain(@[10, 130]) ## .continuousRange(@[0, 960]) ## ## x.invert(960) # 130 proc invert*(scale: D3AnyScale, v: float): float {.importc: "function(scale, v){ return scale.invert(v) }".} ## \ ## Given a value in the range, map to a value in the domain proc invert*[T, U](scale: D3AnyScale, v: T): U {.importc: "function(scale, v){ return scale.invert(v) }".} ## \ ## Given a value in the range, map to a value in the domain proc scaleLinear*(): D3ContinuousScale {.importc: "d3.scaleLinear".} proc continuousDomain*[T](scale: D3ContinuousScale): seq[T] {.importc: "function(scale){ return scale.domain() }".} proc continuousDomain*[T](scale: D3ContinuousScale; minVal, maxVal: T): D3ContinuousScale {.importc: "function(scale, minVal, maxVal){ return scale.domain([minVal, maxVal]) }".} # proc continuousDomain*(scale: D3ContinuousScale; minVal, maxVal: int): D3ContinuousScale {.importc: "function(scale, minVal, maxVal){ return scale.domain([minVal, maxVal]) }".} proc continuousDomain*(scale: D3ContinuousScale; extents: seq[int]): D3ContinuousScale {.importc: "function(scale, extents){ return scale.domain(extents) }".} proc continuousRange*[T](scale: D3ContinuousScale): seq[T] {.importc: "function(scale){ return scale.range() }".} proc continuousRange*(scale: D3ContinuousScale; minVal, maxVal: int): D3ContinuousScale {.importc: "function(scale, minVal, maxVal){ return scale.range([minVal, maxVal]) }".} proc continuousRange*(scale: D3ContinuousScale; extents: seq[int]): D3ContinuousScale {.importc: "function(scale, extents){ return scale.range(extents) }".} proc continuousRange*(scale: D3ContinuousScale; minVal, maxVal: cstring): D3ContinuousScale {.importc: "function(scale, minVal, maxVal){ return scale.range([minVal, maxVal]) }".} proc continuousRange*(scale: D3ContinuousScale; extents: seq[cstring]): D3ContinuousScale {.importc: "function(scale, extents){ return scale.range(extents) }".} proc continuousRange*(scale: D3ContinuousScale; extents: seq[string]): D3ContinuousScale = continuousRange(scale, map(extents, proc(x: string): cstring = cstring(x))) # This causes string ranges to not work as expected # proc continuousRange*[T](scale: D3ContinuousScale; minVal, maxVal: T): D3ContinuousScale {.importc: "function(scale, minVal, maxVal){ return scale.range([minVal, maxVal]) }".} proc scaleOrdinal*(): D3OrdinalScale {.importc: "d3.scaleOrdinal".} proc scaleOrdinal*(rng: cstring): D3OrdinalScale {.importc: "d3.scaleOrdinal".} proc scaleOrdinal*(rng: seq[cstring]): D3OrdinalScale {.importc: "d3.scaleOrdinal".} proc ordinalDomain*[T](scale: D3OrdinalScale): seq[T] {.importc: "function(scale){ return scale.domain() }".} proc ordinalDomain*[T](scale: D3OrdinalScale; minVal, maxVal: T): D3OrdinalScale {.importc: "function(scale, minVal, maxVal){ return scale.domain([minVal, maxVal]) }".} proc ordinalDomain*(scale: D3OrdinalScale; extents: seq[int]): D3OrdinalScale {.importc: "function(scale, extents){ return scale.domain(extents) }".} proc ordinalRange*[T](scale: D3OrdinalScale): seq[T] {.importc: "function(scale){ return scale.range() }".} proc ordinalRange*(scale: D3OrdinalScale; extents: cstring): D3OrdinalScale {.importc: "function(scale, extents){ return scale.range(extents) }".} proc ordinalRange*(scale: D3OrdinalScale; extents: seq[int]): D3OrdinalScale {.importc: "function(scale, extents){ return scale.range(extents) }".} proc ordinalRange*(scale: D3OrdinalScale; minVal, maxVal: cstring): D3OrdinalScale {.importc: "function(scale, minVal, maxVal){ return scale.range([minVal, maxVal]) }".} proc ordinalRange*(scale: D3OrdinalScale; extents: seq[cstring]): D3OrdinalScale {.importc: "function(scale, extents){ return scale.range(extents) }".} proc ordinalRange*(scale: D3OrdinalScale, extents: seq[string]): D3OrdinalScale = ordinalRange(scale, map(extents, proc(x: string): cstring = cstring(x))) proc scaleBand*(): D3BandScale {.importc: "d3.scaleBand".} proc scaleBand*(rng: seq[int]): D3BandScale {.importc: "function(rng){ d3.scaleBand(rng) }".} proc scaleBand*(dom, rng: seq[int]): D3BandScale {.importc: "function(dom, rng){ d3.scaleBand(dom, rng) }".} proc bandDomain*(scale: D3BandScale): seq[cstring] {.importc: "function(scale){ return scale.domain() }".} proc bandDomain*(scale: D3BandScale, extents: seq[cstring]): D3BandScale {.importc: "function(scale, extents){ return scale.domain(extents) }".} proc bandRange*(scale: D3BandScale; minR, maxR: int | float): D3BandScale {.importc: "function(scale, minv, maxv){ return scale.range([minv, maxv]) }".} proc bandRangeRound*(scale: D3BandScale; minR, maxR: int | float): D3BandScale {.importc: "function(scale, minv, maxv){ return scale.rangeRound([minv, maxv]) }".} ## \ ## Convenience function equivalent to setting bandRange and calling ## scale.round(true) proc round*(scale: D3BandScale): bool {.importcpp.} ## \ ## Get rounding setting proc round*(scale: D3BandScale, val: bool): D3BandScale {.importcpp.} ## \ ## Set rounding proc paddingInner*(scale: D3BandScale): float {.importcpp.} ## \ ## Get inner padding setting proc paddingInner*(scale: D3BandScale, pad: int | float): D3BandScale {.importcpp.} ## \ ## Set inner padding proc paddingOuter*(scale: D3BandScale): float {.importcpp.} ## \ ## Get outer padding setting proc paddingOuter*(scale: D3BandScale, pad: int | float): D3BandScale {.importcpp.} ## \ ## Set outer padding proc padding*(scale: D3BandScale): int {.importcpp.} ## \ ## Get inner padding setting proc padding*(scale: D3BandScale, pad: int): D3BandScale {.importcpp.} ## \ ## Set both inner and outer padding proc align*(scale: D3BandScale): float {.importcpp.} ## \ ## Get the alignment proc align*(scale: D3BandScale, val: float): D3BandScale {.importcpp.} ## \ ## Set the alignment proc bandwidth*(scale: D3BandScale): int {.importcpp.} ## \ ## Return the width of each band proc step*(scale: D3BandScale): int {.importcpp.} ## \ ## Distance between the starts of adjacent bands proc copy*(scale: D3BandScale): D3BandScale {.importcpp.} ## \ ## Returns an exact copy of this scale. Changes will not affect the other. nim-d3-0.1.3/src/d3/d3_scale_chromatic.nim000066400000000000000000000075521365037635100201340ustar00rootroot00000000000000## Color Schemes ## ## See for more info ## ## * https://github.com/d3/d3-scale-chromatic ## * https://github.com/d3/d3-scale-chromatic#categorical ## * https://github.com/d3/d3-scale-chromatic#diverging ## * https://github.com/d3/d3-scale-chromatic#sequential-single-hue ## * https://github.com/d3/d3-scale-chromatic#sequential-multi-hue # Categorical # https://github.com/d3/d3-scale-chromatic#categorical proc schemeCategory10*(): cstring {.importc: "function(){ return d3.schemeCategory10 }".} proc schemeAccent*(): cstring {.importc: "function(){ return d3.schemeAccent }".} proc schemeDark2*(): cstring {.importc: "function(){ return d3.schemeDark2 }".} proc schemePaired*(): cstring {.importc: "function(){ return d3.schemePaired }".} proc schemePastel1*(): cstring {.importc: "function(){ return d3.schemePastel1 }".} proc schemePastel2*(): cstring {.importc: "function(){ return d3.schemePastel2 }".} proc schemeSet1*(): cstring {.importc: "function(){ return d3.schemeSet1 }".} proc schemeSet2*(): cstring {.importc: "function(){ return d3.schemeSet2 }".} proc schemeSet3*(): cstring {.importc: "function(){ return d3.schemeSet3 }".} # Diverging # https://github.com/d3/d3-scale-chromatic#diverging # k ranges between 3 to 11 proc schemeBrBG*(k: int): cstring {.importc: "function(k){ return d3.schemeBrBG[k] }".} proc schemePRGn*(k: int): cstring {.importc: "function(k){ return d3.schemePRGn[k] }".} proc schemePiYG*(k: int): cstring {.importc: "function(k){ return d3.schemePiYG[k] }".} proc schemePuOr*(k: int): cstring {.importc: "function(k){ return d3.schemePuOr[k] }".} proc schemeRdBu*(k: int): cstring {.importc: "function(k){ return d3.schemeRdBu[k] }".} proc schemeRdGy*(k: int): cstring {.importc: "function(k){ return d3.schemeRdGy[k] }".} proc schemeRdYlBu*(k: int): cstring {.importc: "function(k){ return d3.schemeRdYlBu[k] }".} proc schemeRdYlGn*(k: int): cstring {.importc: "function(k){ return d3.schemeRdYlGn[k] }".} proc schemeSpectral*(k: int): cstring {.importc: "function(k){ return d3.schemeSpectral[k] }".} # Sequential (Single Hue) # https://github.com/d3/d3-scale-chromatic#sequential-single-hue # k - sequential color scheme of size 3 to 9 proc schemeBlues*(k: int): cstring {.importc: "function(k){ return d3.schemeBlues[k] }".} proc schemeGreens*(k: int): cstring {.importc: "function(k){ return d3.schemeGreens[k] }".} proc schemeGrey*(k: int): cstring {.importc: "function(k){ return d3.schemeGreys[k] }".} proc schemeOranges*(k: int): cstring {.importc: "function(k){ return d3.schemeOranges[k] }".} proc schemePurples*(k: int): cstring {.importc: "function(k){ return d3.schemePurples[k] }".} proc schemeReds*(k: int): cstring {.importc: "function(k){ return d3.schemeReds[k] }".} # Sequential (Multi-Hue) # https://github.com/d3/d3-scale-chromatic#sequential-multi-hue # k - sequential color scheme in range of 3 to 9 proc schemeBuGn*(k: int): cstring {.importc: "function(k){ return d3.schemeBuGn[k] }".} proc schemeBuPu*(k: int): cstring {.importc: "function(k){ return d3.schemeBuPu[k] }".} proc schemeGnBu*(k: int): cstring {.importc: "function(k){ return d3.schemeGnBu[k] }".} proc schemeOrRd*(k: int): cstring {.importc: "function(k){ return d3.schemeOrRd[k] }".} proc schemePuBuGn*(k: int): cstring {.importc: "function(k){ return d3.schemePuBuGn[k] }".} proc schemePuBu*(k: int): cstring {.importc: "function(k){ return d3.schemePuBu[k] }".} proc schemePuRd*(k: int): cstring {.importc: "function(k){ return d3.schemePuRd[k] }".} proc schemeRdPu*(k: int): cstring {.importc: "function(k){ return d3.schemeRdPu[k] }".} proc schemeYlGnBu*(k: int): cstring {.importc: "function(k){ return d3.schemeYlGnBu[k] }".} proc schemeYlGn*(k: int): cstring {.importc: "function(k){ return d3.schemeYlGn[k] }".} proc schemeYlOrBr*(k: int): cstring {.importc: "function(k){ return d3.schemeYlOrBr[k] }".} proc schemeYlOrRd*(k: int): cstring {.importc: "function(k){ return d3.schemeYlOrRd[k] }".} nim-d3-0.1.3/src/d3/d3_selection.nim000066400000000000000000000044541365037635100167770ustar00rootroot00000000000000## See ## ## * https://github.com/d3/d3-selection ## import ./types proc event*(): D3Event {.importc: "function(){ return d3.event }".} proc pageX*(ev: D3Event): float {.importcpp: "#.pageX".} proc pageY*(ev: D3Event): float {.importcpp: "#.pageY".} proc select*(tag: cstring): D3Selection {.importc: "d3.select".} proc select*(parent: D3Selection, tag: cstring): D3Selection {.importcpp.} proc selectAll*(tag: cstring): D3Selection {.importc: "d3.selectAll".} proc selectAll*(parent: D3Selection, tag: cstring): D3Selection {.importcpp.} proc data*[T](sel: D3Selection, vals: seq[T]): D3Selection {.importcpp.} proc html*(node: D3Selection, sValue: cstring): D3Selection {.importcpp.} proc style*(sel: D3Selection, sName, sValue: cstring): D3Selection {.importcpp.} proc style*(sel: D3Selection, sName: cstring, sValue: int): D3Selection {.importcpp.} proc style*(sel: D3Selection, sName: cstring, sValue: float): D3Selection {.importcpp.} proc enter*(sel: D3Selection): D3Selection {.importcpp.} proc exit*(sel: D3Selection): D3Selection {.importcpp.} proc append*(sel: D3Selection, tag: cstring): D3Selection {.importcpp.} proc remove*(sel: D3Selection): D3Selection {.importcpp.} proc join*(sel: D3Selection, tag: cstring): D3Selection {.importcpp.} ## \ ## Merge enter and update ## .. code::javascript ## // in javascript ## svg.selectAll("circle") ## .data(data) ## .join("circle") ## .attr("fill", "none") ## .attr("stroke", "black"); proc attr*(sel: D3Selection, name: cstring, val: cstring): D3Selection {.importcpp.} proc attr*(sel: D3Selection, name: cstring, val: int): D3Selection {.importcpp.} proc attr*(sel: D3Selection, name: cstring, val: float): D3Selection {.importcpp.} proc attr*[T, U](sel: D3Selection, name: cstring, fn: proc(d: T): U): D3Selection {.importcpp.} proc text*(sel: D3Selection): cstring {.importcpp.} proc text*(sel: D3Selection, val: cstring): D3Selection {.importcpp.} proc text*(sel: D3Selection, fn: proc(x: int): cstring): D3Selection {.importcpp.} proc call*(sel: D3Selection, axis: D3Axis): D3Selection {.importcpp.} proc on*[T](sel: D3Selection, name: cstring, fn: proc(x: T)): D3Selection {.importcpp.} proc transition*(sel: D3Selection): D3Selection {.importcpp.} proc duration*(sel: D3Selection, milliseconds: int): D3Selection {.importcpp.} nim-d3-0.1.3/src/d3/d3_time_format.nim000066400000000000000000000043261365037635100173160ustar00rootroot00000000000000## See ## ## * https://github.com/d3/d3-time-format ## import sequtils import ./types proc timeFormat*(specifier: cstring): D3TimeFormat {.importc: "d3.timeFormat".} ## \ ## Create a time format proc timeParse*(specifier: cstring): D3TimeFormat {.importc: "d3.timeParse".} ## \ ## Create a time parsing format proc utcFormat*(specifier: cstring): D3TimeFormat {.importc: "d3.utcFormat".} proc utcParse*(specifier: cstring): D3TimeFormat {.importc: "d3.utcParse".} proc isoFormat*(): D3TimeFormat {.importc: "function(){ return d3.isoFormat }."} proc isoParse*(): D3TimeFormat {.importc: "function(){ return d3.isoParse }".} proc execFormat*(formatter: D3Format, date: Date): cstring {.importcpp: "function(formatter, date){ return formatter(date) }".} proc execParse*(parser: D3Format, s: cstring): Date {.importcpp: "function(parser, s){ return parser(s) }".} proc newDate*(): Date {.importc: "function(){ return new Date() }."} ## \ ## Create a new Date object set to the current date and time. ## ## .. code::javascript ## // equivalent in javascript ## new Date(); // 2019-08-02T22:37:54+00:00 proc newDate*(msUnixTimestamp: int): Date {.importc: "function(msUnixTimestamp){ return new Date(msUnixTimestamp) }."} ## \ ## Create a new Date object set to the number of milliseconds since ## Jan 1, 1970 00:00:00 UTC. ## ## .. code::javascript ## // equivalent in javascript ## new Date(1564785474); // 2019-08-02T22:37:54+00:00 proc newDate*(dateString: cstring): Date {.importc: "function(dateString){ return new Date(dateString) }."} ## \ ## Create a new Date object ## ## .. code::javascript ## // equivalents in javascript ## new Date('December 17, 1995 03:24:00'); ## new Date('1995-12-17T03:24:00'); proc newDate*(year: int, monthIndex: int, day: int = 1, hours: int = 0, minutes: int = 0, seconds: int = 0, milliseconds: int = 0): Date {.importc: "function(dateString){ return new Date(dateString) }."} ## \ ## Create a new Date object specifying each part of the date and time ## ## .. code::javascript ## // equivalents in javascript ## new Date(2019, 0); // Jan 1, 2019 00:00:00 ## new Date(2019, 11, 31, 23, 59, 50) // Dec 31, 2019 23:59:50 nim-d3-0.1.3/src/d3/transforms.nim000066400000000000000000000017511365037635100166170ustar00rootroot00000000000000## Transformation functions ## ## SVG origin is defined to be the top-left corner. Moving right is ## the positive x axis and moving down is the positive y axis. ## ## These routines are utility routines to help construct the value on ## a transform attribute. ## ## .. code::nim ## .attr("transform", translate(-5, 30)) ## import strformat proc translate*[T](x, y: T): cstring = ## Linear move of x and y offset cstring(&"translate({x},{y})") proc scaleAndTranslate*[T](sx, sy, tx, ty: T): cstring = ## Scale by sx and sy and then linear move tx and ty cstring(&"""scale({sx},{sy}) translate({tx},{ty})""") proc translateAndScale*[T](tx, ty, sx, sy: T): cstring = ## Linear move tx and ty and then scale by sx and sy cstring(&"""translate({tx},{ty}) scale({sx},{sy})""") proc translateAndRotate*[T](tx, ty, deg: T): cstring = ## Linear move tx and ty and then rotate by deg degrees cstring(&"""translate({tx},{ty}) rotate({deg})""") nim-d3-0.1.3/src/d3/types.nim000066400000000000000000000012611365037635100155610ustar00rootroot00000000000000type D3Event* = ref object D3Selection* = ref object D3ContinuousScale* = ref object ## \ ## Continuous scales include linear, power, log, identity, time D3OrdinalScale* = ref object ## \ ## Ordinal scales include band, point D3BandScale* = ref object ## \ ## Similar to ordinal scale except output range is continuous and numeric D3AnyScale* = D3ContinuousScale or D3OrdinalScale or D3BandScale D3Axis* = ref object ## \ ## Representing a coordinate axis D3Format* = ref object ## \ ## Representing a number format D3TimeFormat* = ref object ## \ ## Representing a time format Date* = ref object ## \ ## Represents a javascript Date nim-d3-0.1.3/tests/000077500000000000000000000000001365037635100137555ustar00rootroot00000000000000nim-d3-0.1.3/tests/config.nims000066400000000000000000000000441365037635100161100ustar00rootroot00000000000000switch("path", "$projectDir/../src")nim-d3-0.1.3/tests/test1.nim000066400000000000000000000005671365037635100155320ustar00rootroot00000000000000# This is just an example to get you started. You may wish to put all of your # tests into a single file, or separate them into multiple `test1`, `test2` # etc. files (better names are recommended, just make sure the name starts with # the letter 't'). # # To run these tests, simply execute `nimble test`. import unittest import d3 test "can add": check add(5, 5) == 10