if (typeof require === "function") { var d3 = require('d3.v4.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3c = global.d3c || {}))); }(this, function (exports) { 'use strict'; /* ------------- */ /* quad */ /* ------------- */ function quad (form = {type: 'rect', x0: 0, y0: 0, x1: 100, y1: 100}) { if (form.type !== 'rect') { return } let x0 = form.x0 let y0 = form.y0 let x1 = form.x1 let y1 = form.y1 let extent = [ [x1 - 1 , y0 - 1], [x1 + 1 , y1 + 1] ] let bestcandie = function () {} let candidates = 10 var quad = d3.quadtree() // quad .extent(extent) .x(function(d) {return d[0]}) .y(function(d) {return d[1]}) quad.diagonal = function(d, p) { // error: d is undefined var v = p || 1 var s = d.source var t = d.target var rd = 1 + d3.randomNormal(0, v)() // v var r = "M" + s.x + "," + s.y + "C" + (s.x + rd * ((t.x - s.x))) + "," + s.y + " " + (s.x + rd * ((t.x - s.x))) + "," + t.y + " " + t.x + "," + t.y; return r } quad.findmany = function(x, y, r=Infinity, thesemany = 1) { var ret = [] let quadCopy = quad.copy() let limit = Math.min(thesemany, quadCopy.data().length) for (let i = 0; i < limit; i++) { let p = quadCopy.find(x, y, r) quadCopy.remove(p) ret.push(p) } return ret } quad.candysearch = function(candidates, r=Infinity) { let fp = [x9 + Math.random() * (x1 - x0), Math.random() * (y1 - y0), 0] // first point quad.add(fp) // let bestcandy = candysearch({ ctr: [0, 0], rds: [width, height - 110], }) let bestCandidate, bestDistance = 0 // ?? let x, y = 0; // x,y is bestCandidate let z2 = 0; // z2 is bestDistance for (let i = 0; i < candidates; ++i) { let c = [x0 + Math.random() * (x1 - x0), y0 + Math.random() * (y1 - y0)] // c random candidate i let p = quad.find(c[0], c[1], 1000) // p point closest to candidate let dx = p ? c[0] - p[0] : r // x delta if exist tbc let dy = p ? c[1] - p[1] : r // y delta if exist let d2 = dx * dx + dy * dy // distance from candidate to closest if (d2 > z2) x = c[0], y = c[1], z2 = d2 // further is better } quad.add(p = [x, y]) // add selected point return p // return selected point } quad.bestcandie = function(_) { return arguments.length ? (bestcandie = typeof _ === "function" ? _ : constant(!!_), quad) : bestcandie; } quad.candidates = function (_) { // kandidates return (arguments.length) ? (candidates = _ ,quad) : candidates } quad.rds = function (_) { // radius return (arguments.length) ? (rds = _ ,quad) : rds } return quad } /* ------------- */ /* layer */ /* ------------- */ function layer(payload) { if (payload == null ) { d3.selectAll("svg").data([]).exit().remove() var svgLayer = d3.select('body').append("svg") .attr("class", "svg") .attr("id", "svg") .attr("width", function() {return (typeof width !== 'undefined') ? width : 600}) .attr("height", function() {return (typeof height !== 'undefined') ? height : 400}) .style("border", "1px solid lightgray") return svgLayer } else { var dfn = function(d, i) { return (d.id !== undefined) ? d.id : i } var parent = payload.parent || 'svg' var cls = payload.cls || item var item = payload.item var subclass = payload.subclass || item var itemParts = item.split('.') if (itemParts.length > 1) { item = itemParts[0] subclass = itemParts[1] } var idfn = payload.idfn || dfn var data = payload.data || [] var layerMark = d3.select(parent).selectAll('.' + cls).data([cls]) var layerEnter = layerMark.enter().append("g").attr("class", cls) var elemsUpdate = d3.select("." + cls).selectAll(item + '.' + subclass).data(data, idfn) var elemsEnter = elemsUpdate.enter().append(item).attr("class", subclass) var elemsMerge = elemsEnter.merge(elemsUpdate) var elemsExit = elemsUpdate.exit() //.remove() return {e: elemsEnter , u: elemsUpdate, x: elemsExit, m: elemsMerge} } } function itemsControls(payload) { var jsonCircles = [ { "x": 30, "y": 30, "rad": 20, "c" : "Orange" }, { "x": 70, "y": 70, "rad": 20, "c" : "Peru"}, { "x": 110, "y": 100, "rad": 20, "c" : "Sienna"}] var items = { circles: d3c.layer({cls: 'circles', item:'circle', data: jsonCircles}), texts: d3c.layer({cls: 'texts', item:'text', data: jsonCircles}) } items.circles.e .attr("cx", function (d) { return d.x }) .attr("cy", function (d) { return d.y }) .attr("r", function (d) { return d.rad }) .style("fill", function(d) { return d.c }) .attr("transform", function(d, i) { // d.ddx = (d.ddx || 0) + i * 50 // d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) items.texts.e .attr("x", function (d) { return d.x }) .attr("y", function (d) { return d.y }) .text(function(d, i) { return 'circle ' + i }) .attr("transform", function(d, i) { // d.ddx = (d.ddx || 0) + i * 50 // d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) return items } function circles(payload) { var jsonCircles = [ { "x": 30, "y": 30, "rad": 20, "c" : "green" }, { "x": 70, "y": 70, "rad": 20, "c" : "purple"}, { "x": 110, "y": 100, "rad": 20, "c" : "red"}] var circles = d3c.layer({cls: 'circles', item:'circle', data: jsonCircles}).e .attr("cx", function (d) { return d.x }) .attr("cy", function (d) { return d.y }) .attr("r", function (d) { return d.rad }) .style("fill", function(d) { return d.c }) .attr("transform", function(d, i) { d.ddx = (d.ddx || 0) + i * 50 d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) var texts = d3c.layer({cls: 'texts', item:'text', data: jsonCircles}).e .attr("x", function (d) { return d.x }) .attr("y", function (d) { return d.y }) .text(function(d) { return 'hello' }) .attr("transform", function(d, i) { d.ddx = (d.ddx || 0) + i * 50 d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) } function nodes(payload) { var jsonCircles = [ { "x": 30, "y": 30, "rad": 20, "c" : "green" }, { "x": 70, "y": 70, "rad": 20, "c" : "purple"}, { "x": 110, "y": 100, "rad": 20, "c" : "red"}] var nodes = d3c.layer({cls: 'nodes', code: 'node', item:'g', data: jsonCircles}).e nodes .append("circle") .attr("class", "circleNode") .attr("cx", function (d) { return d.x }) .attr("cy", function (d) { return d.y }) .attr("r", function (d) { return d.rad }) .style("fill", function(d) { return d.c }) .attr("transform", function(d, i) { // d.ddx = (d.ddx || 0) + i * 50 // d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) nodes .append("text") .attr("class", "textNode") .attr("x", function (d) { return d.x }) .attr("y", function (d) { return d.y }) .text(function(d) { return 'hello' }) .attr("transform", function(d, i) { d.ddx = (d.ddx || 0) + i * 50 d.ddy = (d.ddy || 0) + i * 50 return "translate(" + i * 50 + "," + i * 50 + ")" }) } /* ================================= */ /* time control */ /* ================================= */ function time(payload) { var currentListeners = [] var nextListeners = currentListeners var period = payload.period || 400 // time period in milliseconds var fps = payload.fps || 60 // frames per second // ------------------------- ensureCanMutateNextListeners function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // ------------------------- timer function timer() {} // ------------------------- start timer.start = function start() { var started = false var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { // listeners[i]() } return timer } // ------------------------- subscribe timer.subscribe = function subscribe (listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) d3.timer( listener , period) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } return timer } /* ================================= */ /* movControls */ /* ================================= */ function mov (selection) { console.log(selection) function dragsubject() { return this } function dragstarted(d) { // --------------- dragstarted d3.select(this).raise().classed("active", true) d.x0 = d3.event.x // set x position at begining of drag d.y0 = d3.event.y // set x position at begining of drag } function dragged(d) { // --------------- dragged d.dx = d3.event.x - d.x0 // set delta x of current drag d.dy = d3.event.y - d.y0 // set delta y of current drag var ddx = (d.ddx || 0) + (d.dx || 0 ) // set delta x of current drag var ddy = (d.ddy || 0) + (d.dy || 0 ) // set delta y of current drag d3.select(this) .attr("transform", "translate(" + ddx + "," + ddy + ")") } function dragended(d) { // --------------- dragended d.ddx = (d.ddx || 0) + d.dx // set accumulated x drag displacment d.ddy = (d.ddy || 0) + d.dy // set accumulated y drag displacment d.dx = 0 // reset last x drag displacment d.dy = 0 // reset last y drag displacment } var drag = d3.drag() drag .on("start", dragstarted) .on("drag", dragged) .on("end", dragended) selection.call(drag) return drag } /* ================================= */ /* drag Control */ /* ================================= */ function drag (selection) { function dragsubject() { return this } function dragstarted(d) { d3.select(this).raise().classed("active", true) } function dragged(d) { d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y); } function dragended(d) { } var drag = d3.drag() drag .on("start", dragstarted) .on("drag", dragged) .on("end", dragended) selection.call(drag) return drag } /* ================================= */ /* pos Control */ /* ================================= */ function pos (selection) { // selection function prevent(e) { } function subject() { return this } function started(d) { } function moved(d) { function createPostipElem() { var padLayer = d3.select('body').selectAll('g.refs').data(['refs']).enter().insert("g", "svg").attr("class", "refs") var postipElem = d3.select("g.refs").selectAll("div.postip").data(['divMousePos']).enter().append("div").attr("class", "postip").call(drawPostipElem) } function drawPostipElem (postip) { postip .attr("viewBox", "0 0 10 10") .style("top", "-5px") .style("position", "absolute") .style("padding", "10px") .style("background", "rgba(255, 255, 255, .90)") .style("border", "1px solid lightgray") .style("pointer-events", "none") .style("z-index", "100") .style('border', '1px solid orange') .style('color', 'grey') .classed('postip-hidden', true) .style("opacity", 0) } function textPadFn (a) { var s = String("_______" + Math.floor(a.ox) + " : " + Math.floor(a.oy) + "_______") return s } // https://github.com/1wheel/swoopy-drag/blob/master/lib/d3-jetpack.js function displayTextPad(a) { d3.select('.postip') .classed('postip-hidden', false) .style('opacity', 1) .html('') .selectAll('div') .data([textPadFn]).enter() .append('div') .html(function(textPadFn) { return (textPadFn(a)) }) } function moveTextPad(node) { var postip = d3.select('div.postip') if (!postip.size()) return var e = d3.event, x = e.clientX, y = e.clientY, doctop = (window.scrollY)? window.scrollY : (document.documentElement && document.documentElement.scrollTop)? document.documentElement.scrollTop : document.body.scrollTop, n = postip.node(), nBB = n.getBoundingClientRect() postip.style('top', (y+doctop-nBB.height-18)+"px"); postip.style('left', Math.min(Math.max(0, (x-nBB.width/2)), window.innerWidth - nBB.width)+"px"); prevent(e) } var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent), ox = origin[0], oy = origin[1] var action = {ox: ox, oy: oy} createPostipElem() displayTextPad(action) moveTextPad(node) } function ended(d) { var node = d3.select(this) // selection var datum = node.datum() // datum d3.select('div.postip') .classed('postip-hidden', true) .style('opacity', 0) d3.selectAll('.postipped') .classed('postipped', false) } function d3Control(selection) { selection.on("mouseenter.pos", started) selection.on("mousemove.pos", moved) selection.on("mouseout.pos", ended) } selection.call(d3Control) return d3Control } /* ================================= */ /* tip Control */ /* ================================= */ function tip (selection) { function prevent(e) { if(e.stopPropagation) e.stopPropagation(); if(e.preventDefault) e.preventDefault(); return false; } function createTooltip() { var tooltipLayer = d3.select('body').selectAll('g.refs').data(['refs']).enter().insert("g", "svg").attr("class", "refs") var toolTipElem = d3.select("g.refs").selectAll("div.tooltip").data(['divTooltip']).enter().append("div").attr("class", "tooltip").call(drawTooltipElem) } function drawTooltipElem (tooltip) { tooltip .attr("viewBox", "0 0 10 10") .style("top", "-5px") .style("position", "absolute") .style("padding", "10px") .style("background", "rgba(255, 255, 255, .90)") .style("border", "1px solid lightgray") .style("pointer-events", "none") .style("z-index", "100") .style('border', '1px solid red') .style('color', 'grey') .classed('tooltip-hidden', true) .style("opacity", 0) } // ------------------------- diaplay action.datum // https://github.com/1wheel/swoopy-drag/blob/master/lib/d3-jetpack.js function displayTooltip(node) { var d = node.datum() if (d.hasOwnProperty('tip')) { // true // console.log('_______________ has tip') var fieldFns = d3.keys(d) // get text keys in datum .filter(function(str){ var r = (str === 'tip') return r }) .map(function(str){ return function (d) { return str + ': ' + '' + wordwrap(d[str].toString()) + '' }}) } else { var fieldFns = d3.keys(d) // get text keys in datum .filter(function(str){ var r = (typeof d[str] != 'object') && (d[str] != 'array') return r }) .map(function(str){ return function (d) { return str + ': ' + '' + textwrap(wordwrap(d[str].toString())) + '' }}) } var tooltip = d3.select('.tooltip') .classed('tooltip-hidden', false) .style('opacity', 1) .html('') .selectAll('div') .data(fieldFns).enter() .append('div') .html(function(fieldFns) { var r = fieldFns(d) return r }) } // ------------------------- moveMoveTooltip function moveMoveTooltip() { var tooltip = d3.select('.tooltip') if (!tooltip.size()) return var e = d3.event, x = e.clientX, y = e.clientY, doctop = (window.scrollY)? window.scrollY : (document.documentElement && document.documentElement.scrollTop)? document.documentElement.scrollTop : document.body.scrollTop, n = tooltip.node(), nBB = n.getBoundingClientRect() tooltip.style('top', (y+doctop-nBB.height-18)+"px"); tooltip.style('left', Math.min(Math.max(0, (x-nBB.width/2)), window.innerWidth - nBB.width)+"px"); prevent(e) } function wordwrap (line, maxCharactersPerLine) { var w = line.split(' '), lines = [], words = [], maxChars = maxCharactersPerLine || 40, l = 0; w.forEach(function(d) { if (l+d.length > maxChars) { lines.push(words.join(' ')); words.length = 0; l = 0; } l += d.length; words.push(d); }); if (words.length) { lines.push(words.join(' ')); } return lines.join('
') } function textwrap (line, maxCharactersPerLine) { var c = line.split(''), lines = [], chars = [], maxChars = maxCharactersPerLine || 40, l = 0; c.forEach(function(d) { if (l+d.length > maxChars) { lines.push(chars.join('')); chars.length = 0; l = 0; } l += d.length; chars.push(d); }); if (chars.length) { lines.push(chars.join(' ')); } return lines.join('
') } function started(d) { // if datum().tiped !== false var node = d3.select(this) // selection var datum = node.datum() // datum createTooltip(node) displayTooltip(node) moveMoveTooltip(node) } function moved(d) { var node = d3.select(this) // selection var datum = node.datum() // datum createTooltip(node) displayTooltip(node) moveMoveTooltip(node) } function ended(d) { var node = d3.select(this) // set selection var datum = node.datum() // set datum d3.select('.tooltip') // select tooltip ref .classed('tooltip-hidden', true) .style('opacity', 0) d3.selectAll('.tooltipped') .classed('tooltipped', false) } function d3Control(selection) { selection.on("mouseenter.tip", started) selection.on("mousemove.tip", moved) selection.on("mouseout.tip", ended) } selection.call(d3Control) return d3Control } exports.layer = layer exports.circles = circles exports.nodes = nodes exports.quad = quad exports.time = time exports.drag = drag exports.pos = pos exports.mov = mov exports.tip = tip }));