Страницы

Поиск по вопросам

четверг, 12 декабря 2019 г.

Трехмерный (X, Y и Z) график с использованием D3.js

#javascript #css #html5 #svg #d3js


Я ищу график, который имеет 3 измерения (x, y и z) и использует D3.js.  

Пожалуйста, подскажите, есть ли какой-либо сайт по визуализации данных, где я могу
найти такой график, или есть  на самом сайте d3js.org, где я не смог его найти.  
    


Ответы

Ответ 1



Вот такая вот попытка изобразить перспективу на чистом js+svg: let f = (x, z) => Math.cos(z/20)*20 + Math.sin(x/10)*10 + x/3*Math.atan2(z,x); let cos = Math.cos, sin = Math.sin, xyz = 'xyz'.split(''), k = 500, a1 = 0, a2 = 0, far = 300, p, w, h, a, points = [], lines = [], s = 100; for (var x = -s; x < s; x += 5) for (var z = -s; z < s; z += 5) points.push({x, z, r:2}); for (var i = 0; i < 6; i++) lines.push([ {x:-s, y:-s, z:-s, color:`hsl(${i*120},55%,55%)`, state:{}}, {x:i%3==0?s:-s, y:i%3==1?s:-s, z:i%3==2?s:-s, state:{}} ]); points.forEach(d=>d.state={fill:`rgb(${d.x+s},${(d.y=f(d.x,d.z))+s},${d.z+s})`}); pointsGroup.innerHTML=points.map((d,i)=>``).join(''); linesGroup.innerHTML=lines.map(d=>``).join(''); let circles = pointsGroup.querySelectorAll('circle'); let paths = linesGroup.querySelectorAll('path'); function project(p) { let x = p.x*cos(a1) + p.z*sin(a1); let z = p.z*cos(a1) - p.x*sin(a1); let y = p.y*cos(a2) + z*sin(a2); let d = z*cos(a2) - p.y*sin(a2) + far; p.state.cx = (k/d)*x + w/2; p.state.cy = (k/d)*y + h/2; p.state.r = far/d*p.r; } function render() { if (a) for (var i=0; i<3; i++) xyz.forEach((c, j) => lines[i][0][c] = i==j ? -s : (lines[i][1]=a)[c]); points.forEach(project); points.sort((a, b) => a.state.r - b.state.r); lines.forEach(line => line.forEach(project)); points.forEach((d, i) => Object.entries(d.state) .forEach(e => circles[i].setAttribute(...e))); lines.forEach((l, i) => paths[i].setAttribute('d', `M${l[0].state.cx} ${l[0].state.cy} L${l[1].state.cx} ${l[1].state.cy}`)); } let evt = (t, f) => addEventListener(t, e => render(f(e))); evt('click', e => a = points[e.target.getAttribute('ind')]) evt('wheel', e => k *= 1 - Math.sign(e.deltaY)*0.1); evt('mouseup', e => p = null); evt('mousedown', e => p = {x: e.x, y: e.y, a1, a2}); evt('mousemove', e => p && (a1 = p.a1-(e.x-p.x)/100) + (a2 = p.a2-(e.y-p.y)/100)); evt('resize', e=>svg.setAttribute('viewBox',`0 0 ${w=innerWidth} ${h=innerHeight}`)); dispatchEvent(new Event('resize')); Почти тот же график, но уже на канве let f = (x, z) => Math.cos(z/20)*20 + Math.sin(x/10)*10 + x/3*Math.atan2(z,x); let ctx = canvas.getContext('2d'); let cos = Math.cos, sin = Math.sin, xyz = 'xyz'.split(''), k = 500, a1 = 0, a2 = 0, far = 300, p, w, h, a, points = [], lines = [], s = 100; for (var x = -s; x < s; x += 5) for (var z = -s; z < s; z += 3) points.push({x, y: f(x,z), z, r:2, state:{}}); points.forEach(d => d.color = `rgb(${d.x+s},${(d.y=f(d.x,d.z))+s},${d.z+s})`) for (var i = 0; i < 3; i++) lines.push([ {x:-s, y:-s, z:-s, color:`hsl(${i*120},55%,55%)`, state:{}}, {x:i%3==0?s:-s, y:i%3==1?s:-s, z:i%3==2?s:-s, state:{}} ]); function project(p) { let x = p.x*cos(a1) + p.z*sin(a1); let z = p.z*cos(a1) - p.x*sin(a1); let y = p.y*cos(a2) + z*sin(a2); let d = z*cos(a2) - p.y*sin(a2) + far; p.state.cx = (k/d)*x + w/2; p.state.cy = (k/d)*y + h/2; p.state.r = far/d*p.r; } function render() { points.forEach(project); points.sort((a, b) => a.state.r - b.state.r); lines.forEach(line => line.forEach(project)); ctx.clearRect(0,0,w,h) points.forEach(drawPoint); lines.forEach(drawLine); } function drawPoint(p){ ctx.beginPath(); ctx.arc(p.state.cx, p.state.cy, p.state.r, 0, 2 * Math.PI); ctx.fillStyle = p.color; ctx.fill(); } function drawLine(l){ ctx.beginPath(); ctx.moveTo(l[0].state.cx, l[0].state.cy); ctx.lineTo(l[1].state.cx, l[1].state.cy); ctx.strokeStyle = l[0].color; ctx.stroke(); } let evt = (t, f) => addEventListener(t, e => render(f(e))); evt('wheel', e => k *= 1 - Math.sign(e.deltaY)*0.1); evt('mouseup', e => p = null); evt('mousedown', e => p = {x: e.x, y: e.y, a1, a2}); evt('mousemove', e => p && (a1 = p.a1-(e.x-p.x)/100) + (a2 = p.a2-(e.y-p.y)/100)); evt('resize', e => (w=canvas.width=innerWidth-20)+(h=canvas.height=innerHeight-20)); dispatchEvent(new Event('resize'));

Ответ 2



Трехмерные графики рассеяния, ссылки, на которые даны VividD и Lars Kotthoff, вероятно, является лучшим примером того, что вы просите, но я собираюсь предположить, что, возможно, вы задаете не тот вопрос. Попытка симулировать три пространственных измерения на плоском экране всегда будет несовершенной и затруднит чтение данных. Однако в D3 очень легко отобразить три разных измерения данных. Допустим,вы используете горизонтальные и вертикальные макеты для двух ваших переменных данных, а затем размер, форму, цвет или затенение для своей третьей переменной. Если все три ваши переменные данные лучше всего представлены непрерывными числами, тогда лучше всего использовать диаграмму разброса пузырьков, где ваши три измерения отображения - это горизонтальное положение, вертикальное положение и размер пузырька. Bubble Scatterplot -- click for original Вы сказали, что ваши три измерения - это Customer, Product и content. Я не знаю, какое значение имеет content (число или категория), но я почти уверен, что Customer и Product - это категории. Вот пример, где используются два категориальных измерения. Чтобы расположить таблицу, каждая ячейка таблицы содержит круг, измеренный третьим, числовым измерением. Если ваша третья переменная является категорией, вы можете использовать форму, чтобы указать, какой тип «контента» (если есть) соответствует каждой паре «клиент» и «продукт»: d3-bubble-matrix Вот еще одно приложение, где третье измерение показано цветом, а не размером. Цвета представляют непрерывную переменную, но вы можете легко выбрать набор высоко контрастных цветов для представления категорий: Day / Hour Heatmap Конечно, простая старая гистограмма с накоплением - это еще один способ показать две категории и числовое значение: Stacked Bar Graphs И вам не нужно останавливаться на трех переменных данных. Если две из переменных данных являются категориями или числами, которые вы не возражаете сгруппировать в категории, вы можете построить график четырех переменных с помощью метода «нескольких кратных», где вы создадите таблицу, представляющую категориальные переменные, а затем повторите график две другие переменные внутри каждой ячейки таблицы. Как здесь: Scatterplot Matrix Или это приложение (где неделя и день недели - это два измерения данных, а категория / сумма - два других): Pie Chart Small Multiples Я надеюсь, что это дало вам много идей ... Источник ответа:@AmeliaBR 3D Surface Plot in D3.js 3D Vector Field in D3.js Источник ответа:@aturc

Ответ 3



Вот один пример, который напоминает то, что вы ищете: 3D scatter plot Обратите внимание, что в этом примере используется X3DOM, довольно новая попытка стандартизировать 3D-рендеринг в HTML, которая поддерживается не всеми браузерами. Ищите также X3DOM в группе Google D3. Showing GPS tracks in 3D with three.js and d3.js В целом, D3.js больше ориентирован на визуализацию данных, чем на научную визуализацию, это означает, что он не имеет широкой поддержки для отображения трехмерного пространства (за исключением отображения географических 3D-данных, которые D3.js поддерживает превосходным образом, но это не то, что нужно). Например (этот пример не связан напрямую с вашими примерами, он просто для пояснения): D3 предоставляет алгоритм для 2D-рисования деревьев, но не предоставляет никакого устройства для 3D-размещения деревьев и последующей визуализации такого размещения на 2D-экране. Если вы не ограничены D3.js, возможно, вы могли бы достичь тех же целей проще и быстрее с другими библиотеками, написанными специально для целей, аналогичных вашим. Например, вы можете использовать Pre3D Посмотрите на этот пример не использует X3DOM, просто рендеринг HTML на 2D-холсте. Я думаю, что анимация - вращение его трехмерных графиков, более плавная, чем в первом примере D3/X3DOM. Источник ответа:@VividD

Комментариев нет:

Отправить комментарий