Алгоритм трехточечного градиента

от автора

Приветствую, друзья.

В данной статье я расскажу вам о том, как рисовать треугольный градиент. Наверняка некоторые языки программирования имеют встроенные средства для выполнения этой задачи. Мы же с вами попробуем реализовать такой градиент используя стандартные геометрический формулы. Вот что в итоге необходимо получить:

Что имеем:

Рассмотрим треугольник с вершинами в точках A,B и C. Точки пересечения перпендикуляров, опущенных из вершин ABC, с противоположными сторонами треугольника назовем A", B" и C":

Задача:

Необходимо в каждой точке внутри треугольника(назовем такую точку O), определить проценты близости этой точки к каждой вершине(A,B,C) треугольника. Проценты в сумме должны дать значение 100.

Решение:

Все что нам понадобится, это формулы пересечения прямых и уравнение пермендикуляра, опущенного из точки на прямую на плоскости. Определить точку пересечения прямых можно по формуле:

// прямые, заданные по двум точкам l1 = {p1,p2}; l2 = {p1,p2}; //  d = (l2.p2.y-l2.p1.y) * (l1.p2.x-l1.p1._x) - (l2.p2.x-l2.p1.x) * (l1.p2.y-l1.p1.y); a = (l2.p2.x-l2.p1.x) * (l1.p1.y-l2.p1._y) - (l2.p2.y-l2.p1.y) * (l1.p1.x-l2.p1.x); // точка пересечения x0 = l1.p1.x + a * (l1.p2.x-l1.p1.x)/d; y0 = l1.p1.y + a * (l1.p2.y-l1.p1.y)/d; 

Получить уравнение перпендикуляра опущенного из точки A на прямую line можно так:

// точка задана по двум координатам A = {x,y}; // линия задана по двум точкам line = {p1,p2}; // // определяем вектор направления линии line dir = {0,0}; dir.x = line.p2.x - line.p1.x; dir.y = line.p2.y - line.p1.y; // // очевидно, что первая точка искомой линии(перпендикуляра) будет точка A // найдем вторую точку point2 = {0,0}; if (dir.y != 0) { 	temp = dir.x*A.x + dir.y*A.y; 	point2.x = A.x != 1 ? 1 : 2; 	point2.y = (-dir.x * point2.x + temp) / dir.y; } else { 	point2.x = A.x; 	point2.y = line.p1.y; } // // искомая линия, перпендикуляр опущенный из точки A на прямую line, // будет прямая проходящая через точки A и point2 // Для решения поставленной в статье задачи, // нам будет необходимо найти длину высоты, // опущенной из вершины треугольника на противоположную сторону. // Эту длину можно определить следующим образом. // Находим точку пересечения прямой A-point2 и противоположной точке А прямой в треугольнике, // и находим расстояние между найденной точкой и заданной A. 

Опустим перепендикуляр из заданной точки O на пермендикуляр опущенный из точки A на противоположную сторону треугольника. Точку пересечения полученной линии с перпендикуляром, опущенным из точки A, назовем Ah. Аналогично находим точки Bh и Ch:

Процент близости от заданной точки О к вершине A можно определить отношением AAh/AA", где AAh — длина отрезка A->Ah, а AA" — длина высоты опущенной из вершины A на противоположную сторону (т.е. отрезок A->A"):

p1 = AAh/AA"; p2 = BBh/BB"; p3 = CCh/CC"; 

Определив проценты близости точки, можем определить цвет этой точки по формуле:

red = color1.red*p1 + color2.red*p2 + color3.red*p3; green = color1.green*p1 + color2.green*p2 + color3.green*p3; blue = color1.blue*p1 + color2.blue*p2 + color3.blue*p3; 

Где red, green и blue — это каналы в цветовой схеме RGB. Получив значения каналов RGB для заданной точки, мы можем получить значение цвета в шеснадцатеричном формате:

color = (red << 16) + (green << 8) + blue; 

Обратное преобразование из шестандцатеричного кода в RGB можно сделать с помощью формул:

color = 0xffff00; // красный канал red = (color >>> 16) & 0xff // зеленый канал green = (color >>> 8) & 0xff // синий канал blue = color & 0xff 

Более подробно про работу с каналами RGB можно почитать здесь.

Пример на языке ActionScript 3.0

Посмотреть результат вы можете во флешке.

Исходный код флешки

package { 	import flash.display.Sprite; 	import flash.filters.GlowFilter; 	 	import ru.flashpress.callback.ICallback; 	import ru.flashpress.geom.line.FPGLine2d; 	import ru.flashpress.geom.line.math.FPGLineToPoint2dMath; 	import ru.flashpress.geom.point.FPGPoint2d; 	import ru.flashpress.geom.point.FPGPoint2dMath; 	import ru.flashpress.geom.triangles.FPGTriangle2d; 	import ru.flashpress.geom.view.core.FillData; 	import ru.flashpress.geom.view.line.LineView; 	import ru.flashpress.geom.view.point.PointView; 	import ru.flashpress.geom.view.triangle.TriangleView; 	 	public class testPointG3 extends Sprite implements ICallback 	{ 		// геометрия треугольника 		private var triangle:FPGTriangle2d; 		// отображение треугольника 		private var triangleView:TriangleView; 		// геометория проверяемой точки 		private var targetPoint:FPGPoint2d; 		// отображение проверяемой точки 		private var targetPointView:PointView; 		// 		// проекции трех вершин на противповоложные стороны 		private var pHeight1:PointView; 		private var pHeight2:PointView; 		private var pHeight3:PointView; 		// 		// проекции текущей точки на высоты 		// опущенные из вершин треугольника 		private var crossHeight1:PointView; 		private var crossHeight2:PointView; 		private var crossHeight3:PointView; 		// 		// пукнтирные линии соединяющие текущую точку 		// с проекциями crossHeight(1/2/3) 		private var lineCross1:LineView; 		private var lineCross2:LineView; 		private var lineCross3:LineView; 		//  		public function testPointG3() 		{ 			var glow:GlowFilter = new GlowFilter(0xffffff, 1, 2, 2, 10, 3); 			// 			var size:Number = Math.min(stage.stageWidth, stage.stageHeight)*0.8; 			var p1:FPGPoint2d = new FPGPoint2d(size/2, 0); 			var p2:FPGPoint2d = new FPGPoint2d(size, size*0.8); 			var p3:FPGPoint2d = new FPGPoint2d(0, size*0.8); 			triangle = new FPGTriangle2d(p1, p2, p3); 			triangle.translate((stage.stageWidth-size)*0.5, (stage.stageHeight-size*0.8)*0.35); 			// 			triangleView = new TriangleView(triangle, new FillData(0x666666, 3)); 			triangleView.visibleHeights = true; 			triangleView.heightLines.fill = new FillData(0x9900, 2); 			// 			triangleView.point1.filters = [glow]; 			triangleView.point2.filters = [glow]; 			triangleView.point3.filters = [glow]; 			triangleView.point1.fill = color1.fill(2); 			triangleView.point2.fill = color2.fill(2); 			triangleView.point3.fill = color3.fill(2); 			triangleView.point1.name = 'A'; 			triangleView.point2.name = 'B'; 			triangleView.point3.name = 'C'; 			triangleView.point1.scale = 1.5; 			triangleView.point2.scale = 1.5; 			triangleView.point3.scale = 1.5; 			// 			triangleView.heightLines.line1.fill = color1.fill(2); 			triangleView.heightLines.line2.fill = color2.fill(2); 			triangleView.heightLines.line3.fill = color3.fill(2); 			// 			// 			pHeight1 = new PointView(null, color1.fill()); 			pHeight1.enabled = false; 			pHeight1.filters = [glow]; 			pHeight1.name = 'A"'; 			// 			pHeight2 = new PointView(null, color2.fill()); 			pHeight2.enabled = false; 			pHeight2.filters = [glow]; 			pHeight2.name = 'B"'; 			// 			pHeight3 = new PointView(null, color3.fill()); 			pHeight3.enabled = false; 			pHeight3.filters = [glow]; 			pHeight3.name = 'C"'; 			// 			crossHeight1 = new PointView(null, color1.fill()); 			crossHeight1.enabled = false; 			crossHeight1.filters = [glow]; 			crossHeight1.name = 'Ah'; 			// 			crossHeight2 = new PointView(null, color2.fill()); 			crossHeight2.enabled = false; 			crossHeight2.filters = [glow]; 			crossHeight2.name = 'Bh'; 			// 			crossHeight3 = new PointView(null, color3.fill()); 			crossHeight3.enabled = false; 			crossHeight3.filters = [glow]; 			crossHeight3.name = 'Ch'; 			// 			lineCross1 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color1.fill(1, true)); 			lineCross2 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color2.fill(1, true)); 			lineCross3 = new LineView(new FPGLine2d(new FPGPoint2d(0, 0), new FPGPoint2d(100, 100)), 0, 0, color3.fill(1, true)); 			// 			var xp:Number = p3.x + (triangle.p2.x-p3.x)*0.40; 			var yp:Number = p1.y + (triangle.p2.y-p1.y)*0.92; 			targetPoint = new FPGPoint2d(xp, yp); 			targetPointView = new PointView(targetPoint, new FillData(0x0, 1)); 			targetPointView.filters = [glow] 			targetPointView.scale = 1.8; 			// 			this.addChild(triangleView); 			this.addChild(pHeight1); 			this.addChild(pHeight2); 			this.addChild(pHeight3); 			this.addChild(crossHeight1); 			this.addChild(crossHeight2); 			this.addChild(crossHeight3); 			this.addChild(lineCross1); 			this.addChild(lineCross2); 			this.addChild(lineCross3); 			this.addChild(targetPointView); 			// 			targetPoint.addCallback(this); 			triangle.addCallback(this); 			callbackEvent(null, null); 		} 		 		/** 		 * Были изменены положения вершин треугольника, 		 * или текущей точки targetPoint 		 */ 		public function callbackEvent(target:Object, data:Object):void 		{ 			pHeight1.data = triangle.heightA.p2; 			pHeight2.data = triangle.heightB.p2; 			pHeight3.data = triangle.heightC.p2; 			// 			var color:Number = getColor(targetPoint.x, targetPoint.y); 			targetPointView.fill = new FillData(color); 		} 			 		 		private var color1:RGB = new RGB(255, 0, 0); 		private var color2:RGB = new RGB(0, 200, 0); 		private var color3:RGB = new RGB(0, 0, 255); 		private function getColor(_x:Number, _y:Number):uint 		{ 			var point:FPGPoint2d = new FPGPoint2d(_x, _y); 			// 			// находим уравнение высоты опущенной из текущей точки(point) 			// на высоту опущенную из вершины A(triangle.heightA) 			var h2mA:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightA); 			// аналогично находим высоты для других точек B и C 			var h2mB:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightB); 			var h2mC:FPGLine2d = FPGLineToPoint2dMath.lineHeight(point, triangle.heightC); 			if (!h2mA || !h2mB|| !h2mC) return 0xffffff; 			// 			// h2mA.p2 - это точка пересечения прямой h2mA с 			// высотой опущенной из вершины A 			crossHeight1.data = h2mA.p2; 			crossHeight2.data = h2mB.p2; 			crossHeight3.data = h2mC.p2; 			// 			// 			// находим расстояние от текущей вершины треугольника 			// до точки пересечения прямой h2mA c высотой, опущенной из вершины A 			var h2Alen:Number = FPGPoint2dMath.length(triangle.p1, h2mA.p2); 			// аналогично находим расстояния для других точек 			var h2Blen:Number = FPGPoint2dMath.length(triangle.p2, h2mB.p2); 			var h2Clen:Number = FPGPoint2dMath.length(triangle.p3, h2mC.p2); 			// 			// рисуем пунктриную линию  			lineCross1.data = h2mA; 			lineCross2.data = h2mB; 			lineCross3.data = h2mC; 			// 			// находим проценты близости 			var p1:Number = 1-h2Alen/(triangle.heightA.length); 			var p2:Number = 1-h2Blen/(triangle.heightB.length); 			var p3:Number = 1-h2Clen/(triangle.heightC.length); 			// 			// задаем нижнюю границу 			if (p1 < 0) p1 = 0; 			if (p2 < 0) p2 = 0; 			if (p3 < 0) p3 = 0; 			// 			// 			// вычисляем значения цветовых каналов red, green и blue 			var r:Number = color1.r*p1 + color2.r*p2 + color3.r*p3; 			var g:Number = color1.g*p1 + color2.g*p2 + color3.g*p3; 			var b:Number = color1.b*p1 + color2.b*p2 + color3.b*p3; 			// задаем верхнюю границу 			if (r > 255) r = 255; 			if (g > 255) g = 255; 			if (b > 255) b = 255; 			// вычисляем значение цвета 			var color:int = (r << 16) + (g << 8) + b; 			return color; 		} 	} } import ru.flashpress.geom.view.core.FillData;    class RGB { 	public var r:uint; 	public var g:uint; 	public var b:uint; 	public var code:uint; 	public function RGB(r:uint, g:uint, b:uint) 	{ 		this.r = r; 		this.g = g; 		this.b = b; 		this.code = (r << 16) + (g << 8) + b; 	} 	 	public function fill(stroke:uint=1, dotline:Boolean=false):FillData 	{ 		return new FillData(this.code, stroke, dotline); 	} } 

Для геометрических вычислений используется библиотека FPGeometry2d.swc.

Пример ActionScript3.0 с использованием шейдера

Результат закраски треугольника с использованием шейдера во флешке.

Главный класс флешки testShaderG3.as

package { 	import flash.display.Sprite; 	import flash.events.MouseEvent; 	import flash.geom.Point; 	 	import ru.flashpress.filters.G3Shader;  	public class testShaderG3 extends Sprite 	{ 		// шейдер, рисующий треугольный градиент 		private var shader:G3Shader; 		public function testShaderG3() 		{ 			start(); 		} 		 		private var p1:Sprite; 		private var p2:Sprite; 		private var p3:Sprite; 		private function start():void 		{ 			shader = new G3Shader(); 			// 			// создаем три вершины треугольника 			p1 = createPoint(200, 50); 			p2 = createPoint(350, 350); 			p3 = createPoint(50, 350); 			// 			shader.initPoints(	new Point(p1.x, p1.y), 								new Point(p2.x, p2.y), 								new Point(p3.x, p3.y)); 			// 			redraw(); 		} 		private function createPoint(_x:Number, _y:Number):Sprite 		{ 			var point:Sprite = new Sprite(); 			point.graphics.beginFill(0x0, 0.5); 			point.graphics.drawCircle(0, 0, 10); 			this.addChild(point); 			point.x = _x; 			point.y = _y; 			point.buttonMode = true; 			point.addEventListener(MouseEvent.MOUSE_DOWN, downHandler); 			return point; 		} 		private var currentPoint:Sprite; 		private function downHandler(event:MouseEvent):void 		{ 			currentPoint = event.target as Sprite; 			currentPoint.startDrag(); 			this.stage.addEventListener(MouseEvent.MOUSE_MOVE, moveHandler); 			this.stage.addEventListener(MouseEvent.MOUSE_UP, upHandler); 		} 		private function moveHandler(event:MouseEvent):void 		{ 			event.updateAfterEvent(); 			switch (currentPoint) { 				//  задаем шейдеру значения вершин треугольника 				case p1: 					shader.point1 = new Point(p1.x, p1.y); 					break; 				case p2: 					shader.point2 = new Point(p2.x, p2.y); 					break; 				case p3: 					shader.point3 = new Point(p3.x, p3.y); 					break; 			} 			redraw(); 		} 		private function upHandler(event:MouseEvent):void 		{ 			currentPoint.stopDrag(); 			this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveHandler); 			this.stage.removeEventListener(MouseEvent.MOUSE_UP, upHandler); 		} 		 		private function redraw():void 		{ 			this.graphics.clear(); 			this.graphics.beginFill(0xff0000, 1); 			this.graphics.beginShaderFill(shader); 			this.graphics.moveTo(p1.x, p1.y); 			this.graphics.lineTo(p2.x, p2.y); 			this.graphics.lineTo(p3.x, p3.y); 		} 	} }

Класс G3Shader.as

package ru.flashpress.filters { 	import flash.display.Shader; 	import flash.geom.Point; 	 	/** 	 * Шейдер, рисующий треугольный градиент, по заданным точкам и цветам к ним 	 * @author Serious Sam 	 */ 	public class G3Shader extends Shader 	{ 		private static var dir:Point = new Point(); 		private static var point2:Point = new Point(); 		private static var temp:Number; 		private static var k1:Number; 		private static var k2:Number; 		private static function getHeight(o:Point, p1:Point, p2:Point):void 		{ 			dir.x = p2.x-p1.x; 			dir.y = p2.y-p1.y; 			point2.x = 0; 			point2.y = 0; 			if (dir.y != 0) { 				temp = dir.x*o.x + dir.y*o.y; 				point2.x = o.x != 1 ? 1 : 2; 				point2.y = (-dir.x * point2.x + temp) / dir.y; 			} else { 				point2.x = o.x; 				point2.y = p1.y; 			} 			// 			k1 = (point2.y-o.y)*(p2.x-p1.x) - (point2.x-o.x)*(p2.y-p1.y); 			k2 = (point2.x-o.x)*(p1.y-o.y) - (point2.y-o.y)*(p1.x-o.x); 			pointH.x = p1.x + k2*(p2.x-p1.x)/k1; 			pointH.y = p1.y + k2*(p2.y-p1.y)/k1; 		} 		// 		// 		// 		[Embed(source="g3.pbj", mimeType="application/octet-stream")] 		private var BytecodesClass:Class; 		// 		/** 		 * @private 		 */ 		public function G3Shader() 		{ 			super(new BytecodesClass()); 		} 		 		private static var pointH:Point = new Point(); 		/** 		 * В этом методе необходимо сообщить шейдеру значения координат вершин (a, b и c) 		 * а так же координаты проекций этих вершин на противоположные стороны (ah, bh, ch) 		 */		 		private function reinit():void 		{ 			// вершина A треугольника 			this.data.a.value = [_point1.x, _point1.y]; 			getHeight(_point1, _point2, _point3); 			// проекция вершины A на сторону BC 			this.data.ah.value = [pointH.x, pointH.y]; 			// 			this.data.b.value = [_point2.x, _point2.y]; 			getHeight(_point2, _point3, _point1); 			this.data.bh.value = [pointH.x, pointH.y]; 			// 			this.data.c.value = [_point3.x, _point3.y]; 			getHeight(_point3, _point1, _point2); 			this.data.ch.value = [pointH.x, pointH.y]; 		} 		 		/** 		 * Инициализировать координаты вершин трегольника 		 */ 		public function initPoints(p1:Point, p2:Point, p3:Point):void 		{ 			this._point1.x = p1.x; 			this._point1.y = p1.y; 			// 			this._point2.x = p2.x; 			this._point2.y = p2.y; 			// 			this._point3.x = p3.x; 			this._point3.y = p3.y; 			// 			reinit(); 		} 		 		private var _point1:Point = new Point(100, 0); 		/** 		 * Координаты первой вершины треугольника 		 */ 		public function get point1():Point {return this._point1.clone();} 		public function set point1(value:Point):void 		{ 			this._point1.x = value.x; 			this._point1.y = value.y; 			// 			this.reinit(); 		} 		 		private var _point2:Point = new Point(200, 200); 		/** 		 * Координаты второй вершины треугольника 		 */ 		public function get point2():Point {return this._point2.clone();} 		public function set point2(value:Point):void 		{ 			this._point2.x = value.x; 			this._point2.y = value.y; 			// 			this.reinit(); 		} 		 		private var _point3:Point = new Point(0, 200); 		/** 		 * Координаты третьей вершины треугольника 		 */ 		public function get point3():Point {return this._point3.clone();} 		public function set point3(value:Point):void 		{ 			this._point3.x = value.x; 			this._point3.y = value.y; 			// 			this.reinit(); 		} 		 		/** 		 * Инициализировать цвета вершин треугольника 		 */ 		public function initColors(color1:uint, color2:uint, color3:uint):void 		{ 			this.color1 = color1; 			this.color2 = color2; 			this.color3 = color3; 		} 		 		private var _color1:Number = 0; 		/** 		 * Цвет первой вершины треугольника 		 */ 		public function get color1():uint {return this._color1;} 		public function set color1(value:uint):void 		{ 			this._color1 = value; 			this.data.color1.value = [	((value >>> 16) & 0xff)/255, 										((value >>>  8) & 0xff)/255, 										(value & 0xff)/255]; 		} 		 		private var _color2:Number = 0; 		/** 		 * Цвет второй вершины треугольника 		 */ 		public function get color2():uint {return this._color2;} 		public function set color2(value:uint):void 		{ 			this._color2 = value; 			this.data.color2.value = [	((value >>> 16) & 0xff)/255, 										((value >>>  8) & 0xff)/255, 										(value & 0xff)/255]; 		} 		 		private var _color3:Number = 0; 		/** 		 * Цвет третьей вершины треугольника 		 */ 		public function get color3():uint {return this._color3;} 		public function set color3(value:uint):void 		{ 			this._color3 = value; 			this.data.color3.value = [	((value >>> 16) & 0xff)/255, 										((value >>>  8) & 0xff)/255, 										(value & 0xff)/255]; 		} 	} }

Шейдер g3.pbk

<languageVersion: 1.0;>   kernel TriangleGradient <   namespace : "flashpress.ru";     vendor : "FlashPress.ru";     version : 1;     description : "Triangle Gradient"; > {          parameter float2 a     <         defaultValue:float2(50.0, 50.0);     >;     parameter float2 ah     <         defaultValue:float2(50.0, 200.0);     >;     parameter float2 b     <         defaultValue:float2(50.0, 200.0);     >;     parameter float2 bh     <         defaultValue:float2(125.0, 125.0);     >;     parameter float2 c     <         defaultValue:float2(200.0, 200.0);     >;     parameter float2 ch     <         defaultValue:float2(50.0, 200.0);     >;     parameter float3 color1     <         minValue:float3(0.0, 0.0, 0.0);         maxValue:float3(1.0, 1.0, 1.0);         defaultValue:float3(1.0, 0.0, 0.0);     >;     parameter float3 color2     <         minValue:float3(0.0, 0.0, 0.0);         maxValue:float3(1.0, 1.0, 1.0);         defaultValue:float3(0.0, 1.0, 0.0);     >;     parameter float3 color3     <         minValue:float3(0.0, 0.0, 0.0);         maxValue:float3(1.0, 1.0, 1.0);         defaultValue:float3(0.0, 0.0, 1.0);     >;      input image4 src;     output float4 dst;      void     evaluatePixel() {     //     //     //     //     float2 direction;     float lineA;     float lineB;     float lineC;     float checkd;     float checka;     float2 point;     float2 o = outCoord();     //      // уравнение перпендикуляра опущенного из точки O на прямую a-ah     direction = ah-a;     lineA = -direction.y;     lineB = -direction.x;     lineC = direction.x*o.x + direction.y*o.y;     if (lineA != 0.0) {         point = float2(o.x + 1.0, 0);         point.y = -(lineB*point.x+lineC)/lineA;     } else {         point = float2(o.x, a.y);     }     // пересечение p1-point и b-c     checkd = (point.y-o.y)*(ah.x-a.x) - (point.x-o.x)*(ah.y-a.y);     checka = (point.x-o.x)*(a.y-o.y) - (point.y-o.y)*(a.x-o.x);     // точка пересечения перпендикуляра опущенного из A и прямой BC     float2 aho = float2(0.0, 0.0);     aho.x = a.x + checka*(ah.x-a.x)/checkd;     aho.y = a.y + checka*(ah.y-a.y)/checkd;     //     //     //     // уравнение перпендикуляра опущенного из точки O на прямую b-bh     direction = bh-b;     lineA = -direction.y;     lineB = -direction.x;     lineC = direction.x*o.x + direction.y*o.y;     if (lineA != 0.0) {         point = float2(o.x+1.0, 0.0);         point.y = -(lineB*point.x+lineC)/lineA;     } else {         point = float2(o.x, b.y);     }     // пересечение p1-point и b-c     checkd = (point.y-o.y)*(bh.x-b.x) - (point.x-o.x)*(bh.y-b.y);     checka = (point.x-o.x)*(b.y-o.y) - (point.y-o.y)*(b.x-o.x);     // точка пересечения перпендикуляра опущенного из A и прямой BC     float2 bho = float2(0.0, 0.0);     bho.x = b.x + checka*(bh.x-b.x)/checkd;     bho.y = b.y + checka*(bh.y-b.y)/checkd;     //     //     //     // уравнение перпендикуляра опущенного из точки O на прямую c-ch     direction = ch-c;     lineA = -direction.y;     lineB = -direction.x;     lineC = direction.x*o.x + direction.y*o.y;     if (lineA != 0.0) {         point = float2(o.x+1.0, 0.0);         point.y = -(lineB*point.x+lineC)/lineA;     } else {         point = float2(o.x, c.y);     }     // пересечение p1-point и b-c     checkd = (point.y-o.y)*(ch.x-c.x) - (point.x-o.x)*(ch.y-c.y);     checka = (point.x-o.x)*(c.y-o.y) - (point.y-o.y)*(c.x-o.x);     // точка пересечения перпендикуляра опущенного из A и прямой BC     float2 cho = float2(0.0, 0.0);     cho.x = c.x + checka*(ch.x-c.x)/checkd;     cho.y = c.y + checka*(ch.y-c.y)/checkd;     //     //     //     float pp1 = 1.0-((a.x-aho.x)*(a.x-aho.x)+(a.y-aho.y)*(a.y-aho.y))/((a.x-ah.x)*(a.x-ah.x)+(a.y-ah.y)*(a.y-ah.y));     float pp2 = 1.0-((b.x-bho.x)*(b.x-bho.x)+(b.y-bho.y)*(b.y-bho.y))/((b.x-bh.x)*(b.x-bh.x)+(b.y-bh.y)*(b.y-bh.y));     float pp3 = 1.0-((c.x-cho.x)*(c.x-cho.x)+(c.y-cho.y)*(c.y-cho.y))/((c.x-ch.x)*(c.x-ch.x)+(c.y-ch.y)*(c.y-ch.y));     //     dst = float4(0.0, 0.0, 0.0, 1.0);     dst.r = color1.r*pp1 + color2.r*pp2 + color3.r*pp3;     dst.g = color1.g*pp1 + color2.g*pp2 + color3.g*pp3;     dst.b = color1.b*pp1 + color2.b*pp2 + color3.b*pp3; } } 

ссылка на оригинал статьи http://habrahabr.ru/company/mailru/blog/196104/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *