Published
Edited
Jul 24, 2020
Importers
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
RI = 200
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
combinations = [[...Array(Agents.position.length).keys()], [0, 1], [0, 2], [1, 2]]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
embed = require("vega-embed@6")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
p1 = [700, 150]
Insert cell
p2 = [800, 350]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
async function draw(Agents, options, combinations) {
const svg = d3.create('svg')
.attr('width', width)
.attr('height', height)

options = options.concat(Options);
render(svg, [width, height], Agents, combinations, influenceRadius, options);
return svg.node();
}
Insert cell
function render(svg, size, Agents, combinations, influenceRadius, options) {
const nodes = getNodes(size),
dots = Agents.position.concat(nodes),
times = timeNodes(Agents, dots),
weightedDots = conflictZone(times, options.includes('relation'), Agents.relation, area);

zoneInflu(Agents, influenceRadius, 1);
const delaunay = d3.Delaunay.from(dots);
const path = d3.geoPath();
const contours1 = d3.tricontour().thresholds(300)(
weightedDots.map((d, i) => [d.pos[0], d.pos[1], d.weight]).filter(d => isFinite(d[2]))
);
/*const contours1 = d3.tricontour().contour(
weightedDots.map((d, i) => [d.pos[0], d.pos[1], d.weight]).filter(d => isFinite(d[2])),
d.treshold
);*/
const delaunayG = svg.append("g");
if (options.includes('colorMap')){
delaunayG.selectAll("path1").data(contours1)
.enter()
.append("path")
.attr('d', path)
.style('fill', d => { let c = color(d.value)
if (options.includes('brighter')) c = d3.color(c).brighter(0.6)
return c
})
/*delaunayG
.append("path")
.attr('d', path(contours1))
.style('stroke', 'blue');*/
}
if (options.includes('contours')){
let Zones = multipleZones2(combinations, times, Agents, options);
drawZone(svg, size, path, Zones, Agents, combinations, 0.5, options);
}
const agentsG = svg.append('g');
if (!options.includes('noPos')) {
agentsG.selectAll('agentPos').data(Agents.position)
.enter()
.append('circle')
.attr('class', 'agentPos')
.attr('cx', agent => agent[0])
.attr('cy', agent => agent[1])
.attr('r', agentRadius)
.style('fill', (_, i) => Agents.color ? Agents.color[i] : 'red')
.style('stroke', Agents.color ? 'red' : 'none')
agentsG.selectAll('agentPos').data(Agents.position)
.enter()
.append('text')
.attr('x', agent => agent[0])
.attr('y', agent => agent[1])
.attr('text-anchor', 'middle')
.attr('dy', 20)
.text((agent, i) => `Agent ${i}`)
.style('font-size', textSize);
}
if (!options.includes('noSpeed')) {
agentsG.selectAll('agentSpeed').data(Agents.position)
.enter()
.append('line')
.attr('x1', d => d[0])
.attr('y1', d => d[1])
.attr('x2', (d, i) => d[0] + 10*Agents.speed[i][0])
.attr('y2', (d, i) => d[1] + 10*Agents.speed[i][1])
.attr("stroke", "black")
.attr("stroke-width", 2);
}
if (options.includes('ZI')) {
const ZIG = svg.append('g');
ZIG.selectAll('ZI').data(Agents.ZI)
.enter()
.append('path')
.attr('d', (d, i) => influencePaths(d3.path(), Agents, i, d))
.style('fill', 'none')
.style('stroke-dasharray', 4)
.style('stroke-width', 2)
.style('stroke', '#757574');
}
//show nodes
if (options.includes('RI')) {
svg.selectAll("RI").data(Agents.position)
.enter()
.append("circle")
.attr('class', 'RI')
.attr('cx', agent => agent[0])
.attr('cy', agent => agent[1])
.attr('r', RI)
.style('fill', 'none')
.style('stroke-dasharray', 6)
.style('stroke-width', 2)
.style('stroke', 'black')//'#757574');
}
//show nodes
//show grid
if (options.includes('grid')) {
delaunayG.append("path")
.attr('d', delaunay.render())
.style('fill', 'none')
.style('stroke', '#7d7d7d')
}
if (options.includes('dots')) {
delaunayG.selectAll("circle").data(weightedDots)
.enter()
.append("circle")
.attr('cx', point => point.pos[0])
.attr('cy', point => point.pos[1])
.attr('r', 2)
//.style('stroke', '#afb0b3')
.style('fill', point => options.includes('weight') ? color(point.weight) : '#7d7d7d');
}
return svg.node()
}
Insert cell
legend = (selection, props) => {
const {combinations, abs, spacing, options} = props,
rectWidth = 20 + textSize;
const groups = selection.selectAll('g').data(combinations),
element = groups.enter()
.append('g')
.attr('transform', (d, i) => `translate(0, ${i * (spacing + textSize)})`);

groups.enter()
.append('text')
.attr('class', 'title')
.attr('x', 0)
.attr('dy', textSize)
.text('Relations:')
.style('font-size', textSize);
element.append('rect')
.attr('y', spacing + textSize)
.attr('width', rectWidth)
.attr('height', rectWidth/2)
.style('fill', (d, i) => !options.includes('ellipse') ? tTexture(selection, i) : tTexture(selection, i, colors[i%10]))
.style('opacity', 0.5)
.style('stroke', 'grey')
element.append('text')
.attr('x', spacing + rectWidth)
.attr('y', spacing + textSize + rectWidth/4)
.attr('dy', '0.35em')
.style('fill', (_, i) => abs.includes(i) ? 'red' : 'black')
.text(d => d.toString().replace(/,/gi, '-'))
.style('font-size', textSize)
}
Insert cell
drawZone = (selection, size, path, zones, Agents, combinations, opacity, options) => {
const zoneG = selection.append('g'),
abs = [],
texture = textures.lines().lighter().stroke('grey');
selection.call(texture);
if (!options.includes('noZones')){
zoneG.selectAll('path').data(zones)
.enter()
.append("path")
.attr('id', (d, i) => 'contour' + i)
.attr('d', (zone, i) => path(zone) ? path(zone) : abs.push(i))
.style('fill', (d, i) => !options.includes('noFillZones') ? tTexture(selection, i, /*colors[i%10]*/"rgba(235, 218, 218, 0.3)") : 'none')
//.style('fill', (d, i) => !options.includes('noFillZones') ? : 'none')
.style('opacity', opacity)
.style('stroke', 'grey')//'black')
.style('stroke-width', 2)
}
if (options.includes('ellipse')){
zones.forEach((zone, i) => {
let ellipses = findEllipse(zone);
console.log('ellipse', i, ' : ', ellipses)
const ellipseG = selection.append('g'),
Resize = 1
ellipseG.selectAll('ellipse').data(ellipses)
.enter()
.append('ellipse')
.attr('transform', ellipse => `rotate(${toDeg(ellipse.tau)} ${ellipse.cx} ${ellipse.cy})`)
.attr('cx', ellipse => ellipse.cx)
.attr('cy', ellipse => ellipse.cy)
.attr('rx', ellipse => ellipse.a*Resize)
.attr('ry', ellipse => ellipse.b*Resize)
.style('stroke-width', 2)
.style('stroke', 'black')
.style('fill', !options.includes('noFillZones') ? tTexture(selection, i, colors[i%10]) : 'none')
.style('opacity', opacity)
})
}
const baryG = selection.append('g'),
baryScale = d3.scaleSqrt().domain([0, 30000]).range([1, 10])
zones.forEach((Z, i) => {
Z.coordinates.forEach((coord, j) => {
coord.forEach(array => {
let b = barycenter(array),
area = d3.polygonArea(array),
R = baryScale(area);
if (options.includes('barycenter')){
baryG.append('path')
.attr('d', starSymbol(d3.path(), b, R))
.style('fill', colors[i%10])
.style('opacity', opacity)
.style('stroke', 'black')
.style('stroke-width', '0.5');
}
if (options.includes('link')){
combinations[i].forEach(k => {
baryG.append('line')
.attr('x1', b[0])
.attr('y1', b[1])
.attr('x2', Agents.position[k][0])
.attr('y2', Agents.position[k][1])
.style('stroke', colors[i%10])
})
}
if (options.includes('label')){
baryG.append('text')
.attr('x', b[0])
.attr('y', b[1])
.attr('text-anchor', 'middle')
.text(combinations[i].toString().replace(/,/gi, '-'))
.style('fill', colors[i%10])
.style('stroke', 'black')
.style('stroke-width', 0.5)
}
})
})
})
if (!options.includes('noLegend')){
const place = legendPlacement(Agents, size, combinations)

const legendG = selection.append('g')
.attr('transform', `translate(${place}, 0)`)
.call(legend, {
combinations,
abs,
spacing: 10,
options
})
}
}
Insert cell
legendPlacement = (Agents, size, combinations) => {
let place = 5,
b = barycenter(Agents.position);

/*if (b[0] < size[0]/2){
let margin = 100;
combinations.forEach(d => {
let m = d.length*22;
if (m > margin) margin = m;
})
place = size[0] - margin;
}*/
return place;
}
Insert cell
multipleZones2 = (combinations, times, Agents, options) => {
var Z = [];
combinations.forEach((combination, i) => {
let time = timeIdxFilter(times, combination),
weight = conflictZone(time, options.includes('relation'), Agents.relation, area),
thresh = getTreshold(weight, tau);
let A = selectAgents(Agents, combination),
t_thresh = timeAgents(A, time);
if (t_thresh < 5 && !options.includes('notThreshMin')) t_thresh = 5;
let contourZone = d3.tricontour()
.value((d, i) => {
let value = 0;
if ((!options.includes('noFilterAgents') && nodeInZone(Agents, d, options)) || (!options.includes('notThresh') && tooAway(t_thresh, time[i]))){
value = thresh.treshold - 0.1
}
else value = d[2];
return value;
})
.contour(
weight.map((d, i) => [d.pos[0], d.pos[1], d.weight]).filter(d => isFinite(d[2])),
thresh.treshold
);
if (options.includes('convexHull')) contourZone = simplifyZone(contourZone)
Z.push(contourZone);
})
return Z;
}
Insert cell
multipleZones = (combinations, times, Agents, options) => {
var Z = [];
combinations.forEach((combination, i) => {
let time = timeIdxFilter(times, combination),
weight = conflictZone(time, options.includes('relation'), Agents.relation, area),
tresh = getTreshold(weight, tau),
tresh2 = getTreshold(weight, tau/2),
filtered = filterWeight(weight, tresh.treshold);
if (!options.includes('notThresh')){
let A = selectAgents(Agents, combination),
t_thresh = timeAgents(A, time);
if (t_thresh < 5) t_thresh = 5;
filtered = weightAfterTresh(filtered, t_thresh);
}
/*if (!options.includes('noFilterAgents')){
filtered = filterNodesAgents(filtered, Agents);
}*/
let contourZone = d3.tricontour()
.value(d => !options.includes('noFilterAgents') ? nodeInZone(Agents, d, options) ? -5 : 1 : 1)
.contour(
filtered.map((d, i) => [d.pos[0], d.pos[1], d.weight]).filter(d => isFinite(d[2])),
1
);
/*let contours = d3.tricontour().thresholds([tresh.treshold, tresh2.treshold])(
filtered.map((d, i) => [d.pos[0], d.pos[1], d.weight]).filter(d => isFinite(d[2]))
);*/
//contours.forEach(contour => Z.push(contour));
Z.push(contourZone)
})
return Z;
}
Insert cell
influencePaths = (context, Agents, i, params) => {
let angleSpeed = Math.atan(Agents.speed[i][1]/Agents.speed[i][0]);

angleSpeed += Agents.speed[i][0] < 0 ? Math.PI : 0;
if (Agents.speed[i][1] == 0 && Agents.speed[i][0] == 0){
angleSpeed = 0;
}
let minus = (angleSpeed - params.angle),
plus = (angleSpeed + params.angle);
context.moveTo(Agents.position[i][0], Agents.position[i][1]);
context.arc(Agents.position[i][0], Agents.position[i][1], params.radius, minus, plus);
context.closePath();
return context;
};
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
refine = 2
Insert cell
Insert cell
nodes = getNodes([width, height])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
tooAway = (t_thresh, dot) => {
let bool = false;
for (let i = 0; i < dot.t.length; i++){
if (dot.t[i] > t_thresh) {
bool = true
break
}
}
return bool
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
simplifyZone = (Multipolygon) => {
Multipolygon.coordinates.forEach((coord, i) => {
coord.forEach((array, j) => {
let convexH = d3.polygonHull(array);
Multipolygon.coordinates[i][j] = convexH;
})
})
return Multipolygon;
}
Insert cell
Insert cell
RR = multipleZones2(combinations, times, Agents, [''])
Insert cell
RE = findEllipse(RR[1])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
textSize = 32
Insert cell
agentRadius = 6
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
rng = Math.random
Insert cell
width = 800
Insert cell
height = 500
Insert cell
d3 = require('d3', "d3-delaunay@5", "d3-tricontour@0.1", "d3-polygon");
Insert cell
math = require("mathjs")
Insert cell
textures = require('textures@1.2.0/dist/textures.js')
Insert cell
import {addlabel} from "@fil/contour-labels-svg"
Insert cell
import { pick2d } with { rng } from "@fil/2d-point-distributions"
Insert cell
import {distanceTime} from "@julesallegre/4-th-order-polynomial-equation-resolution"
Insert cell
import {slider, text, radio, checkbox, select} from "@jashkenas/inputs";
Insert cell
import {Scrubber} from "@mbostock/scrubber"
Insert cell
import {vl} from '@vega/vega-lite-api'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more