Уроки, алгоритмы, программы, примеры

Вход на сайт

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

У Вас число превысит максимальное число int. Можно использовать в Вашем случае uint, но лучше все переписать на double.
Добавление к программе строки glutReshapeFunc(changeSize); приводит к тому, что треугольник перестаёт совсем отрисовываться.
Выдаёт ошибку glut32.dll не найден! При том, что он лежит в System32! Всё решил) Нужно отправить не в System32, а в System.
Спасибо за статью. Я не Ваш студент. Но мне она помогла написать функцию для Канторова множества на Python для черепашки: import turtle def kanter(x, y, d):     if d > 1:         turtle...
Как реализовать в данном примере границы расчёта?

Счетчики и рейтинг

Рейтинг@Mail.ru Яндекс.Метрика
Скриншот к примеру
Среда программирования: 
Notepad++

Цель: Показать работу алгоритма удаления невидимых граней выпуклого многогранника.
Краткое описание: Куб задан множеством из 6 граней. Вначале находятся нормали к граням, затем рассчитывается, какие из граней будут невидимы при отрисовке.
Для того, чтобы куб появился, нажмите ЛКМ и потяните мышку на canvas'е.

Код программы: 

Вначале создаем canvas для рисования

<html>
<head></head>
	<body>
		<p>Для отображения тела нажмите ЛКМ в поле ниже и потяните в любую сторону </p>
		<canvas id="pic" width="700" height="700" style="border:1px solid red">
		</canvas><br>
		<button onclick="ctx.clearRect(0,0,500,500);">clear</button><br>
		<script type="text/javascript">//... реализация приведена ниже</script>
	</body>
</html>

Инициализация canvas и реализация алгоритма:
let _ = undefined;
let c = document.getElementById('pic');
let ctx = c.getContext('2d');
let colors = ['#ff0000', '#00ff00', '#0000ff', '#ff00ff', '#00ffff', '#ffff00'] // цвета граней
// данные для вращения мышкой
let prevPointer = [];
let ctxClicked = false;
let xRotation=0, yRotation=0;
let K = 30;
// описание грани фигуры
class Face
{
	constructor(pts)
	{
		if(pts[0].length!=3) throw 'можно только 3D';
		if(pts.length<3) throw 'минимальная фигура - треугольник';
		this.center = [];
		this.points = pts.slice();
		for(let i in this.points[0])
		{
			this.center[i] = 0;
			for(let j of this.points)
				this.center[i] += j[i];
			this.center[i] /= this.points.length;
		}
		this.normal = [ (this.points[0][1]-this.points[1][1])*(this.points[2][2]-this.points[1][2])-
							(this.points[0][2]-this.points[1][2])*(this.points[2][1]-this.points[1][1]),
 
						(this.points[0][2]-this.points[1][2])*(this.points[2][0]-this.points[1][0])-
							(this.points[0][0]-this.points[1][0])*(this.points[2][2]-this.points[1][2]),
 
						(this.points[0][0]-this.points[1][0])*(this.points[2][1]-this.points[1][1])-
							(this.points[0][1]-this.points[1][1])*(this.points[2][0]-this.points[1][0])];
	}
	rotate(alpha=undefined, axis=0, self=false) // axis 1=x, 2=y, 3=z. 0=нет оси поворота
	{
		let x, y, z;
		let offset = [];
		if(axis!=0 && alpha!=undefined)
		{
			alpha = alpha*(Math.PI/180);
			if(self) // есть возможность повернуть грань вокруг ее центра
			{
				for(const o in this.center)
					offset[o] = this.center[o];
				for(const i in this.points)
					for(const j in this.points[i])
						this.points[i][j]-=offset[j];
				for(const i in this.center)
				{
					this.center[i]-=offset[i]
					this.normal[i]-=offset[i]
				}
			}
			// поворот данной грани вокруг точки
			for(const i in this.points)
			{
				x = this.points[i][0]; 
				y = this.points[i][1];
				z = this.points[i][2];
				if(axis==1) this.points[i] = [x, y*Math.cos(alpha)+z*Math.sin(alpha), -y*Math.sin(alpha)+z*Math.cos(alpha)];
				else if(axis==2) this.points[i] = [x*Math.cos(alpha)+z*Math.sin(alpha), y, -x*Math.sin(alpha)+z*Math.cos(alpha)];
				else if(axis==3) this.points[i] = [x*Math.cos(alpha)+y*Math.sin(alpha), -x*Math.sin(alpha)+y*Math.cos(alpha), z];
			}
 
			if(axis==1)
			{						
				this.normal = [this.normal[0], this.normal[1]*Math.cos(alpha)+this.normal[2]*Math.sin(alpha), -this.normal[1]*Math.sin(alpha)+this.normal[2]*Math.cos(alpha)];
				this.center = [this.center[0], this.center[1]*Math.cos(alpha)+this.center[2]*Math.sin(alpha), -this.center[1]*Math.sin(alpha)+this.center[2]*Math.cos(alpha)];
			}
			else if(axis==2)
			{
				this.normal = [this.normal[0]*Math.cos(alpha)+this.normal[2]*Math.sin(alpha), this.normal[1], -this.normal[0]*Math.sin(alpha)+this.normal[2]*Math.cos(alpha)];
				this.center = [this.center[0]*Math.cos(alpha)+this.center[2]*Math.sin(alpha), this.center[1], -this.center[0]*Math.sin(alpha)+this.center[2]*Math.cos(alpha)];
			}
			else if(axis==3)
			{
				this.normal = [this.normal[0]*Math.cos(alpha)+this.normal[1]*Math.sin(alpha), -this.normal[0]*Math.sin(alpha)+this.normal[1]*Math.cos(alpha), this.normal[2]];
				this.center = [this.center[0]*Math.cos(alpha)+this.center[1]*Math.sin(alpha), -this.center[0]*Math.sin(alpha)+this.center[1]*Math.cos(alpha), this.center[2]];
			}
			if(self)
			{
				for(const i in this.points)
					for(const j in this.points[i])
						this.points[i][j]+=offset[j];
				for(const i in this.center)
				{
					this.center[i]+=offset[i]
					this.normal[i]+=offset[i]
				}
			}
		}
	}
}
// задание куба
let shape =
[
	new Face([[-5, -5, -5], [-5, -5, 5], [-5, 5, 5], [-5, 5, -5]]),
	new Face([[5, 5, 5], [5, -5, 5], [5, -5, -5], [5, 5, -5]]),
 
	new Face([[-5, 5, -5], [5, 5, -5], [5, -5, -5], [-5, -5, -5]]),
	new Face([[-5, 5, 5], [5, 5, 5], [5, -5, 5], [-5, -5, 5]]),
 
	new Face([[-5, 5, -5], [-5, 5, 5], [5, 5, 5], [5, 5, -5]]),
	new Face([[-5, -5, -5], [-5, -5, 5], [5, -5, 5], [5, -5, -5]])
];
 
checkNormals(shape); // проверка нормалей, чтобы они смотрели "наружу"
// обработка события нажатия и перетаскивания мышью на canvas
c.onmousedown = (e)=>{ ctxClicked = true; prevPointer = [e.pageX-8, e.pageY-8];}
c.onmouseup   = (e)=>
{
	ctxClicked = false;
	prevPointer = [];
}
c.onmousemove = (e)=>
{
	if(ctxClicked)
	{
		x = e.pageX-8;
		y = e.pageY-8;
		if(prevPointer[0]!=x || prevPointer[1]!=y)
		{
			xRotation = ((x - prevPointer[0])*20)/(Math.PI*180);
			yRotation = ((y - prevPointer[1])*20)/(Math.PI*180);
			draw(yRotation, -xRotation);
			prevPointer = [x, y];
		}
	}
}
 
function checkNormals(shp)
{
	let testVector = [0,0,0];
	let result = 0;
	for(let i in shp)
	{
		for(let j in shp[i].center)
			testVector[j] += shp[i].center[j];
		for(let j in testVector)
			result += shp[i].normal[j]*(shp[i].center[j] - (testVector[j]/shp.length)); // dot(A,B), где B=вектор из центра многогранника до центра данной грани
		if(result<0)
			for(let j in shp[i].normal)
				shp[i].normal[j] *= -1; // разворот вектора нормали
		result = 0;
		testVector = [0,0,0];
	}
}
 
function getProjectionXY(x, y, z, k=K, x_bias=350, y_bias=350) // проекция на экран
{
	let scale = 15;
	return [((k*x)/(z+k))*scale+x_bias, ((k*y)/(z+k))*scale+y_bias];
}
 
//непосредственно отрисовка
function draw(xRotation, yRotation)
{
	ctx.clearRect(0,0,700,700);
	for(const i in shape)
	{
		ctx.beginPath();
		ctx.fillStyle = colors[i];
		shape[i].rotate(xRotation, 1); // поворот всех точек
		shape[i].rotate(yRotation, 2);
		for(let j in shape[i].points)
		{
			if(( (shape[i].normal[0]*(-shape[i].center[0])) + (shape[i].normal[1]*(-shape[i].center[1])) + (shape[i].normal[2]*(-K-shape[i].center[2])) )>0) 
				ctx.lineTo(...getProjectionXY(...shape[i].points[j])); // отрисовка грани, если скалярное произведение между вектором "камеры" и вектором нормали грани > 0
		}
		ctx.closePath();
		ctx.fill();
	}
}

Прикрепленный файлРазмер
Melnikov_invisibleFaceDelete.zip2.39 кб