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
}));