Как нарисовать поверхность по заданным параметрам { x, y, z } и прочее в d3.js?

Не нашел четких решений для того чтобы решить данную проблему. Есть какой нибудь пример?

Я пробовал вот это, но у меня не получилось модернизировать код под себя.

Вот задача: даны 6 точкек: A(x1,y1,z1), B(x2,y2,z2), C(x3,y3,z3) ,D(x4,y4,z4), E(x5,y5,z5), F(x6,y6,z6):

Вот вопрос: как нарисовать из этого поверхность, и/или кривые Безье в 3d пространстве используя d3.js? Да и прибавьте мне рейтинга а то я совсем уже того-с-с-с...


Ответы (2 шт):

Автор решения: Олег

То, что я смог реализовать, скорее идея, но не полное решение. Изменил пример по ссылке таким образом:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body{
  font-family: sans;
  padding: 10px;
}
svg path{
  stroke: #000;
  stroke-width: 1px;
  stroke: rgba(0,0,0,0.2);
}
svg{
  border: 1px solid #DED8BF;
  background-color: #f4f4d9;
  width: 700px;
  height: 400px;
}
h1{
  font-weight: normal;
  margin: 0;
  padding-left: 5px;
  color: #53483e;
}
p{
  margin: 0;
  margin-bottom: 10px;
  padding-left: 5px;
  color: #917e6b;
}
ul{
  width: 200px;
  float: left;
  list-style-type: none;
  margin: 0;
  padding: 0;
  padding-right: 10px;
}
li{
  cursor: pointer;
  background-color: #c8ad93;
  padding: 10px;
  margin: 2px;
  color: #fff;
}
</style>
</head>
<body>
  <h1>D3.js Surface Plots</h1>
  <p>Drag graph to change view</p>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <script src="surface3d.js"></script>
  <script>
var yaw=0.5,pitch=0.5, width=700, height=400, drag=false;
var coords = [ // тут координаты точек
	[23,2,15,4,10,31], // x
	[1,35,2,20,14,3], // y
	[154,35,72,29,63,81] // z
]
var output=[];
for(var x=-20;x<20;x++){
	var f0=[];            
	output.push(f0);
	for(var y=-20;y<20;y++){
		f0.push(0);
	}
}
for(i=0;i<6;i++) output[coords[0][i]][coords[1][i]]=-coords[2][i];
var surfaces=[
	{
		name: 'Dataset 1',
		data: output
	}
 ];
  var selected=surfaces[0];

  var ul=d3.select('body')
           .append('ul');
  var svg=d3.select('body')
          .append('svg')
            .attr('height',height)
            .attr('width',width);

  var group = svg.append("g");

  var md=group.data([surfaces[0].data])
    .surface3D(width,height)
      .surfaceHeight(function(d){ 
        return d;
      }).surfaceColor(function(d){
        var c=d3.hsl((d+100), 0.6, 0.5).rgb();
        return "rgb("+parseInt(c.r)+","+parseInt(c.g)+","+parseInt(c.b)+")";
      });

  ul.selectAll('li')
    .data(surfaces)
      .enter().append('li')
        .html(function(d){
          return d.name
        }).on('mousedown',function(){
          md.data([d3.select(this).datum().data]).surface3D()
            .transition().duration(500)
            .surfaceHeight(function(d){
              return d;
            }).surfaceColor(function(d){
              var c=d3.hsl((d+100), 0.6, 0.5).rgb();
              return "rgb("+parseInt(c.r)+","+parseInt(c.g)+","+parseInt(c.b)+")";
            });
        });

  svg.on("mousedown",function(){
    drag=[d3.mouse(this),yaw,pitch];
  }).on("mouseup",function(){
    drag=false;
  }).on("mousemove",function(){
    if(drag){            
      var mouse=d3.mouse(this);
      yaw=drag[1]-(mouse[0]-drag[0][0])/50;
      pitch=drag[2]+(mouse[1]-drag[0][1])/50;
      pitch=Math.max(-Math.PI/2,Math.min(Math.PI/2,pitch));
      md.turntable(yaw,pitch);
    }
  });
  </script>
</body>
</html>

Получилось, что в местах с заданными координатами x и y точек образовались пирамидальные пики, высота которых соответствует координате z каждой из них. Останется плавно соединить вершины этих пиков между собой. Я пытался подобрать алгоритм, но за один вечер ничего не вышло.

→ Ссылка
Автор решения: Олег

Первый вариант: ряд кривых Безье. За основу берутся 16 опорных точек, разбитых на матрицу 4 группы по 4 точки. Каждая из этих групп рисует одну кривую Безье, располагающуюся циклом координат x и z нашего 3-д холста. А сами кривые расположены вдоль направления изменения координаты y холста.

<!DOCTYPE html>
<html>
<head>
<title>Ряд кривых Безье по 16 опорным точкам</title>
<meta charset="utf-8">
<style>
svg{
  border: 1px solid #DED8BF;
  background-color: #f4f4d9;
  width: 700px;
  height: 400px;
}
</style>
</head>
<body>
<h1>Ряд кривых Безье по 16 опорным точкам</h1>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="surface3d.js"></script>
<script>
var size=64;
var step=0.01;
var arr = new Array([],[],[],[]);

arr[0][0] = [5, 15]; // в скобках координаты x и z опорных точек для первой кривой Безье,
arr[0][1] = [15, 30]; // координата y для всех точек - 0
arr[0][2] = [32, 150];
arr[0][3] = [60, 15];
	
arr[1][0] = [10, 45]; // в скобках координаты x и y опорных точек для второй кривой Безье
arr[1][1] = [43, 15]; // координата y для всех точек - 1
arr[1][2] = [27, 150];
arr[1][3] = [57, 30];

arr[2][0] = [25, 15]; // в скобках координаты x и y опорных точек для третьй кривой Безье
arr[2][1] = [33, 165]; // координата y для всех точек - 2
arr[2][2] = [47, 69];
arr[2][3] = [55, 30];

arr[3][0] = [5, 5]; // в скобках координаты x и y опорных точек для четвёртой кривой Безье
arr[3][1] = [20, 170]; // координата y для всех точек - 3
arr[3][2] = [43, 45];
arr[3][3] = [57, 15];
		
var yaw=0.5,pitch=0.5, width=700, height=400, drag=false;
var output=[];
for(var x=-size/2;x<size/2;x++){
	var f0=[];            
	output.push(f0);
	for(var y=-2;y<2;y++) f0.push(0);
}
for(i=0;i<arr.length;i++){
	var res = [];
	for (var t = 0; t < 1 + step; t += step) {
		var ind = res.length;
		res[ind] = [0, 0];
		for (var j = 0; j < arr[i].length; j++) {
			var b = getBezierBasis(j, arr[i].length - 1, t);
			res[ind][0] += arr[i][j][0] * b;
			res[ind][1] += arr[i][j][1] * b;
		}
		res[ind][0] = Math.floor(res[ind][0]);
		res[ind][1] = Math.floor(res[ind][1]);
	}
	for(j=0;j<res.length;j++) output[res[j][0]][i]=-res[j][1];
}
	
 // i - номер вершины, n - количество вершин, t - положение кривой (от 0 до 1)
function getBezierBasis(i, n, t) {

	// Факториал
	function f(n) {return (n <= 1) ? 1 : n * f(n - 1)};
	
	// считаем i-й элемент полинома Берштейна
	return (f(n)/(f(i)*f(n - i)))* Math.pow(t, i)*Math.pow(1 - t, n - i);
}

var svg=d3.select('body')
	.append('svg')
	.attr('height',height)
	.attr('width',width);
var group = svg.append("g");
var md=group.data([output])
	.surface3D(width,height)
	.surfaceHeight(function(d){ 
	return d;
}).surfaceColor(function(d){
	var c=d3.hsl((d+100), 0.6, 0.5).rgb();
	return "rgb("+parseInt(c.r)+","+parseInt(c.g)+","+parseInt(c.b)+")";
});
svg.on("mousedown",function(){
	drag=[d3.mouse(this),yaw,pitch];
}).on("mouseup",function(){
	drag=false;
}).on("mousemove",function(){
	if(drag){            
		var mouse=d3.mouse(this);
		yaw=drag[1]-(mouse[0]-drag[0][0])/50;
		pitch=drag[2]+(mouse[1]-drag[0][1])/50;
		pitch=Math.max(-Math.PI/2,Math.min(Math.PI/2,pitch));
		md.turntable(yaw,pitch);
	}
});
</script>
</body>
</html>

Второй вариант: поверхность Безье. За основу взяты те же самые 16 опорных точек, что и в первом варианте.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Поверхность Безье по 16 опорным точкам</title>
<style>
svg{
  border: 1px solid #DED8BF;
  background-color: #f4f4d9;
  width: 700px;
  height: 400px;
}
</style>
</head>
<body>
  <h1>Поверхность Безье по 16 опорным точкам</h1>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <script src="surface3d.js"></script>
  <script>
var size=64;
var step=1/size;
var arr = new Array([],[],[],[]);

arr[0][0] = [5, 15]; // в скобках координаты x и z опорных точек для поверхности Безье,
arr[0][1] = [15, 30]; // координата y для всех точек - 0
arr[0][2] = [32, 150];
arr[0][3] = [60, 15];
	
arr[1][0] = [10, 45]; // в скобках координаты x и y опорных точек для поверхности Безье
arr[1][1] = [43, 15]; // координата y для всех точек - 1
arr[1][2] = [27, 150];
arr[1][3] = [57, 30];

arr[2][0] = [25, 15]; // в скобках координаты x и y опорных точек для поверхности Безье
arr[2][1] = [33, 165]; // координата y для всех точек - 2
arr[2][2] = [47, 69];
arr[2][3] = [55, 30];

arr[3][0] = [5, 5]; // в скобках координаты x и y опорных точек для поверхности кривой Безье
arr[3][1] = [20, 170]; // координата y для всех точек - 3
arr[3][2] = [43, 45];
arr[3][3] = [57, 15];
		
var res = [];
for (var t1 = 0; t1 < 1 + step; t1 += step) {
	var ind1 = res.length;
	res[ind1] = [];
	for (var t = 0; t < 1 + step; t += step) {
		var ind = res[ind1].length;
		res[ind1][ind] = [0, 0];
		for (var i = 0; i < arr.length; i++) {
			for (var j = 0; j < arr[i].length; j++) {
				var b = getBezierBasis(i, arr.length - 1, t1);
				var b1 = getBezierBasis(j, arr[i].length - 1, t);
				res[ind1][ind][0] += arr[i][j][0] * b * b1;
				res[ind1][ind][1] += arr[i][j][1] * b * b1;
			}
		}
		res[ind1][ind][0] = Math.floor(res[ind1][ind][0]);
		res[ind1][ind][1] = Math.floor(res[ind1][ind][1]);
	}
}

 // i - номер вершины, n - количество вершин, t - положение кривой (от 0 до 1)
function getBezierBasis(i, n, t) {

	// Факториал
	function f(n) {return (n <= 1) ? 1 : n * f(n - 1)};
	
	// считаем i-й элемент полинома Берштейна
	return (f(n)/(f(i)*f(n - i)))* Math.pow(t, i)*Math.pow(1 - t, n - i);
}

var yaw=0.5,pitch=0.5, width=700, height=400, drag=false;
var output=[];
for(var x=-size/2;x<size/2;x++){
	var f0=[];            
	output.push(f0);
	for(var y=-size/2;y<size/2;y++) f0.push(0);
}
for(j=0;j<size;j++) for(i=0;i<size;i++) output[res[j][i][0]][j]=-res[j][i][1];
var svg=d3.select('body')
	.append('svg')
	.attr('height',height)
	.attr('width',width);
var group = svg.append("g");
var md=group.data([output])
	.surface3D(width,height)
	.surfaceHeight(function(d){ 
	return d;
}).surfaceColor(function(d){
	var c=d3.hsl((d+100), 0.6, 0.5).rgb();
	return "rgb("+parseInt(c.r)+","+parseInt(c.g)+","+parseInt(c.b)+")";
});
svg.on("mousedown",function(){
	drag=[d3.mouse(this),yaw,pitch];
}).on("mouseup",function(){
	drag=false;
}).on("mousemove",function(){
	if(drag){            
		var mouse=d3.mouse(this);
		yaw=drag[1]-(mouse[0]-drag[0][0])/50;
		pitch=drag[2]+(mouse[1]-drag[0][1])/50;
		pitch=Math.max(-Math.PI/2,Math.min(Math.PI/2,pitch));
		md.turntable(yaw,pitch);
	}
});
</script>
</body>
</html>

К сожалению, с помощью библиотеки d3.js (в отличии от three.js на WebGl) невозможно отобразить абсолютно любую поверхность Безье, а только такую, у которой не встречается одинаковых совпадений координат x и y. (типо похожую на реальный участок поверхности земли) Поэтому приходится подбирать опорные точки, а как результат, поверхность Безье получается слишком сглаженной. Поэтому первый вариант более выразителен.

→ Ссылка