<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>CTAPbIu_MABP&#039;s BLOG &#187; 2d</title>
	<atom:link href="http://mabp.kiev.ua/tag/2d/feed/" rel="self" type="application/rss+xml" />
	<link>http://mabp.kiev.ua</link>
	<description>энтузиазм = 1/опыт © Старый Мавр</description>
	<lastBuildDate>Sat, 12 May 2012 07:40:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2</generator>
		<item>
		<title>Canvas Overlay</title>
		<link>http://mabp.kiev.ua/2010/11/05/canvas-overlay/</link>
		<comments>http://mabp.kiev.ua/2010/11/05/canvas-overlay/#comments</comments>
		<pubDate>Fri, 05 Nov 2010 14:56:35 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1291</guid>
		<description><![CDATA[Скуки ради решил сделать Canvas Overlay. В основе естественно Raphaël Overlay, но немного переделанный. Если все пойдет хорошо сделаю из них один. На самом деле показывать пока практически нечего. Надо еще сильно подумать над синхронизацией и выставлением интервалов. А пока пример:]]></description>
			<content:encoded><![CDATA[<p>Скуки ради решил сделать Canvas Overlay. В основе естественно <a href="http://mabp.kiev.ua/2010/09/10/raphael-overlay/">Raphaël Overlay</a>, но немного переделанный. Если все пойдет хорошо сделаю из них один.</p>
<span id="more-1291"></span>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/content/source/googlemaps/CanvasOverlay-0.1.js"></script>
<script type="text/javascript" src="/content/js/canvas-overlay.js"></script>

<p>На самом деле показывать пока практически нечего. Надо еще сильно подумать над синхронизацией и выставлением интервалов. А пока пример:</p>



<div id="map" style="width:600px;height:400px;"></div>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/11/05/canvas-overlay/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>gRaphaël overlay</title>
		<link>http://mabp.kiev.ua/2010/10/29/graphael-overlay/</link>
		<comments>http://mabp.kiev.ua/2010/10/29/graphael-overlay/#comments</comments>
		<pubDate>Fri, 29 Oct 2010 20:20:03 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[Raphaël]]></category>
		<category><![CDATA[SVG]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1282</guid>
		<description><![CDATA[Я там давеча обмолвился что не буду больше развивать Raphaël Overlay - спиздел. Итак пролетая версию 0.8 в которой был добавлен логер прилетаем сразу к 0.9 а в ней возможность расовать графики при помощи gRaphaël Пирожки Столбики вертикально Столбики горизонтаьно Линии Точки, правда они еще не очень хорошо работают]]></description>
			<content:encoded><![CDATA[<p>Я там давеча обмолвился что не буду больше развивать <a href="http://mabp.kiev.ua/2010/09/10/raphael-overlay/">Raphaël Overlay</a> - спиздел.</p>

<span id="more-1282"></span>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/content/source/raphael/raphael-1.5.2.js"></script>
<script type="text/javascript" src="/content/source/raphael/g.raphael.js"></script>
<script type="text/javascript" src="/content/source/raphael/g.pie.js"></script>
<script type="text/javascript" src="/content/source/raphael/g.line.js"></script>
<script type="text/javascript" src="/content/source/raphael/g.dot.js"></script>
<script type="text/javascript" src="/content/source/raphael/g.bar.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/RaphaelOverlay-0.9.3.js"></script>
<script type="text/javascript" src="/content/js/graphael-overlay.js"></script>

<p>Итак пролетая версию 0.8 в которой был добавлен логер прилетаем сразу к 0.9 а в ней возможность расовать графики при помощи gRaphaël</p>

<p>Пирожки</p>
<div id="map1" style="width:600px;height:400px;"></div>

<p>Столбики вертикально</p>
<div id="map21" style="width:600px;height:400px;"></div>

<p>Столбики горизонтаьно</p>
<div id="map22" style="width:600px;height:400px;"></div>

<p>Линии</p>
<div id="map3" style="width:600px;height:400px;"></div>

<p>Точки, правда они еще не очень хорошо работают</p>
<div id="map4" style="width:600px;height:400px;"></div>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/10/29/graphael-overlay/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hypotrochoid</title>
		<link>http://mabp.kiev.ua/2010/10/24/hypotrochoid/</link>
		<comments>http://mabp.kiev.ua/2010/10/24/hypotrochoid/#comments</comments>
		<pubDate>Sun, 24 Oct 2010 20:19:11 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[algorithms]]></category>
		<category><![CDATA[canvas]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1276</guid>
		<description><![CDATA[Много чего полезного принес мне конкурс JS1k. Вот и сегодня разбирая одну из работ я приготовил материал для статьи. На самом деле стоило бы дать линк на эту работу но я его не знаю. То есть как, автор приготовил это для конкурса, но линк на работу не дал. В общем по результатам разбора примера, найден [...]]]></description>
			<content:encoded><![CDATA[<p>Много чего полезного принес мне конкурс <a href="http://js1k.com/home" rel="nofollow external">JS1k</a>. Вот и сегодня разбирая одну из работ я приготовил материал для статьи.</p>
<span id="more-1276"></span>

<script src="/content/source/hypotrochoid.js" type="text/javascript"></script> 
<script src="/content/js/hypotrochoid.js" type="text/javascript"></script>

<p>На самом деле стоило бы дать линк на эту работу но я его не знаю. То есть как, автор приготовил <a href="http://mudcu.be/labs/JS1k/BreathingGalaxies.html" rel="nofollow external">это</a> для конкурса, но линк на работу не дал.</p>
<p>В общем по результатам разбора примера, найден проеб. При попытке оптимизировать отрисовку, оптимизация не случилась :)</p>
<p>То есть автор сделал как-то так</p>

<pre><code class="javascript">
setInterval(function(){
	while(25--){
		c.beginPath();
		x = ...
		y = ...
		if(a){
			c.moveTo(a, b);
			c.lineTo(x, y);
		}
		a = x
		b = y
		c.stroke()
	}
}, 16); 
</code></pre>

<p>пытаясь запихнуть в один интервал отрисовку нескольких линий, а надо было выкинуть beginPath и stroke за цикл</p>

<pre><code class="javascript">
setInterval(function(){
	c.beginPath();
	while(25--){
		x = ...
		y = ...
		с[!!a?"lineTo":"moveTo"](x, y);
		a = x
		b = y
	}
	c.stroke()
}, 16)
</code></pre>

<p>Но я не об этом. Я тут приготовил пример на поиграться :)</p>

<div style="margin:auto;width:400px;height:400px;" id="hypotrochoid"></div>
<div style="text-align:center;">
<label for="outerRadius">радиус</label>
<input type="text" name="outerRadius" id="outerRadius" value="0.4005"/><br/>
<label for="">цвет HSL</label>
<input type="text" name="hue" id="hue" value="100"/><br/>
<input type="button" id="apply" value="зафигачить"/>
</div>

<p>Радиус, это дробное число до 0,5. Почему до 0,5? Потому что 0,2 = 0,8 и 0,4 = 0,6. Ну вы поняли они зеркальные. Чтобы звездочка начала крутиться советую добавить 0,005. Наслаждайтесь :)</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/10/24/hypotrochoid/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Raphaël Overlay</title>
		<link>http://mabp.kiev.ua/2010/09/10/raphael-overlay/</link>
		<comments>http://mabp.kiev.ua/2010/09/10/raphael-overlay/#comments</comments>
		<pubDate>Fri, 10 Sep 2010 00:20:16 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[Raphaël]]></category>
		<category><![CDATA[SVG]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1250</guid>
		<description><![CDATA[После недавних экспериментов я решил создать что-то, чем действительно можно пользоваться для рисования по картам. Ссылка для скачивания в самом низу страницы. Начал с примитивов - прямоугольников (картинок) и кругов (эллипсов). Дело совершенно не хитрое перевел координату в точки по карте и умножил сторону (радиус) на два в степени приближение. // рисуем круг из центра [...]]]></description>
			<content:encoded><![CDATA[<p>После <a href="http://mabp.kiev.ua/2010/08/22/google-maps-v3-and-raphael/">недавних</a> <a href="http://mabp.kiev.ua/2010/09/04/google-maps-v3-raphael-overlay/">экспериментов</a> я решил создать что-то, чем действительно можно пользоваться для рисования по картам.</p>

<span id="more-1250"></span>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/content/source/raphael/raphael-1.5.2.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/RaphaelOverlay-0.9.3.js"></script>
<script type="text/javascript" src="/content/js/raphael-overlay.js"></script>

<p>Ссылка для скачивания в самом низу страницы.</p>
<p>Начал с примитивов - прямоугольников (картинок) и кругов (эллипсов). Дело совершенно не хитрое перевел координату в точки по карте и умножил сторону (радиус) на два в степени приближение.</p>

<pre><code class="javascript">

// рисуем круг из центра координат с радиусом

Overlay.prototype.onAdd = function() {
	
	var center = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(0,0)), // центр мира
		worldWidth = this.getProjection().getWorldWidth(); // длина мира
			
	this.div = document.createElement('div'); // наш слой
	this.div.style.border = 'none';
	this.div.style.position = 'absolute';
	this.div.style.overflow = 'visible';
	this.div.style.left = center.x - worldWidth / 2 + 'px'; // от левого края карты
	this.div.style.top = center.y - worldWidth / 2 + 'px'; // от верха карты
	this.div.style.width = worldWidth+'px'; // шириной в карту
	this.div.style.height = worldWidth+'px'; // высотой в карту
	
	// таким образом див полностью перекрывает одну (центральную) секцию карты
	// заметте, карта повторяеться горизонтально 
	// но этот слой лежит только поверх одной секции не перекрывая соседние
	
	this.getPanes().overlayImage.appendChild(this.div); // добавляем слой на карту
	this.canvas = Raphael(this.div); // делаем из слоя холст для рисования
	
};

Overlay.prototype.draw = function() {
	var center = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(0,0)), // центр круга
		worldWidth = this.getProjection().getWorldWidth(), // длина карты
		left = center.x - worldWidth / 2, // край карты
		top = center.y - worldWidth / 2, // край карты
		scale = 1 &lt;&lt; this.getMap().getZoom(), // увеличение ( Math.pow(2, zoom))
		r = scale * radius, // реальный радиус
		x = center.x - left, // координата по слою
		y = center.y - top; // координата по слою
		
		this.canvas.clear(); // чистим холст от старых картинок
		this.canvas.circle(x, y, r); // рисуем круг
		
}

</code></pre>

<p>Вот собственно простейший пример и готов. Но мне же этого мало! А что если круг нарисовать с радиусом больше чем worldWidth/2? Тогда он будет вылазить за рамки слоя и будет обрезан по краям. Но это же не кошерно (или не по фен-шую :) ). Что же делать? Надо расширить слой. Слой расширяется в четырех направлениях так как он выпирает за границы слоя со всех четырех сторон. просто расширить слой не достаточно потому как див перестанет выпирать только справа и снизу, надо еще сместить див влево и вверх, но если сместили див надо поправить координаты круга. Для тех кто не дружит с математикой как я все страшно запутано поэтому показываю на примере.</p>

<pre><code class="javascript">

// r > worldWidth / 2 по условию

Overlay.prototype.draw = function() {
	var center = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(0,0)), // центр круга
		worldWidth = this.getProjection().getWorldWidth(), // длина карты
		left = center.x - worldWidth / 2, // край карты
		top = center.y - worldWidth / 2, // край карты
		scale = 1 &lt;&lt; this.getMap().getZoom(), // увеличение ( Math.pow(2, zoom))
		r = scale * radius, // реальный радиус
		x = center.x - left, // координата по слою
		y = center.y - top, // координата по слою
		
		// отступы
		offsetLeft = r - x,
		offsetTop = r - y,
		offsetRight = r + x - worldWidth,
		offsetBottom = r + y - worldWidth,
		
		// длина и высота учитывая отступы
		fullWorkdWidth = offsetLeft + worldWidth + offsetRight,
		fullWorkdHeight = offsetTop + worldWidth + offsetBottom;
		
		// изменяем положение слоя и размеры холста
		this.div.style.left = left - offsetLeft + 'px';
		this.div.style.top = top - offsetTop + 'px';
		this.div.style.width = fullWorkdWidth + 'px';
		this.div.style.height = fullWorkdHeight + 'px';
		this.canvas.setSize(fullWorkdWidth, fullWorkdHeight);
		
		this.canvas.clear();
		this.canvas.circle(x, y, r); // рисуем круг
		
}

</code></pre>

<p>В результате получаеться где-то такая фигура</p>

<div id="map1" style="width:600px;height:400px;"></div>

<p>Примерно так же рассчитываться прямоугольники, но показывать их просто так не интересно, интересно станет когда фигур на карте будет больше одной и выпирать они будут за края карты с разных сторон и на разную длину. В этом случаи надо сначала просчитать все фигуры, найти максимальные отступы, подвинуть слой, сместить фигуры и только потом что-то рисовать. Пробуем:</p>

<div id="map2" style="width:600px;height:400px;"></div>

<p>Походу с примитивами разобрались, Но кому нужны эти примитивы? Все хотят отмечать на карте разного рода кривые и полигоны (многогранники), что же, нужно и это реализовать. Возьмем пример которым пользуется Dmitry Baranovskiy, автор RaphaelJS - это <a href="http://ru.wikipedia.org/wiki/Лист_Мёбиуса" rel="nofollow external">петля Мёбиуса</a>, знак бесконечности или просто перевернутая восьмерка для людей без воображения. Задаётся она вот такими координатами <span style="color:#107020;font-weight:bold;">M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z</span>. Координаты как вы ведите относительные (это видно из маленьких "с"), но даже если бы они были абсолютными это было бы не страшно. Первое что делаем переводим абсолютные в относительные, потом смещаем первую точку куда нам надо и умножаем все остальные точки по известной формуле.</p>


<pre><code class="javascript">

Overlay.prototype.draw = function() {
	var center = this.getProjection().fromLatLngToDivPixel(new google.maps.LatLng(0,0)), // точка смещения
		worldWidth = this.getProjection().getWorldWidth(), // длина карты
		left = center.x - worldWidth / 2, // край карты
		top = center.y - worldWidth / 2, // край карты
		scale = 1 &lt;&lt; this.getMap().getZoom(), // увеличение ( Math.pow(2, zoom))
		path = Raphael.pathToRelative(Raphael.parsePathString("M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z"));
		line = [];
			
		for (var i in path) {
			for (var j in path[i]) {
				if (j == 0) {
					line[i] = [path[i][j]]; // буква
				} else if (i==0 &#038;& j==1) {
					line[i].push(center.x - left + path[i][j]); // начальная точка по X
				} else if (i==0 &#038;& j==2) {
					line[i].push(center.y - top + path[i][j]); // начальная точка по Y
				} else {
					line[i].push(path[i][j] * scale);  // все остальные точки
				}
			}
		}
		
		this.canvas.clear();
		this.canvas.path(line);
		
}

</code></pre>

<div id="map3" style="width:600px;height:400px;"></div>

<p>Единственный минус рисования кривой (как и текста), это то что пока не нарисуешь не узнаешь размеров, конечно можно долго высчитывать их по координатам но по моему это того не стоит, особенно если координаты относительные. С другой стороны узнать размер можно после рисования, вытереть и сместить все что нужно и нарисовать по новой, но по моему это требует еще больше усилий, хотя формулой для этого я не поделиться не могу.</p>

<pre><code class="javascript">

	// для кривой (рисуеться с левого верхнего края)
	var path = this.canvas.path(line),
		width = path.getBBox().width,
		height = path.getBBox().height,
		x = point.x - left,
		y = point.y - top;
		
	// для текста (рисуеться из центра)
	var text = this.canvas.text(point.x, point.y, text),
		width = text.getBBox().width,
		height = text.getBBox().height,
		x = point.x - width / 2 - left,
		y = point.y - height / 2 - top;

</code></pre>

<p>С основными возможностями и трудностями рисования разобрались переходим к конкретным примерам</p>

<p>Прямоугольники</p>
<p>Есть пять разный вариантов нарисовать прямоугольник</p>
<ul>
<li>Ограничить область на карте с помощью границ (LatLngBounds)</li>
<li>Нарисовать как во всех графических редакторах из верхнего левого угла с привязкой к координате</li>
<li>Нарисовать как круг указав координату центра</li>
<li>Нарисовать от левого верхнего угла в определенной точке на карте, при этом надо знать размер дива с картой и сам прямоугольник не будет менять позиции про движении карты, а уедет за край видимой области</li>
<li>То же что и предыдущий, только из центра</li>
</ul>

<pre><code class="javascript">

	var map = new google.maps.Map(document.getElementById("map"), {
		zoom: 0,
		center: new google.maps.LatLng(0,0),
		mapTypeId: google.maps.MapTypeId.SATELLITE
	});

	new RaphaelOverlay({
		map:map,
		shapes : [{
			// прямоугольник ограниченный парой координат
			// может пропадать при движении карты
			// из-за отрицательной длинны
			position : new google.maps.LatLngBounds(
				// верхний првым угол (north-east)
				new google.maps.LatLng(-50, -100), 
				// нижний левый угол (south-west)
				new google.maps.LatLng(50, 100)
			),
			type: "rect",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#00f', 
				"fill-opacity": 0.3
			}
		},{
			// обычный прямоугольник нарисованный 
			// из верхнего левого угла
			// от координат 
			position : new google.maps.LatLng(85, -175),
			// и имеющий размеры 100x100
			size : new google.maps.Size(100, 100),
			type: "rect",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#0f0', 
				"fill-opacity": 0.3
			}
		},{
			// прямоугольник нарисованный 
			// из центра
			// от координат
			center : new google.maps.LatLng(-85, 175),
			// и имеющий размеры 100x100
			size : new google.maps.Size(100, 100),
			type: "rect",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		},{
			// прямоугольник нарисованный 
			// из верхнего левого угла
			// от верхнего левого угла карты
			// такой прямоугольник не меняет позиции 
			// при движении карты
			position : new google.maps.Point(300, 100),
			size : new google.maps.Size(100, 100),
			type: "rect",
			// скругление углов
			radius: 10,
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#ff0', 
				"fill-opacity": 0.3
			}
		},{
			// такой же как предыдущий, только 
			// из центра 
			center : new google.maps.Point(300, 100),
			size : new google.maps.Size(100, 100),
			type: "rect",
			// скругление углов
			radius: 10,
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f0f', 
				"fill-opacity": 0.3
			}
		}]
	});

</code></pre>

<div id="map4" style="width:600px;height:400px;"></div>

<p>Картинки</p>
<p>По большому счету ничем не отличаются от прямоугольников, кроме того что имеют ссылку на картинку и не имеют скругления углов</p>

<pre><code class="javascript">

	new RaphaelOverlay({
		map:map,
		shapes : [{
			// ссылка на картинку
			src : "http://upload.wikimedia.org/wikipedia/commons/d/d6/Wikipedia-logo-v2-en.png",
			position : new google.maps.LatLng(85, -175),
			size : new google.maps.Size(135, 155),
			type: "image"
		}]
	});

</code></pre>

<div id="map5" style="width:600px;height:400px;"></div>

<p>Круги и овалы</p>

<pre><code class="javascript">

	new RaphaelOverlay({
		map:map,
		shapes : [{
			// у круга центр совпадает с позицией
			// но центр приоритетнее
			center: new google.maps.LatLng(85, 175),
			type: "circle",
			radius: 100,
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#0f0', 
				"fill-opacity": 0.3
			}
		},{
			// как и квадрат круг и овал можно рисовать
			// из фиксированной точки
			position: new google.maps.Point(300, 100),
			type: "circle",
			radius: 100,
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		},{
			// овал отличается от круга двумя радиусами
			position: new google.maps.LatLng(-85, -175),
			type: "ellipse",
			rx: 100,
			ry: 50,
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#00f', 
				"fill-opacity": 0.3
			}
		}]
	});

</code></pre>

<div id="map6" style="width:600px;height:400px;"></div>

<p>Кривые и полигоны</p>

<pre><code class="javascript">

	new RaphaelOverlay({
		map:map,
		shapes : [{
			// Фигура нарисованная по координатам
			// может менять ширину при движении карты
			position : [
				new google.maps.LatLng(84, -175),
				new google.maps.LatLng(84, 175),
				new google.maps.LatLng(0, 0)
			],
			type: "polygon",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		},{
			// Фигура нарисованная по кривой 
			// с абсолютными координатами
			position: new google.maps.LatLng(85, -175),
			path: "M 0 0 L 200 0 L 100 200 z",
			type: "path",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		},{
			// Фигура нарисованная по кривой 
			// с относительными координатами
			position: new google.maps.LatLng(85, -175),
			path: "M100,100c0,50 100-50 100,0c0,50 -100-50 -100,0z",
			type: "path",
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		}]
	});

</code></pre>

<div id="map7" style="width:600px;height:400px;"></div>

<p>Текст</p>
<pre><code class="javascript">

	new RaphaelOverlay({
		map:map,
		shapes : [{
			position: new google.maps.LatLng(0, 0),
			text : "CTAPbIu_MABP",
			type: "text",
			attr : {
				fill: '#fff', 
				"font-size" : 20
			}
		}]
	});

</code></pre>

<div id="map8" style="width:600px;height:400px;"></div>

<p>Похоже это все, забыл только сказать что есть еще настройка приближения. Поскольку во всех примерах приближение поставлено минимальное, то при приближении хотя бы 10 все фигуры распадутся и будет очень некрасиво. Это происходит по разным причинам - кривизна SVG или VML или переполнение int32 в параметре ширины или высоты. Поэтому я сделал поправку на приближение. Первых два пункта интуитивно понятны это минимальное и максимальное приближение на котором видно фигуру, а третий пункт это приближение на котором заданы координаты кривых. То есть фигуру видно с 3 по 5 приближение, но координаты были рассчитаны при нулевом то можно это указать, иначе считается что они были заданы по меньшему из видимых. Смотрите пример с 3 по 5 приближение возле маркера.</p>

<pre><code class="javascript">

	new RaphaelOverlay({
		map:map,
		shapes : [{
			position: new google.maps.LatLng(0, 0),
			path: "M0,0c0,50 100-50 100,0c0,50 -100-50 -100,0z",
			type: "path",
			zoom : {
				min : 3,
				max : 5,
			},
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		},{
			position: new google.maps.LatLng(0, 0),
			path: "M0,0c0,50 100-50 100,0c0,50 -100-50 -100,0z",
			type: "path",
			zoom : {
				min : 3,
				max : 5,
				adjusted : 0
			},
			attr : {
				stroke: '#fff', 
				"stroke-width":2, 
				fill: '#f00', 
				"fill-opacity": 0.3
			}
		}]
	});

</code></pre>

<div id="map9" style="width:600px;height:400px;"></div>

<p> Скачать скрипт можно тут <a href="http://mabp.kiev.ua/content/source/googlemaps/RaphaelOverlay-0.9.3.js">DOWNLOAD LINK</a>. Вот и все, надеюсь будет хоть кому-то полезно!</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/09/10/raphael-overlay/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Google Maps v3 &amp; Raphaël Overlay</title>
		<link>http://mabp.kiev.ua/2010/09/04/google-maps-v3-raphael-overlay/</link>
		<comments>http://mabp.kiev.ua/2010/09/04/google-maps-v3-raphael-overlay/#comments</comments>
		<pubDate>Sat, 04 Sep 2010 21:51:43 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[SVG]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1248</guid>
		<description><![CDATA[Прошлый раз была небольшая заметочка о том как рисовать при помощи Raphaël на гуглокартах. На этот раз будет статья посерьезнее. На этот раз я довел Эвклидову проекцию до ума, и теперь с ней действительно можно что-то рисовать. А так же накодил более менее сносную прокладку между Raphaël и Google Maps, теперь ею можно рисовать. Пожалуй [...]]]></description>
			<content:encoded><![CDATA[<p>Прошлый раз была <a href="http://mabp.kiev.ua/2010/08/22/google-maps-v3-and-raphael/">небольшая заметочка</a> о том как рисовать при помощи Raphaël на гуглокартах. На этот раз будет статья посерьезнее.</p>

<span id="more-1248"></span>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/content/source/raphael/raphael-1.5.2.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/EuclideanProjection.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/CoordMapType.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/RaphaelOverlay-0.1.js"></script>
<script type="text/javascript" src="/content/js/google-maps-v3-raphael-overlay.js"></script>

<p>На этот раз я довел <a href="http://mabp.kiev.ua/2010/08/16/custom-maptype-projection-in-google-map-api-v3/">Эвклидову проекцию</a> до ума, и теперь с ней действительно можно что-то рисовать. А так же накодил более менее сносную прокладку между Raphaël и Google Maps, теперь ею можно рисовать. Пожалуй вот сейчас этим и займемся.</p>

<div id="map" style="width:300px; height:400px;margin:auto;"></div>

<p>Код прокладки еще сырой и выкладывать его пока не буду, чтобы потом по 10 раз не править. Но если кто хочет посмотреть то он может посмотреть его <a href="http://mabp.kiev.ua/content/source/googlemaps/RaphaelOverlay-0.1.js">тут</a>. А я пока прокомментирую. Для того чтобы нарисовать что-либо поверх карты рисуется див во всю ширину и высоту. Да-да вы правильно поняли если взять гуглокарту и приблизить уровень так на десятый, чтобы стало видно города, то ширина дива будет несколько тысяч пикселей. Потом в этом диве Raphaël уже рисует свои фигуры. Передаваемые параметры считаются за первый уровень приближения, дальше масштабируются по простой формуле [width = width * (1 << zoom)]. У меня на практике такой образ отрисовки совсем не тормозил, не смотря на космические цифры (Core2 2.33, RAM 4, FF 3.6.8), расскажите в каментах как быстро рисуеться у вас.</p>

<p>Из видимых недостатков только один - если подвигать карту чуть-чуть вправо-влево то видно что четвертушка круга рисуется то в правом, то в левом верхнем углу. И там и там она рисуется правильно, но еще правильнее было бы ели бы рисовался полный круг выходя за края карты, но поскольку див (соответственно и область рисования svg или vml) имеет ширину и высоту карты, то вылезти за его рамки не представляется возможным и фигура остается усеченной. Надо будет попробовать задавать размеры диву при отрисовке каждой новой фигуры.</p>

<p>А теперь я пожалуй  покажу  как этим добром пользоваться, думаю что при добавлении новых возможностей вид передаваемых данных не измениться</p>

<pre><code class="javascript">
function initialize() {

	// для всяких недобраузеров или отключенного фаербага
	if (!("console" in window)){
		window.console = {log:alert};
	}

	// я использую Эвклидову проекцию но можно брать и обычные карты
	var map = new google.maps.Map(document.getElementById("map"), {
		zoom : 0,
		center : new google.maps.LatLng(0,0),
		mapTypeControlOptions : {
			mapTypeIds: ['myCustomMap']
		}
	});

	map.mapTypes.set('myCustomMap', new CoordMapType());
	map.setMapTypeId('myCustomMap');

	// маркер для того чтобы проверить координату
	var marker = new google.maps.Marker({
		position : new google.maps.LatLng(0, 0),
		map : map,
		draggable : true
	});

	google.maps.event.addListener(marker, 'dragend', function() {
		console.log(this.getPosition());
	});
	
	// а вот и новый слой
	new RaphaelOverlay({
		map : map,
		figures : [{
			position : new google.maps.LatLng(85, 175),
			type : "circle",
			radius : 100,
			attr : {
				"stroke" : "#fff", 
				"stroke-width" : 2, 
				"fill" : '#000', 
				"fill-opacity" : 0.3
			}
		},{
			position: new google.maps.LatLng(85, -175),
			type: "rect",
			width: 100,
			height: 100,
			attr : {
				"stroke" : "#fff", 
				"stroke-width" : 2, 
				"fill" : '#000', 
				"fill-opacity" : 0.3
			}
		},{
			position : new google.maps.LatLng(0, 0),
			type : "ellipse",
			rx : 100,
			ry : 50,
			attr : {
				"stroke" : "#fff", 
				"stroke-width" : 2, 
				"fill" : "#000", 
				"fill-opacity" : 0.3
			}
		},{
			position : new google.maps.LatLng(0, 0),
			src : "http://upload.wikimedia.org/wikipedia/commons/d/d6/Wikipedia-logo-v2-en.png",
			type : "image",
			width : 135,
			height : 155
		},{
			position : new google.maps.LatLng(0, 0),
			text : "CTAPbIu_MABP",
			type : "text",
			attr : {
				"fill" : "#fff", 
				"font-size" : 20
			}
		}]
	});
	
}

google.maps.event.addDomListener(window, 'load', initialize);
</code></pre>

<p>Самое сложное во всем этом было пожалуй пересчет координат из широты-долготы в пиксели по карте, но по моему получилось весьма неплохо. В ближайшее время думаю добавить еще пару функций и довести код до приличного вида.</p>


]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/09/04/google-maps-v3-raphael-overlay/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Прямоугольник с закругленными углами на холсте</title>
		<link>http://mabp.kiev.ua/2010/09/03/rectangle-with-rounded-corners-on-canvas/</link>
		<comments>http://mabp.kiev.ua/2010/09/03/rectangle-with-rounded-corners-on-canvas/#comments</comments>
		<pubDate>Fri, 03 Sep 2010 18:42:11 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[canvas]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1246</guid>
		<description><![CDATA[По моему название не очень звучит? Ну да неважно, в общем это еще один черновик от которого пора избавиться. Встречайте функция для рисования закругленных углов. function roundedRect(ctx,x,y,width,height,radius){ ctx.beginPath(); ctx.moveTo(x,y+radius); ctx.lineTo(x,y+height-radius); ctx.quadraticCurveTo(x,y+height,x+radius,y+height); ctx.lineTo(x+width-radius,y+height); ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius); ctx.lineTo(x+width,y+radius); ctx.quadraticCurveTo(x+width,y,x+width-radius,y); ctx.lineTo(x+radius,y); ctx.quadraticCurveTo(x,y,x,y+radius); ctx.stroke(); } Работает как-то так:]]></description>
			<content:encoded><![CDATA[<p>По моему название не очень звучит? Ну да неважно, в общем это еще один черновик от которого пора избавиться. Встречайте функция для рисования закругленных углов.</p>

<span id="more-1246"></span>

<script type="text/javascript" src="/content/js/rectangle-with-rounded-corners-on-canvas.js"></script>  

<pre><code class="javascript">
function roundedRect(ctx,x,y,width,height,radius){
	ctx.beginPath();
	ctx.moveTo(x,y+radius);
	ctx.lineTo(x,y+height-radius);
	ctx.quadraticCurveTo(x,y+height,x+radius,y+height);
	ctx.lineTo(x+width-radius,y+height);
	ctx.quadraticCurveTo(x+width,y+height,x+width,y+height-radius);
	ctx.lineTo(x+width,y+radius);
	ctx.quadraticCurveTo(x+width,y,x+width-radius,y);
	ctx.lineTo(x+radius,y);
	ctx.quadraticCurveTo(x,y,x,y+radius);
	ctx.stroke();
}
</code></pre>

<p>Работает как-то так:</p>

<div style="text-align:center;">
	<canvas id="canvas" width="150" height="150"></canvas>
</div>

]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/09/03/rectangle-with-rounded-corners-on-canvas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Google Maps v3 &amp; Raphaël</title>
		<link>http://mabp.kiev.ua/2010/08/22/google-maps-v3-and-raphael/</link>
		<comments>http://mabp.kiev.ua/2010/08/22/google-maps-v3-and-raphael/#comments</comments>
		<pubDate>Sun, 22 Aug 2010 13:31:59 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[Raphaël]]></category>
		<category><![CDATA[SVG]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1240</guid>
		<description><![CDATA[Совсем коротенький пост о том как подружить Google Maps v3 и Raphaël Я упомянул в прошлом посте, что я хотел нарисовать десяточку на улыбке Моны Лизы, но из-за стечения обстоятельств так этого и не сделал. И не сделаю сегодня ;) Но покажу куда копать. В третьей (3.1 если быть точнее) версии API появились встроенные объекты, [...]]]></description>
			<content:encoded><![CDATA[<p>Совсем коротенький пост о том как подружить Google Maps v3 и Raphaël</p>

<span id="more-1240"></span>

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="/content/source/raphael/raphael-1.5.2.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/EuclideanProjection.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/CoordMapType.js"></script>
<script type="text/javascript" src="/content/source/googlemaps/RaphaelCircle.js"></script>
<script type="text/javascript" src="/content/js/google-maps-v3-raphael.js"></script>


<p>Я упомянул в прошлом <a href="http://mabp.kiev.ua/2010/08/16/custom-maptype-projection-in-google-map-api-v3/">посте</a>, что я хотел нарисовать десяточку на улыбке Моны Лизы, но из-за стечения обстоятельств так этого и не сделал. И не сделаю сегодня ;) Но покажу куда копать.</p>
<p>В третьей (3.1 если быть точнее) версии API появились встроенные объекты, попробуем их использовать для рисования.</p>

<pre><code class="javascript">
	for (var i=0,j=11;i&ly;j;i++){
		new google.maps.Circle({
			strokeWeight: 2,
			strokeColor : "#ffffff",
			map : map,
			center : new google.maps.LatLng(0, 0),
			radius : i*1000000
		});
	}
</code></pre>

<div id="map1" style="width:300px; height:400px;margin:auto;"></div>

<p><strong>Updated 04.09.10</strong> после небольшой коррекции реализации Эвклидовой проекции, круги перестали правильно рисоваться, так что в этом примере проекция немного подхачена, чтобы вернуть кругу былую квадратность.</p>

<p>В рот мне ноги! Да это же квадратичный круг! Не-не-не Дэвид Блэйн ты нас не проведешь!</p>
<p>Вобщем вы поняли что это лажа и при больших радиусах круг становиться не очень круглым. Хотя идея хороша.</p>
<p>Ну думаю самое время перейти к рисованию кругов при помощи Raphaël</p>

<pre><code class="javascript">
function RaphaelCircle(options) {
	this.options = options;
	this.setMap(options.map);
}

RaphaelCircle.prototype = new google.maps.OverlayView();

RaphaelCircle.prototype.onAdd = function() {
	this.div = document.createElement('DIV');
	this.div.style.border = '0px solid';
	this.div.style.position = 'absolute';
	this.div.style.overflow = 'visible';
	
	this.getPanes().overlayImage.appendChild(this.div);
	this.canvas = Raphael(this.div, 100, 100);
};

RaphaelCircle.prototype.draw = function() {

	var radius = (1 << this.getMap().getZoom()) * this.options.radius,
		center = this.getProjection().fromLatLngToDivPixel(this.options.position);
	this.div.style.left = center.x - radius / 2 + 'px';
	this.div.style.top = center.y - radius / 2 + 'px';
	this.div.style.width = radius + 'px';
	this.div.style.height = radius + 'px';
	this.canvas.setSize(radius, radius);
	if (this.circle) {
		this.circle.remove();
	}
	this.circle = this.canvas.circle(radius / 2, radius / 2, radius / 2)
	this.circle.attr({stroke: '#fff', "stroke-width":2, fill: '#000', "fill-opacity": 0.3});

};

RaphaelCircle.prototype.onRemove = function() {
	this.circle.remove();
	this.div.parentNode.removeChild(this.div);
};
</code></pre>

<p>Если забить на то как я обошёлся с опциями при создании объекта, то в принципе все нормально. Можно запускать!</p>

<div id="map2" style="width:300px; height:400px;margin:auto;"></div>

<p>С таким подходом можно создать новый overlay который будет рисовать не просто круг а сразу всю десяточку. Успехов!</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2010/08/22/google-maps-v3-and-raphael/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Paint</title>
		<link>http://mabp.kiev.ua/2009/12/08/paint/</link>
		<comments>http://mabp.kiev.ua/2009/12/08/paint/#comments</comments>
		<pubDate>Tue, 08 Dec 2009 00:26:55 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[Raphaël]]></category>
		<category><![CDATA[SVG]]></category>
		<category><![CDATA[UI plugin]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1147</guid>
		<description><![CDATA[Научившись работать с Raphaël и пользуясь познаниями из прошлой статьи плагин нужно соединить с colorpicker'ом и datagrid'ом. Долго не гугля я выбрал два кажущиеся мне нормальные плагина и похоже не ошибся. Интеграция прошла быстро и безболезненно :) Задача: есть некая модель данных, которая должна отображаться в двух видах: в табличке в виде данных и на [...]]]></description>
			<content:encoded><![CDATA[<p>Научившись работать с <a href="http://mabp.kiev.ua/2009/12/08/raphael/">Raphaël</a> и пользуясь познаниями из <a href="http://mabp.kiev.ua/2009/12/08/advanced-theory-of-plugin-creation/">прошлой статьи</a> плагин нужно соединить с <a href="http://www.eyecon.ro/colorpicker/" rel="nofollow external">colorpicker'ом</a> и <a href="http://www.trirand.com/blog/" rel="nofollow external">datagrid'ом</a>. Долго не гугля я выбрал два кажущиеся мне нормальные плагина и похоже не ошибся. Интеграция прошла быстро и безболезненно :)</p>
<span id="more-1147"></span>
<p>Задача: есть некая модель данных, которая должна отображаться в двух видах: в табличке в виде данных и на картинке ввиде линии. Линии можно выделять при этом реагирует табличка подсвечивая выбранный ряд, соответственно и наоборот если выбрать ряд в табличке надо подсветить линию на рисунке, линии можно удалять, удаляя данные и из таблички, и добавлять добавляя пустой ряд в табличку. Так вроде все описал, на всякий случай еще приделал к этому цветовую палитру, еще наверное надо будет толщину линии выбирать разрешить, но это просто.</p>

<p>Модель данных взята от фонаря три шага налево...</p>

<pre><code class="javascript">
var model = [
{id : 0, amount : 100, units : "meters", value : 13, color : "#00f", path : "M 250 73 L 250 73"},
{id : 1, amount : 200, units : "meters", value : 22, color : "#0f0", path : "M 327 67 L 327 67"},
{id : 2, amount : 300, units : "meters", value : 36, color : "#f00", path : "M 185 172 L 185"},
{id : 3, amount : 400, units : "meters", value : 18, color : "#00f", path : "M 216 231 L 216"},
{id : 4, amount : 150, units : "meters", value : 23, color : "#f00", path : "M 274 171 L 274 171"},
{id : 5, amount : 360, units : "liters", value : 36, color : "#00f", path : "M 308 176 L 308 176"},
{id : 6, amount : 170, units : "liters", value : 12, color : "#0f0", path : "M 358 113 L 358 113"},
{id : 7, amount : 180, units : "liters", value : 24, color : "#f00", path : "M 201 109 L 201 109"},
{id : 8, amount : 292, units : "liters", value : 37, color : "#00f", path : "M 228 114 L 228 114"},
{id : 9, amount : 182, units : "kgrams", value : 43, color : "#0f0", path : "M 357 245 L 357 245"},
{id : 10,amount : 273, units : "kgrams", value : 53, color : "#f00", path : "M 346 172 L 346 172"},
{id : 11,amount : 164, units : "kgrams", value : 61, color : "#00f", path : "M 386 179 L 386 179"},
{id : 12,amount : 254, units : "kgrams", value : 27, color : "#0f0", path : "M 218 178 L 218 178"}
];
</code></pre>

<p>Загадочная надпись M 250 73 L 250 73 это начало линии в <a href="http://www.w3.org/TR/SVG/paths.html#PathData" rel="nofollow external">формате SVG</a>, дословно она означает Move To 250 px OX 73 px OY Line To 250 px OX 73 px OY, если в конце написать "z" то начало и конец линии замкнуться. В примере я укоротил запись в исходниках все по честному.</p>

<p>Теперь это все надо отрендерить, для этого я написал следующий код, который вы можете использовать как есть, но использовать пока не советую из-за крайней сырости, но за найденные баги буду очень благодарен</p>

<pre><code class="javascript">
(function($) {

    /**
     * author: CTAPbIu_MABP
     * email: ctapbiumabp@gmail.com
     * site: http://mabp.kiev.ua/
     * last update: 08.12.2009
     * version: 0.2
     */

    var Paint = function (node, options) {
        $.extend(this, options);
        this.init(node);
    };


    Paint.prototype = {

        // private
        raphael : null,
        isStarted : false,
        node : null,
        offset : null,

        // public
        color : "#f00",
        defaultColor : "#000",

        // callbacks
        onInit : function() {
        },
        onLineClick : function() {
        },
        onLineDblClick : function() {
        },
        onLineDelete : function() {
        },
        onStart : function() {
        },
        onStop : function() {
        },
        onMove : function() {
        },

        init : function (raw_img) {

            var img = $(raw_img).hide(),
                    self = this,
                    width = img.width(),
                    height = img.height();

            this.node = img.wrap("&lt;div/&gt;").parent().addClass("plot").css({width:width,height:height});
            this.raphael = new Raphael(this.node.get(0), width, height);
            this.raphael.image(img.attr("src"), 0, 0, width, height);

            this.unselectable(this.node.get(0));
            this.node.mouseover(function() {
                //this.style.cursor = 'pointer';
                //$(this).css({'cursor':'url("pencil.cur")'});
            }).mousedown(function(e) {
                self.start(e);
            }).mouseup(function(e) {
                self.stop(e);
            }).mousemove(function(e) {
                self.move(e);
            });

            this.node.bind("drawLine setColor deleteLine", function(e, param) {
                self[e.type](param);
            });

            var model = this.model;
            this.model = [];

            for (var i = 0,j = model.length; i &lt; j; i++) {
                this.setColor(model[i].color);
                this.drawLine(model[i].path);
            }

            this.onInit.apply(this, [raw_img]);
        },

        setColor : function(color) {
            this.color = /^#([a-f0-9]{3}){1,2}$/i.test(color) ? color : this.defaultColor;
        },

        start: function(e) {
            //console.log("start");
            e.preventDefault(); // prevent drag image
            if (this.isStarted)
                return;

            this.offset = this.node.offset();

            this.model.push({
                line : "M " + (e.pageX - this.offset.left) + " " + (e.pageY - this.offset.top),
                color : this.color,
                path : null
            });

            this.isStarted = true;
            this.onStart.apply(this, [e]);
        },

        move: function(e) {
            //console.log("move");
            if (!this.isStarted) return;

            var model = this.model.pop();
            model.path &#038;& model.path.remove();
            delete model.path,model;

            this.drawLine(model.line + " L " + (e.pageX - this.offset.left) + " " + (e.pageY - this.offset.top));
            this.onMove.apply(this, [e]);
        },

        stop: function(e) {
            //console.log("finish");
            this.isStarted = false;

            var model = this.model.pop();
            e.isClick = !model.path; // was it line or just click on image

            model.path &#038;& this.model.push(model) || delete model;

            this.onStop.apply(this, [e]);
        },

        drawLine : function(line) {
            var path = this.raphael.path(line)
                    .attr({stroke:this.color,'stroke-width':5,'stroke-linejoin':'round'});

            var self = this;

            $(path.node)
                .click((function(c) {return function() {self.onLineClick.apply(self, [c]);}})(self.model.length))
                .dblclick((function(c) {return function() {self.onLineDblClick.apply(self, [c]);}})(self.model.length))
                .mouseover(function() {this.style.cursor = 'crosshair';});

            this.model.push({line:line,path:path,color:this.color});
        },

        deleteLine : function(index) {
            var model = this.model[index];
            model.path &#038;& model.path.remove();
            delete model.path, model;

            this.onLineDelete.apply(this, [index])
        },

        unselectable: function(element) {
            // http://ajaxcookbook.org/disable-text-selection/
            element.onselectstart = function() {
                return false;
            };
            element.unselectable = "on";
            element.style.MozUserSelect = "none";
        }
    };


    $.fn.paint = function(options) {
        this.each(function() {
            new Paint(this, options);
        });
        return this;
    };

})(jQuery);
</code></pre>

<p>Простите, но комментариев к коду пока нет, будут в следующей версии, наверное, сильно не обольщайтесь. Да и скачать этот код можно <a href="http://mabp.kiev.ua/content/source/paint/jquery.paint.js">отсюда</a>.</p>

<p>Теперь пора показать как это все собрать в кучу, запускаю плагин.</p>

<pre><code class="javascript">
$("#plot").paint({
    model : model,
    // текущий подсвеченный элемент
    hightlited : null, // it may be zero
    hightlitedColor : "#fff",

    // обработчик двойного клика на линию
    // удаляет подсветку
    // удаляет линию с рисунка
    // удаляет ряд из таблицы
    onLineDblClick : function(index){
        this.hightlited = null;
        this.deleteLine(index);
        $("#grid").jqGrid('delRowData',index+1); 
    },

    // обработчик клика на линию
    onLineClick : function (index) {
        // снимает прошлуб подсветку если была
        if (this.hightlited != null){
            var prev = this.model[this.hightlited];
            prev.path.attr({stroke:prev.color});
        }

        // добавляет новую
        this.model[index].path.attr({stroke:this.hightlitedColor});
        // подсвечивает ряд в таблице
        $("#grid").jqGrid('setSelection',index+1);
        this.hightlited = index;
    },

    // call-back на событие остановки рисования линии
    onStop : function(e){
        // удаляет старую подсветку если была
        if (this.hightlited != null){
            var prev = this.model[this.hightlited];
            prev.path.attr({stroke:prev.color});
        }

        // если это не случайный клик
        if (!e.isClick){
            // подсвечивает свежесозданную линию
            var index = this.model.length-1;
            this.model[index].path.attr({stroke:"#fff"});
            this.hightlited = index;
            // добавляет ряд в таблицу
            $("#grid").jqGrid('addRowData',index+1,this.model[index]);
        }
    },

    // пока не забыл надо в самом начале определить 
    // событие которое будет вызвано когда в таблице
    // поменяется выделенная строка
    onInit : function(){
        var self = this;
        this.node.bind("hightliteLine", function(e, index) {
            // убирает старую подсветку
            if (self.hightlited != null){
                var prev = self.model[self.hightlited];
                prev.path.attr({stroke:prev.color});
            }

            // подсвечивает новую линию
            self.model[index].path.attr({stroke:self.hightlitedColor});
            self.hightlited = index;
        });
    }
});
</code></pre>

<p>Теперь надо привязать выбор цвета, для этого создается небольшая функция инициализирующая colorpicker, когда colorpicker меняет цвет срабатывает событие у рисовалки, и она меняет цвет линии.</p>

<pre><code class="javascript">
$('#colorpicker').ColorPicker({
    flat: true,
    // начальный цвет
    color: '#00ff00',
    // вся магия тут
    onChange: function(hsb, hex, rgb) {
        $("#plot").trigger("setColor","#"+hex);
    }
});
</code></pre>

<p>Еще немного магии для инициализации data grid'а :</p>

<pre><code class="javascript">
$("#grid").jqGrid({
    datatype: "local",
    height: 250,
    colNames:['No','Amount','Units','Value'],
    colModel:[
        {name:'id',index:'id',width:100, sorttype:"int"},
        {name:'amount',index:'amount', width:100, sorttype:"int", align:"right"},
        {name:'units',index:'units', width:100, sorttype:"float"},
        {name:'value',index:'value', width:100, sorttype:"int", align:"right"}
    ],
    caption: "Lines Data",
    onSelectRow : function (id){
        $("#plot").trigger("hightliteLine", id-1);
    }
});

for(var i=0, j=model.length;i<=j;i++)
    $("#grid").jqGrid('addRowData',i+1,model[i]);
</code></pre>

<p>Тут, помимо общих настроек и установки модели, есть чудо метод который вызовет событие подсветки линии когда измениться текущий ряд в таблице.</p>

    <link rel="stylesheet" href="/content/source/paint/colorpicker.css" type="text/css" />
    <link rel="stylesheet" type="text/css" media="screen" href="/content/source/jqgrid/css/ui.jqgrid.css" />
    <style type="text/css">
        .plot {
            cursor: url("/content/source/paint/pencil.gif"), url("/content/source/paint/pencil.cur"), auto;
        }
        #gbox_grid {
            line-height:100%;
        }
        #gview_grid table{
            margin : 0px;
        }
        #gview_grid .ui-jqgrid-hbox {
            padding : 0px !important;
        }
    </style>

    <script src="/content/source/paint/jquery.paint.js" type="text/javascript"></script>
    <script src="/content/source/paint/colorpicker.js" type="text/javascript"></script>

    <script src="/content/source/jqgrid/js/i18n/grid.locale-ru.js" type="text/javascript"></script>
    <script src="/content/source/jqgrid/js/jquery.jqGrid.min.js" type="text/javascript"></script>

    <script src="/content/source/raphael/raphael-1.5.2.js" type="text/javascript"></script>
    <script src="/content/source/paint/jquery.paint.example.js" type="text/javascript"></script>

<img id="plot" src="/content/source/face.png" width="400" height="400" alt="face"/>
<div id="colorpicker"></div>
<table id="grid"></table>

<p>Я думаю, я в ближайшее время не заброшу этот плагин так что ждите новую версию, возможно даже с документацией, пишите свои каменты, не забывайте читать мои бредни ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/12/08/paint/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Raphaël</title>
		<link>http://mabp.kiev.ua/2009/12/08/raphael/</link>
		<comments>http://mabp.kiev.ua/2009/12/08/raphael/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 22:27:46 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[hate]]></category>
		<category><![CDATA[Raphaël]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1145</guid>
		<description><![CDATA[Предисловие: мне поднакопилось что сказать, так что если я не успею высказать все мысли за одну статью, а так скорее всего и будет, то разбор примера по косточкам будет в следующей статье. Я использовал достаточно много сторонних библиотек для построения примера, думаю что все обсуждать не буду но обо всех скажу хоть слово. Дело началось [...]]]></description>
			<content:encoded><![CDATA[<p>Предисловие: мне поднакопилось что сказать, так что если я не успею высказать все мысли за одну статью, а так скорее всего и будет, то разбор примера по косточкам будет в <a href="http://mabp.kiev.ua/2009/12/08/advanced-theory-of-plugin-creation/">следующей статье</a>. Я использовал достаточно много сторонних библиотек для построения примера, думаю что все обсуждать не буду но обо всех скажу хоть слово.</p>
<span id="more-1145"></span>

<p>Дело началось не так давно, когда заказчик высказал идею что на сайте должен быть виджет позволяющий рисовать по верх картинки линии как в MS Paint. Более того линии которые ты нарисовал, нужно выделить, и в табличке рядом заполнить дополнительные параметры. У меня перед глазами пронеслись все <del>прожитые годы</del> библиотеки векторной и растровой графики для <a href="http://mabp.kiev.ua/category/programming/javascript/">javascript</a>. Сначала я думал что я буду использовать что-то на основе <a href="http://mabp.kiev.ua/tag/canvas/">canvas</a>, хорошо хоть опыт уже есть, но потом понял что в голове крепко засела мысль о Raphaël. Ну и хорошо, я давно хотел с ней (библиотекой) поработать, но не было реального примера.</p>

<p>В двух словах <a href="http://raphaeljs.com/" rel="nofollow external">Raphaël</a> - это библиотека которая умеет рисовать векторную графику в формате SVG во всех нормальных браузерах, в остальных она умеет рисовать в формате VML. Как она умеет это делать меня интересует мало, главное что она предоставляет более менее удобное и совершенно недокументированное API. Почему я говорю недокументированное?! Потому что глядя на <a href="http://raphaeljs.com/reference.html" rel="nofollow external">официальный мануал</a> я чуть не заплакал, там <del>нихуя нет</del> ничего нельзя понять. Ну я решил раз в мануале нет, пох, я тут код <a href="http://mabp.kiev.ua/tag/jquery/">jQuery</a> читаю нормально, что мне какой-то Raphaël, но был сильно не прав потому как в коде творится <del>полный пиздец</del> кромешный ужас летящий на крыльях ночи.</p>

<p>Нет, я прекрасно понимаю когда кто-то считает координаты <a href="http://ru.wikipedia.org/wiki/Кривая_Безье" rel="nofollow external">кривой Безье</a> второго порядка он должен писать нечто вроде</p>

<pre><code class="javascript">
Math.sqrt(Math.abs((rx2 * ry2 - rx2 * y * y - ry2 * x * x) / (rx2 * y * y + ry2 * x * x)))
</code></pre>

<p>Тут даже можно догадаться что rx2 это произведение R (радиуса) на X (координаты по иксу) в квадрате</p>

<p>Я могу понять регулярное выражение которое проверяет валидность записи цвета в трех форматах (hex, rgb, <a href="http://ru.wikipedia.org/wiki/HSV_(цветовая_модель)" rel="nofollow external">hsb и hsl</a> можно считать как один)</p>

<pre><code class="javascript">
/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i
</code></pre>

<p>Но, блядь, я не понимаю каким надо быть <a href="http://tema.livejournal.com/481967.html" rel="nofollow external">мудОфилом</a>, что бы писать вот так:</p>

<pre><code class="javascript">
path = ["C"][concat](a2c[apply](0, [d.x, d.y][concat](path.slice(1))))
now = [+from[attr][0] + pos * ms * diff[attr][0], +from[attr][1] + pos * ms * diff[attr][1], (2 in to[attr] ? to[attr][2] : E), (3 in to[attr] ? to[attr][3] : E)][join](S)
</code></pre>

<p>Я для особо "тупых" переведу: вот эти [concat] и [apply] это всего лишь вызов соответствующих методов, но почему же, блядь, не [slice] ?</p>

<pre><code class="javascript">
["C"].concat(a2c.apply(0, [d.x, d.y].concat(path.slice(1))))
</code></pre>

<p>а вот эти "2 in to[attr]" это ничто иное как</p>

<pre><code class="javascript">
if(to[attr] &#038;& to[attr][2])
	return to[attr][2]
else 
	return E // E = 2.71
</code></pre>

<p>Тут возникает вопрос почему так сложно, потому что если to[attr] == null, то to[attr][2] вызовет ошибку и прервет исполнение функции. С таким подходом код становиться нечитабельным и плохо сопровождаемым, но я понимаю что <a href="http://dmitry.baranovskiy.com/" rel="nofollow external">Дмитрия Барановского</a> все устраивает, поэтому он пишет еще такое:</p>

<pre><code class="javascript">
~~double
</code></pre>

<p>Оператор ~~ приводит double или строку вида "3.14" к integer, то есть к целому, то есть к 3.</p>

<pre><code class="javascript">
javascript:(function(){alert(~~(""+Math.PI))})()
</code></pre>

<p>Ну и последние - это сокращенные записи if-else, но не такие ?: а такие &#038;&|| , хотя насмотревшись на его код я и сам пару раз такое использовал</p>

<pre><code class="javascript">
if(c) b = c else b = d;
b = c ? c : d;
b = c &#038;& c || d;
</code></pre>

<p>На чём я закончил, перед тем как отвлечься на порыв ненависти к разработчику Raphaël?! Ах да я рассказывал <del>какая это классная библиотека</del> как искал описание API. После бесполезного чтения мануала и безрезультатного просмотра исходников я открыл примеры и начал заниматься реверс инженерингом, та еще радость, но это единственное что помогло. Потом еще в гугле нашелся удачный <a href="http://jeremyslade.com/2008/12/implementing-a-ruler-with-raphael-and-jquery/" rel="nofollow external">пример</a> который дал мне толчок для написания своего плагина.</p>

<p>Дальше больше...</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/12/08/raphael/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Квадрат Мавревича</title>
		<link>http://mabp.kiev.ua/2009/07/10/square-mavrevicha/</link>
		<comments>http://mabp.kiev.ua/2009/07/10/square-mavrevicha/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 13:31:57 +0000</pubDate>
		<dc:creator>CTAPbIu_MABP</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[2d]]></category>
		<category><![CDATA[canvas]]></category>

		<guid isPermaLink="false">http://mabp.kiev.ua/?p=1085</guid>
		<description><![CDATA[В школе терпеть не мог математику, особенно тригонометрию. И вот те на - вырос и понял что неплохо было бы перечитать учебник за 7-8-9 (какой то там) класс. А все почему потому что в течении 4 часов не мог развернуть несчастный 4-х угольник на 30 градусов. В оригинале конечно был квадрат, но то что я [...]]]></description>
			<content:encoded><![CDATA[<p>В школе терпеть не мог математику, особенно тригонометрию. И вот те на -  вырос и понял что неплохо было бы перечитать учебник за 7-8-9 (какой то там) класс. А все почему потому что в течении 4 часов не мог развернуть несчастный 4-х угольник на 30 градусов. В оригинале конечно был квадрат, но то что я из него делал можно с натяжкой назвать даже 4-х угольником. Я его даже пару раз в бантик завязал.</p>

<span id="more-1085"></span>

<script type="text/javascript" src="/content/source/excanvas.js"></script>
<script type="text/javascript" src="/content/js/square-of-mavrevich.js"></script>

<p>А началось все с того что я увидел имитацию <a href="http://www.benjoffe.com/code/demos/canvascape/" rel="nofollow external">Counter Strike</a> нарисованную при помощи векторной графике и <a href="http://mabp.kiev.ua/tag/javascript/">javascript</a> в canvas. Я решил написать нечто подобное. Но с чего начать? Я решил нарисовать кубик который можно вращать во все стороны. До кубика дело не дошло. Проблемы возникли еще с первой стороной. Блядская математика. В общем как я уже говорил, я пытался повернуть квадрат хоть как-нибудь, хоть куда-нибудь целых 4 часа. Более того не сумел найти ни одно учебника по 2D графике в котором бы внятно описывалось как создавать псевдо-трехмерные объекты, с формулами. Ближе к часу ночи вспомнил про книгу "Как самому создать трехмерную игру", вспомнил громким матом. Выругался и полез искать учебник по математике для школы, нашел <a href="http://www.mathematics.ru/" rel="nofollow external">mathematics.ru</a>, стало немного проще, но все равно квадратик развернулся только в два ночи, когда я синусы, косинусы, плюсы и минусы расставлял почти не думая, пытаясь забрутфорсить формулу.</p>
<p>Вот такая хоботня у меня получилась: красным цветом исходный квадрат, черным - повернутый.</p>

<div style="text-align:center;"><canvas  id="sqrt" width="400" height="400"/></div>

<p>Рисуется это вот таким мегаламерским кодом засунутым в онлоад, ничего я скоро исправлюсь.</p>

<pre><code class="javascript">
	var plot = document.getElementById("sqrt").getContext("2d"),
	theta = 30 * Math.PI / 180,
	delta = [Math.cos(theta),Math.sin(theta)];
	radius = 100 * Math.sqrt(2);

	// Горизонтальные полосы
	plot.fillRect(0,100,400,1)
	plot.fillRect(0,200,400,1)
	plot.fillRect(0,300,400,1)
	
	// Вертикальные полосы
	plot.fillRect(100,0,1,400)
	plot.fillRect(200,0,1,400)
	plot.fillRect(300,0,1,400)
	
	// Красный квадрат
	plot.beginPath();
	plot.fillStyle = "red";
	plot.moveTo(100, 100);
	plot.lineTo(100, 300);
	plot.lineTo(300, 300);
	plot.lineTo(300, 100);
	plot.closePath();
	plot.fill();
	
	// черный квадрат Малевича
	plot.beginPath();
	plot.fillStyle = "black";
	plot.moveTo(200 - radius * delta[1], 200 - radius * delta[0]);
	plot.lineTo(200 + radius * delta[0], 200 - radius * delta[1]);
	plot.lineTo(200 + radius * delta[1], 200 + radius * delta[0]);
	plot.lineTo(200 - radius * delta[0], 200 + radius * delta[1]);
	plot.closePath();
	plot.fill();
</code></pre>

<p>Спонсор картинки в <a href="http://mabp.kiev.ua/tag/ie/">IE 6 и 7</a> - Google co своим скриптом <a href="http://code.google.com/p/explorercanvas/" rel="nofollow external">explorercanvas</a>, <a href="http://mabp.kiev.ua/tag/ie/">IE 8</a> идет на хуй.</p>
<p>Вывод из этой басни такой: надо было учить математику в школе, а потом появляются посты такие как этот и <a href="http://mabp.kiev.ua/2009/01/27/markovs-chains/">прошлый</a>.</p>]]></content:encoded>
			<wfw:commentRss>http://mabp.kiev.ua/2009/07/10/square-mavrevicha/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>

