Создание стен. Часть 1. Создание эквидистант.

1+

Для построения стен заданной толщины по заданной полилинии необходимо определение эквидистант к этой линии. Основной задачей построения эквидистант является нахождения точек в углах линии – пересечений эквидистант.

Для каждой точки полилинии определяем внутреннюю и внешнюю точку эквидистанты и добавляем их в соответствующие массивы

for (let i=0; i < points.length; i++){
    if (points[i+1] &amp;&amp; points[i+2]){
        const coner = {};
        coner.pointA = points[i];
        coner.vertex = points[i+1];
        coner.pointC = points[i+2];

        const inside = buildEquidistantPoint(coner, WALLTHICKNESS, 'inner');
        const outer = buildEquidistantPoint(coner, WALLTHICKNESS, 'outer');

        wallArray.push(inside);
        wallArrayOuter.push(outer);
      }
}

Определение внутренней точки происходит следующим образом

function buildEquidistantPoint(coner, wallThickness, type){
	const halfTickness = 0.5 * wallThickness;
	const leftSide = coner.vertex.clone().sub(coner.pointA.clone()).normalize();
	const rightSide = coner.vertex.clone().sub(coner.pointC.clone()).normalize();
	const angle = rightSide.angle() - leftSide.angle();
	const alfa = angle/2;
	let bisectorLength;
	if(type === 'inner'){
		bisectorLength = halfTickness/Math.sin(-alfa);
	}
	if(type === 'outer'){
		bisectorLength = halfTickness/Math.sin(alfa);
	}
	const bisector = leftSide.clone().rotateAround(new THREE.Vector2(0, 0), alfa);
	const point = coner.vertex.clone().add((bisector.clone()).multiplyScalar(bisectorLength));
	return point;
}

Первое – определяем вектора, компланарные линиям, выходящим из данной точки, для это производим вычитание векторов вершин и нормализуем им для получение единичного вектора.

const leftSide = coner.vertex.clone().sub(coner.pointA.clone()).normalize();
const rightSide = coner.vertex.clone().sub(coner.pointC.clone()).normalize();

второе – определяем угол между ними

const angle = rightSide.angle() - leftSide.angle();

точка эквидистанты всегда лежит на биссектрисе угла, поэтому угол делим пополам

const alfa = angle/2;

затем определяем гипотенузу в треугольнике, образованном половиной толщины стены и полилинией. Обозначим ее bisectorLength

bisectorLength = halfTickness/Math.sin(-alfa);

получим вектор, выходящий из вершины в точку b поворотом вектора leftSide на угол alfa

const bisector = leftSide.clone().rotateAround(new THREE.Vector2(0, 0), alfa);

все, что нам остается – это перенести точку B  по полученному вектору. Для этого умножим его на скаляр – длину полученного отрезка bisectorLength и произведём сложение вектора вершины угла и полученного вектора.

const point = coner.vertex.clone().add((bisector.clone()).multiplyScalar(bisectorLength));

построение конечных точек незамкнутых линий осуществляет подобным образом, только угол всегда одинаков const angle = 3 * Math.PI / 4;

function buildEnding(pointB, pointA, wallThickness) {
	const halfTickness = 0.5 * wallThickness;
	const ending = [];
	const BA = pointB.clone().sub(pointA.clone()).normalize();
	const angle = 3 * Math.PI / 4;
	const bisectorLength = halfTickness/Math.sin(angle);
	const bisectorOuter = BA.clone().rotateAround(new THREE.Vector2(0, 0), -angle);
	const outerEnding = pointB.clone().add((bisectorOuter.clone().negate()).multiplyScalar(bisectorLength));
	ending.push(outerEnding);
	const bisectorInner = BA.clone().rotateAround(new THREE.Vector2(0, 0), angle);
	const innerEnding = pointB.clone().add((bisectorInner.clone().negate()).multiplyScalar(bisectorLength));
	ending.push(innerEnding);
	return ending;
}

Обработка конечных точек полилинии происходит следующим образом

if(points[0].equals(points[points.length-1])) {
	const coner = {};
	coner.pointA = points[points.length - 2];
	coner.vertex = points[0];
	coner.pointC = points[1];

	const inside = buildEquidistantPoint(coner, WALLTHICKNESS, 'inner');
	const outer = buildEquidistantPoint(coner, WALLTHICKNESS, 'outer');

	wallArray.push(inside);
	wallArrayOuter.push(outer);
	wallArray.push(wallArray[0]);
	wallArrayOuter.push(wallArrayOuter[0]);
} else {
	const end = buildEnding(points[points.length-1], points[points.length-2], WALLTHICKNESS)
	wallArrayOuter.push(...end)
	wallArray.push(wallArrayOuter[wallArrayOuter.length-1]);

	const start = buildEnding(points[0], points[1], WALLTHICKNESS)
	wallArrayOuter.unshift(...start);
	wallArray.unshift(wallArrayOuter[0]);
}

Полный код можно увидеть здесь https://jsfiddle.net/Dragon3DGraff/w5pufk04/10/

1+