// Mike Bostock?s Block http://bl.ocks.org/mbostock/1021103 // Christophe Viau implemented a new shape type as a D3 Plugin based on superformulas. /* -------------------------- */ /* xsfPlugin */ /* -------------------------- */ var xsfPlugin = function xsfPlugin ( ) { var formparams = {} var fpoints = [] // _superformulaPoints(p) // sf form points var spoints = [] // _supersegmentPoints(p, fpoints) // xsf segment points var tfpoints = [] // transformPoints(p, fpoints) // xsf form tfpoints var tspoints = [] // transformPoints(p, spoints) // xsf segment points var fpath = [] // _superformulaPath(tfpoints) // xsf fpath var spath = [] // _superformulaPath(tspoints) // xsf path var fuel = null var trace = null var dots = null var fill, stroke, strokeWidth // style /* ------------------ xsfPlugin ------------------ */ var xsfPlugin = function xsfPlugin(params = {}) { let n, m formparams = Object.assign( {}, __mapper("config").form.initparams, // form init formparams, params ) // 2nd order params if (formparams.m !== undefined && formparams.m1 === undefined) formparams.m1 = formparams.m if (formparams.m !== undefined && formparams.m2 === undefined) formparams.m2 = formparams.m for (n in formparams) { formparams[n] = constant(formparams[n])} for (m in formparams) { formparams[m] = formparams[m].call(this, formparams) } var color = __mapper("config").colors.scale if (formparams.cf < 0 ) fill = "transparent" else fill = color(formparams.cf / 1000) // [0,1] if (formparams.cs < 0 ) stroke = "transparent" else stroke = color(formparams.cs / 1000) // [0,1] strokeWidth = formparams.cw || __mapper().config.form.strokeWidth || "1.5px" // preprocess let randomlimit = 40 for (n in formparams) { if (formparams[n] > 1000) { let v = (0.5 - Math.random()) * randomlimit formparams[n] = v } } console.log("_________ formparams",formparams ) fpoints = _superformulaPoints(formparams) if (formparams.id === "random") { let tries = 0 while (( !fpoints[0] || isNaN(fpoints[0][0]) || fpoints[0][0] === 0) && ++tries < 100) { formparams.m1 = (0.5 - Math.random()) * randomlimit formparams.m2 = (0.5 - Math.random()) * randomlimit formparams.n1 = (0.5 - Math.random()) * randomlimit formparams.n2 = (0.5 - Math.random()) * randomlimit formparams.n3 = (0.5 - Math.random()) * randomlimit formparams.a = (0.5 - Math.random()) * randomlimit formparams.b = (0.5 - Math.random()) * randomlimit fpoints = _superformulaPoints(formparams) } if (tries > 99) { formparams.m1 = -76 formparams.m2 = 10 formparams.n1 = 1000 formparams.n2 = 150 formparams.n3 = 350 formparams.a = 1 formparams.b = 1 fpoints = _superformulaPoints(formparams) } } spoints = _supersegmentPoints(formparams, fpoints) tfpoints = transformPoints(formparams, fpoints) tspoints = transformPoints(formparams, spoints) fpath = _superformulaPath(tfpoints) spath = _superformulaPath(tspoints) if (formparams.fuel > 0) fuel = fuelPlugin()(formparams) if (formparams.hc > 0) trace = tracesPlugin()(formparams) if (formparams.dots > 0) dots = dotsPlugin()(formparams) return xsfPlugin } xsfPlugin.render = function render() { let path = { id: formparams.id, // path id fill: fill, // fill stroke: stroke, // stroke strokeWidth: strokeWidth, // stroke-width path: spath } var formsLayerUpdate = d3.select('svg').selectAll('.' + formparams.layer).data([formparams.layer]) var formsLayerEnter = formsLayerUpdate .enter().append("g").attr("class", formparams.layer) .merge(formsLayerUpdate) var formElemsUpdate = formsLayerEnter.selectAll('.form').data([path], d => d.id) .attr("class", "form") .attr("d", d => d.path) // path .style("stroke-width", d => d.strokeWidth) .style("fill", d => d.fill) .style("stroke", d => d.stroke) var formElemsEnter = formElemsUpdate .enter().append("path") .attr("class", "form") .attr("d", d => d.path) .style("stroke-width", d => d.strokeWidth) .style("fill", d => d.fill) .style("stroke", d => d.stroke) .on("click", function(d) { if (__mapper("scales")) __mapper("scales").render(formparams) if (__mapper("consol")) __mapper("consol").render(formparams) d3.event.stopPropagation(); }) if (__mapper("scales")) __mapper("scales").render(formparams) // if (__mapper("consol")) { __mapper("consol").render(formparams) } if (formparams.dots > 0) dotsPlugin()(formparams).render() if (formparams.fuel > 0) fuel.render() } xsfPlugin.clear = function() { var formsLayerUpdate = d3.select('svg').selectAll('.' + formparams.layer).data([formparams.layer]) var formsLayerEnter = formsLayerUpdate .enter().append("g").attr("class", formparams.layer) .merge(formsLayerUpdate) var formElemsUpdate = formsLayerEnter.selectAll('.form').data([]) formElemsUpdate .exit() .remove() if (fuel) fuel.clear() if (trace) trace.clear() if (dots) dots.clear() } xsfPlugin.point = function(point) { return tfpoints[point] } xsfPlugin.area = function() { return d3.polygonArea(tfpoints) } xsfPlugin.isin = function(point) { return d3.polygonContains(tfpoints, point) } xsfPlugin.x = function(point) { return tfpoints[point][0] } xsfPlugin.y = function(point) { return tfpoints[point][1] } xsfPlugin.tfpoints = function() { return tfpoints } xsfPlugin.xtfpoints = function() { return tfpoints.map(d => d[0]) } xsfPlugin.ytfpoints = function() { return tfpoints.map(d => d[1]) } xsfPlugin.tspoints = function() { return tspoints } xsfPlugin.xtspoints = function() { return tspoints.map(d => d[0]) } xsfPlugin.ytspoints = function() { return tspoints.map(d => d[1]) } xsfPlugin.fpath = function() { return fpath } xsfPlugin.spath = function() { return spath } xsfPlugin.formparams = function() { return formparams } xsfPlugin.reset = function(params = {}) { formparams = Object.assign({}, formparams, __mapper("config").form.init, params) return xsfPlugin } return xsfPlugin } function constant(v) {return typeof v === "function" ? v : function() { return v }} function noop() {} function transformRelative(_) { if (!_) return noop; var x0 = 0, y0 = 0, kx = _.kx || 1, ky = _.ky || 1, dx = _.tx, dy = _.ty, rot = _.rot * Math.PI / 180 // degrees to rads var transformRelative = function(point, i) { if (!i) x0 = y0 = 0 var x1 = Math.round((point[0] - dx) / kx), y1 = Math.round((point[1] - dy) / ky); point[0] = x1 - x0; point[1] = y1 - y0; x0 = x1 y0 = y1 return point } return transformRelative } function transformAbsolute(_) { if (!_) return noop; var x0 = 0, y0 = 0, kx = _.kx || 1, ky = _.ky || 1, dx = _.tx, dy = _.ty, rot = _.rot * Math.PI / 180 // degrees to rads var transformAbsolute = function(p, i) { if (!i) x0 = y0 = 0; var point = [] x0 = (x0 === undefined) ? x0 : 0 y0 = (y0 === undefined) ? y0 : 0 var pointx = p[0] * Math.cos(rot) - p[1] * Math.sin(rot) var pointy = p[0] * Math.sin(rot) + p[1] * Math.cos(rot) x0 = x0 + pointx y0 = y0 + pointy point[0] = x0 * kx + dx point[1] = y0 * ky + dy return point } return transformAbsolute } function transformPoints(params, path) { var _transform = transformAbsolute(params) // var _transform = transformRelative(params) let tpath = [] for (let i = 0; i < path.length; i++) { tpath.push(_transform(path[i])) } return tpath } function _superformulaPath(pts) { return d3.line()(pts) // + "Z" // return path } function _superformulaPoints(params) { var n = params.segs var i = -1, dt = 2 * Math.PI / n, // sector per symmetry t, t1, t2, r = 0, // initialize x, y, pts = [] // points in path let v0 = params.v0 || 0 // radio faktor 0 i = -1; while (++i < n) { t1 = (params.m1) * (i * dt - Math.PI) / 4 t2 = (params.m2) * (i * dt - Math.PI) / 4 // t1 = (params.m1) * (i * dt - Math.PI) / 0.25 // t2 = (params.m2) * (i * dt - Math.PI) / 0.000000000000000000000003 t = Math.pow( Math.abs( Math.pow(Math.abs(Math.cos(t1) / params.a), params.n2) + Math.pow(Math.abs(Math.sin(t2) / params.b), params.n3)), -1 / params.n1) t = Math.max(0, t * ( 1 + v0 * i )) // _e_tbc if (t > r) r = t pts.push(t) } let rn = params.rad / r // * Math.SQRT1_2 / r var points = _superformulaCoords(params, pts, rn, dt, n) return points } function _superformulaCoords(params, pts, rn, dt, n) { let points = [] let i = -1; while (++i < n) { points[i] = [] points[i][0] = _superformulaSeries(params, pts, rn, dt, n, i, 0) points[i][1] = _superformulaSeries(params, pts, rn, dt, n, i, Math.PI / 2) } return points } function _superformulaSeries(params, pts, rn, dt, n, i, ang) { let v1 = params.v1 || 1 let ptx = pts[i] * rn * Math.cos(ang - v1 * i * dt) // angle faktor 1 let coord = Math.abs(ptx) < 1e-6 ? 0 : ptx return coord } function _supersegmentPoints(params, pts) { var pta = params.pta || 0 var ptb = params.ptb || 0 // pts.length // params.segs let r = [] if (ptb < 0) { for(let i = -1; i>=ptb; i--) { r = r.concat(pts.slice(pta, pts.length)) } } else if (pta <= ptb) { r = r.concat(pts.slice(pta, ptb)) } else if (pta > ptb) { r = r.concat(pts.slice(ptb, pts.length)) r = r.concat(pts.slice(0, pta)) } return r } ;