четверг, 23 февраля 2012 г.

FLEX: основы работы с веб-камерой

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

Сам по себе процесс подключения к камере тривиален. Добавляем компонент spark.components.VideoDisplay:
<s:VideoDisplay id="videoDisplay"
creationComplete="videoDisplay_creationComplete();"
x="40"
y="44"
width="320"
height="240"
/>

и цепляем в скрипте к нему камеру:
private var camera:Camera = null;

private function videoDisplay_creationComplete():void
{
camera = Camera.getCamera();
if (!camera)
return;
camera.setQuality(0,100);
var localCam:Video = new Video(320,240);
videoDisplay.addChild(localCam);
localCam.attachCamera(camera);
}


Правда, на этом дело не окончилось. Аппетит, пришедший во время еды, потребовал добавить возможность делать снапшоты. Это оказалось тоже несложно. Добавляем кнопку:
<s:Button id="button"
label="Сделать фотографию"
click="videoDisplay_snapShot();"
/>

список, в которм будем показывать снимки:
<s:List id="mylist" horizontalCenter="1" width="100%" height="77" itemRenderer="SnapshotRenderer">
<s:dataProvider>
<mx:ArrayList/>
</s:dataProvider>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle"/>
</s:layout>
</s:List>

и в обработчике нажатия кнопки пишем следующее:
private function videoDisplay_snapShot():void
{
if (!camera)
return;
if (camera.muted)
return;
try
{
var snapshot:BitmapData = ImageSnapshot.captureBitmapData(videoDisplay);
var bitmap:Bitmap = new Bitmap(snapshot);
if (3 < mylist.dataProvider.length)
mylist.dataProvider.removeItemAt(0);
mylist.dataProvider.addItem(bitmap);
}
catch (error:*)
{
Alert.show(error.message);
}
}


В окончательном виде получился вот такой миленький проект:

1. Файл SnapshotRenderer.mxml - отвечает за отрисовку снимков экрана

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="100" height="75">

<mx:Image source="{this.data}" width="100%" height="100%"/>

</s:ItemRenderer>


2. Файл main.mxml - основной файл проекта
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
frameRate="100"
creationComplete="init()"
>

<s:layout>
<s:VerticalLayout horizontalAlign="center" verticalAlign="middle" paddingLeft="5" paddingRight="5" paddingTop="5" paddingBottom="5"/>
</s:layout>

<s:Group>

<!-- рисуем рамку вокруг области с видеоизображением с камеры -->
<s:Image source="@Embed(source='frame.jpg')"/>

<!-- область с видеоизображением с камеры -->
<s:VideoDisplay id="videoDisplay"
x="40"
y="44"
width="320"
height="240"
/>
</s:Group>

<s:Button id="button"
label="Сделать фотографию"
click="videoDisplay_snapShot();"
/>

<!-- галерея снимков, присланных с сервера -->
<s:List id="mylist" horizontalCenter="1" width="100%" height="77" itemRenderer="SnapshotRenderer">
<s:dataProvider>
<mx:ArrayList/>
</s:dataProvider>
<s:layout>
<s:HorizontalLayout horizontalAlign="center" verticalAlign="middle"/>
</s:layout>
</s:List>

<fx:Script>
<![CDATA[
import mx.graphics.ImageSnapshot;
import mx.graphics.codec.JPEGEncoder;
import mx.utils.Base64Encoder;
import mx.controls.Alert;

private var camera:Camera = null;

// функция, подключающая камеру
private function videoDisplay_creationComplete():void
{
try
{
// в этот момент на экран выскочит окно с предупреждением безопасности
// если пользователь откажется от подключения камеры, это можно будет потом узнать,
// проверив свойство camera.muted
camera = Camera.getCamera();

if (camera)
{
camera.setQuality(0,100);
var localCam:Video = new Video(320,240);

// маленькая хитрость, чтобы получить зеркальное отображение видео с камеры
var WIDTH:int = localCam.width;
var ma:Matrix=new Matrix();
ma.a = -1;
ma.tx = WIDTH;
localCam.transform.matrix = ma;

videoDisplay.addChild(localCam);
localCam.attachCamera(camera);
}
else
{
Alert.show("Нет доступа к веб-камере");
}
}
catch(error:*)
{
Alert.show(error.message);
}
}

// класс, служащий для передачи снимка экрана на сервер и получения в ответ нескольких
// последних изображений - для отображения в галерее
private var http:HTTPServiceEngine = new HTTPServiceEngine();

private function videoDisplay_snapShot():void
{
// проверка наличия камеры
if (!camera)
return;

// проверка, не отказался ли пользователь подключить камеру
if (camera.muted)
return;

try
{
var snapshot:BitmapData = ImageSnapshot.captureBitmapData(videoDisplay);

// кодируем снимок экрана в base64 и отправляем его на сервер
var jpg:JPEGEncoder = new JPEGEncoder();
var ba:ByteArray = jpg.encode(snapshot);
var b64encoder:Base64Encoder = new Base64Encoder();
b64encoder.encodeBytes(ba);
var b64String:String = b64encoder.flush();
http.SendSnapshot(b64String);
}
catch (error:*)
{
Alert.show(error.message);
}
}

// А это нужно вот для чего. С сервера в качестве ответа на посланный снимок экрана
// возвращаются несколько последних сохраненных снимков. Их нужно превратить
// из byteArray в bitmap и последовательно добавить в компонент mylist.
// Для этой цели и служит loader. А так как он работает асинхронно, то заводим счетчик -
// чтобы хранить, какое по порядку изображение этим loader-ом обрабатывается.
private var loader : Loader = new Loader();
private var loaderCounter:int = 0;

// эта функция вызывается после получения и обработки ответа от сервера и запускает процесс
// добавления присланных изображений в mylist
public function refreshImages():void
{
mylist.dataProvider.removeAll();
loaderCounter = http.Images.length-1;
if (0 <= loaderCounter)
loader.loadBytes(http.Images[loaderCounter]);
}

// обработчик события COMPLETE для loader-а. Его вызов происходит,
// когда loader получил все данные и готов выдать bitmap
private function getBitmapData(e:Event):void
{
try
{
var loader:Loader = Loader(e.target.loader);
var bitmap:Bitmap = Bitmap(loader.content);
mylist.dataProvider.addItem(bitmap);

loaderCounter--;
if (0 <= loaderCounter)
loader.loadBytes(http.Images[loaderCounter]);
}
catch (error:*)
{
Alert.show(error.message);
}
}

// Функция, вызываемая по завершении загрузки проекта.
// Присваиваем loader-у обработчик и подключаемся к веб-камере
private function init():void
{
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, getBitmapData);
videoDisplay_creationComplete();
}

]]>
</fx:Script>

</s:Application>


3. Файл HTTPServiceEngine.as - что зря пропадать снапшотам, пусть складируются на сервере 8-) - класс для приема-передачи изображений через AJAX.
package
{
import mx.rpc.http.HTTPService;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;

import mx.core.FlexGlobals;
import mx.controls.Alert;

import mx.utils.Base64Decoder;

public class HTTPServiceEngine
{
private static var HTTPServiceName :String = "http://myserver/ajax.py";

private var httpService :HTTPService = null;

// буфер для хранения изображений, присланных с сервера, в виде байтовых массивов
public var Images :Array = new Array();

// конструктор
public function HTTPServiceEngine()
{
httpService = new HTTPService();
httpService.url = HTTPServiceEngine.HTTPServiceName;
httpService.method = "POST";
httpService.addEventListener("result", HTTPResult);
httpService.addEventListener("fault", HTTPFault);
}


// отправляем на сервер снимок и ожидаем в ответ список последних изображений
public function SendSnapshot(v:String):void
{
var parameters:Object = {};
parameters["r"] = "snapshot";
parameters["s"] = v;
httpService.send(parameters);
}


// сервер должен вернуть список последних изображений в таком формате:
// [file]
// изображение 1, закодированное в base64
// [file]
// изображение 2, закодированное в base64
// и т.п.
private function HTTPResult(event:ResultEvent):void
{
var result:String = String(event.result);
var buffer:String = "";

// готовим декодер
var b64decoder:Base64Decoder = new Base64Decoder();

// очищаем буфер для хранения полученных с сервера снимков
Images.length = 0;

// парсим ответ
var lines:Array = result.split("\n");
for (var i:int = 0; i < lines.length; i++)
{
if (lines[i] == "[file]")
{
if (buffer != "")
{
b64decoder.decode(buffer);
Images.push(b64decoder.toByteArray());
}
buffer = "";
}
else
buffer += lines[i];
}
if (buffer != "")
{
b64decoder.decode(buffer);
Images.push(b64decoder.toByteArray());
}

// отображаем полученные изображения на экране
FlexGlobals.topLevelApplication.refreshImages();
}

// обработчик ошибки взаимодействия с сервером
public function HTTPFault(event:FaultEvent):void
{
var faultstring:String = event.fault.faultString;
Alert.show(faultstring);
}
}
}


Ну и, наконец, собственно результат:



четверг, 16 февраля 2012 г.

Фишки Windows

Q: Где посмотреть путь до рисунка рабочего стола?
A: HKEY_CURRENT_USER\Control Panel\Desktop

Q: Как изменить права пользователя на доступ к папке из командной строки?
A: Воспользоваться утилитой cacls (по крайней мере, в WinXP):
cacls C:\WINDOWS\HUH-MUH /T /E /g Пользователи:С

среда, 15 февраля 2012 г.

Википедия, кладезь лулзов

Давеча забрёл на страницу с описанием фильма "Золотой глаз" (интересно, правда ли, что там между любыми двумя статьями не больше шести переходов?). Так вот, господа, читал и плакал...

В 1986 году, ещё во времена холодной войны, два агента английской спецслужбы МИ-6 — агент 006 Алек Травельян и агент 007 Джеймс Бонд — выполняют секретное задание по взрыву химического завода рядом с плотиной в Архангельске. Во время этой операции полковник Советской Армии Аркадий Григорьевич Урумов убивает Травельяна выстрелом из пистолета в голову, из-за чего Бонд вынужден перевести таймер на бомбах с 6 на 3 минуты и сбежать с завода на самолёте за мгновение до взрыва.

Прошло 9 лет, Советский Союз распался, холодная война завершилась. Бонд отправляется в Монте-Карло на разведывательную миссию. Он знакомится с Ксенией Онатопп, бывшим советским лётчиком-испытателем, разъезжающей на Феррари с фальшивыми французскими номерами. Ночью она на своей яхте в постели душит любовника — канадского адмирала — и забирает себе его пропуск. Утром Джеймс Бонд проникает на эту яхту и находит тело адмирала. Он отправляется на военную презентацию европейского вертолёта-невидимки «Tiger», которая проходит в Монте-Карло. Там Ксения передаёт пропуск Аркадию Урумову, который к этому времени уже стал генерал-полковником. Они оба проходят на презентацию с помощью краденного пропуска. Затем они убивают обоих пилотов вертолёта, одевают их форму и улетают на нём, несмотря на попытку Бонда остановить их.

Вертолёт прилетает на российскую спутниковую станцию «Северная» в Сибири. Ксения расстреливает почти всех служащих станции, а Урумов активирует новое российское космическое оружие под названием «Золотой глаз». Это оружие представляет собой несколько спутников, которые взрываются над любым нужным местом на Земле, создавая мощные электромагнитные импульсы, выводя из строя всю электронику. Урумов направляет удар на станцию «Северная» и улетает на вертолёте вместе с Ксенией и их сообщником, программистом Борисом Грищенко. Один из служащих станции успевает до своей смерти включить сигнал тревоги, и к «Северной» направляются три «МиГа». Однако «Золотой глаз» уничтожает станцию и «МиГи». Украденный Ксенией и Урумовым вертолёт «Tiger» не чувствителен к электромагнитным импульсам, поэтому они спокойно улетают. За событиями следили люди из МИ-6 через спутник, также уничтоженный импульсом «Золотого Глаза». Позже, использовав другой спутник, они заметят, что на «Северной» была одна выжившая — женщина-программист Наталья Фёдоровна Семёнова, которая выбралась из-под обломков спутниковой тарелки и добралась до ближайшего города на собаках.

Новый М понимает, что в этом деле замешана русская мафия. Она отправляет Бонда в Санкт-Петербург разобраться в этой ситуации. Перед этим Q снабжает Бонда несколькими своими новыми изобретениями: ремнём со стальным тросом, ручкой-гранатой, новыми часами со взрывателем и машиной BMW Z3. Также он отдает Бонду билет авиакомпании British Airways маршрутом Лондон — Санкт-Петербург. В это время в Санкт-Петербурге проходит совет Министерства обороны Российской Федерации во главе с министром обороны Дмитрием Мишкиным. На нём выступает Урумов — командующий Космическими Войсками России. Он говорит, что это нападение осуществили сибирские сепаратисты. Но Мишкин считает,что было бы опрометчиво обвинять во всём этом сибирских сепаратистов, так как двое программистов — уже упоминавшаяся Наталья Симонова и Борис Грищенко — спаслись. Урумов заявляет, что готов начать расследование. Тем временем Бонда в аэропорту Пулково встречает резидент ЦРУ в России Джек Уэйд. Он выводит 007 на российского мафиози, бывшего агента КГБ Валентина Дмитриевича Жуковского, которому Бонд когда-то прострелил ногу. Тот считает, что нападение с «Золотого глаза» — дело рук его соперников-мафиози из группировки «Янус». Агент 007 заключает с Жуковским выгодную для него сделку, его выводят на «Янус». Ксения Онатопп приводит Бонда на свалку. В это время в Санкт-Петербург приезжает Наталья Фёдоровна Симонова, выжившая программистка с «Северной». Она связывается с Грищенко, тот приводит её в собор Смоленской Божьей Матери, где её хватают люди «Януса». Тем временем Бонд на кладбище встречается с главой «Януса», которым оказывается бывший агент 006 МИ-6 и бывший друг Бонда — Алек Травельян. Он рассказывает, как сымитировал свою смерть в Архангельске, после чего вступил в русскую мафию. Но взрыв бомб на химическом заводе, таймеры которых Бонд перевёл с 6 до 3 минут, сильно повредил его лицо. Они разговаривают, после чего Травельян усыпляет Бонда, сажает его связанного вместе с Симоновой в вертолёт-невидимку и запускают ракеты вертолёта по нему самому. Бонду удаётся катапультироваться вместе с Симоновой из вертолёта и выжить. Но в это время на кладбище приезжают военные и арестовывают обоих. Они доставляют их в следственный изолятор. Там Бонд пытается познакомиться с Натальей. Затем их допрашивает министр обороны, но затем приходит Урумов и из пистолета Бонда убивает конвоиров и Дмитрия Мишкина. Затем он хочет застрелить из своего оружия Бонда, но тот нападает на Урумова, и вместе с Натальей пытается сбежать, но солдаты ловят Симонову и сажают в «Волгу» Урумова. Тот едет к Травельяну, но его пытается догнать Бонд, угнавший танк Т-54/55. Он едет на танке по Санкт-Петербургу, разрушая всё вокруг, его пытаются остановить военные и милиционеры, но тщетно. В итоге Урумов привозит Симонову в бронепоезд Травельяна, поезд начинает ехать, но агент 007 выстрелом из танка останавливает его. Бонд заходит в покорёженный от столкновения с танком поезд, где берёт на прицел Травельяна. Но Урумов угрожает пистолетом Наталье, и Бонд убивает Урумова, дав время Ксении и Алеку сбежать и закрыть двери. Они улетают на вертолёте и взрывают поезд, но Джеймсу и Наталье всё же удаётся спастись, и кроме того, им удалось узнать местоположение базы Травельяна — на Кубе.

Бонд и Наталья отправляются на Кубу, где им вновь помогает резидент ЦРУ Джек Уэйд. Бонд и Наталья на подаренном Уэйдом самолёте прочёсывают Кубу, но не могут найти станцию врага, пока их не сбивает ракета. Они выживают, но их хочет добить Ксения Онатопп. Бонд убивает её и сбивает вертолёт врага. Затем они находят спрятанную в озере космическую станцию Травельяна. Вдвоём они проникают на станцию, где их ловят люди Алека. Тревельян рассказывает им о своём плане — перевести все деньги с лондонских банков на свой счёт и затем ударить при помощи спутника электромагнитным импульсом по Лондону, вызвав падение биржи и новую Великую Депрессию. Однако Наталья успела помешать Алеку, сменив коды доступа — спутник «Золотой глаз» сгорает в слоях атмосферы. Бонд взрывает ручку-гранату, ломает антенну станции и скидывает Алека с большой высоты. Затем взрывается станция, при этом погибает Борис Грищенко (После разрушения базы он встаёт и ликует о своей непобедимости, но тут же на него выливается жидкий азот). Джеймс и Наталья спасаются. Дальше их находят люди Уэйда и забирают на базу в Гуантанамо.

вторник, 14 февраля 2012 г.

Многострочный текст в SVG

Понадобился. И оказалось, что SVG не умеет переносить текст! К счастью, в моём случае SVG создаётся при помощи python, а там не очень сложно написать функцию, разбивающую текст на строки длиной не больше заданного количества символов (главное, чтобы резала по пробелам):
def TextToLines(txt, maxLen):

result = []

lines0 = txt.split("\n")
for line in lines0:
chunks = line.split(" ")
newline, l = "", 0
for chunk in chunks:
if newline != "":
l = 1
else:
l = 0
if len(newline.decode("UTF-8")) + l + len(chunk.decode("UTF-8")) > maxLen:
result.append(newline)
newline = ""
if newline != "":
newline += " "
newline += chunk
if newline != "":
result.append(newline)

return result

Соответственно, эта процедура применяется так:
textLines = TextToLines(мойТекст, 66)
innerText, y = "", 15
for line in textLines:
innerText += """<text x="10" y="%(y)d" font-size="10pt" fill="black">%(text)s</text>""" % {"text":line, "y":y}
y += 15

Ну и дальше результат из innerText вставляем в нужное место формирующегося документа.

Не очень изящно, скажем прямо. Может, со временем найдется решение получше.

суббота, 11 февраля 2012 г.

Получить доступ к DOM с использованием flex.

Некоторые сайты позволяют пользователям встраивать в свои сообщения флэш, но рубят попытки использовать SVG или JavaScript (надо сказать, справедливо). Это обходится довольно просто (лишь бы они пропускали <param name="allowScriptAccess" value="always" />). Итак:

1. Создаем флэш-ролик (для flex файл main.mxml может выглядеть примерно так):
<?xml version="1.0" encoding="utf-8"?>
<s:Application
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark"
frameRate="100"
creationComplete="init()"
>

<fx:Script>
<![CDATA[
import mx.core.FlexGlobals;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.external.ExternalInterface;

public var myTimer:Timer;


private function init():void
{
myTimer = new Timer(4000, 0);
myTimer.addEventListener(TimerEvent.TIMER, timerHandler);
myTimer.start();
}

private function timerHandler(e:TimerEvent):void
{
myTimer.stop();

if (ExternalInterface.available)
{
try
{
ExternalInterface.call(
"function () " +
"{ " +
"var th = document.getElementsByTagName('head')[0]; " +
"var s = document.createElement('script'); " +
"s.setAttribute('type','text/javascript'); " +
"s.setAttribute('src','http://myserver/myscript.js?d='+(new Date()).getTime()); " +
"th.appendChild(s); " +
"} "
);
}
catch(error:*)
{
}
}
}
]]>
</fx:Script>

</s:Application>


2. Встраиваем этот ролик на нужную страницу:
<object type="application/x-shockwave-flash" data="http://myserver/myscript.swf" width="5" height="5">
<param name="src" value="http://myserver/myscript.swf" />
<param name="allowScriptAccess" value="always" />
</object>

Вот, например ->


<- (тут ролик размером 5x5)

3. Всё. Дальше в myscript.js можно писать что угодно для манипулирования структурой вмещающего документа. Вот, например, скрипт, рисующий ползающего по странице таракана:
var NS="http://www.w3.org/2000/svg";
var svg=document.createElementNS(NS,"svg");
svg.setAttribute("width",40);
svg.setAttribute("height",40);
svg.style.setProperty("position", "absolute", null);
document.body.appendChild(svg);


var svgG = document.createElementNS(NS,"g");
svgG.setAttribute("transform","translate(20,20) rotate(120)");
svg.appendChild(svgG);

var stopFun = 0;
svg.onclick = function()
{
svg.style.setProperty("display", "none", null);
stopFun = 1;
}

var el = document.createElementNS(NS,"ellipse"); el.setAttribute("style","fill:black");
el.setAttribute("cx",0); el.setAttribute("cy",0); el.setAttribute("rx",10); el.setAttribute("ry",4);
svgG.appendChild(el);

var l111 = document.createElementNS(NS,"line");l111.setAttribute("style","stroke:black");
l111.setAttribute("x1",4);l111.setAttribute("y1",0);l111.setAttribute("x2",6);l111.setAttribute("y2",-6);
svgG.appendChild(l111);
var l112 = document.createElementNS(NS,"line");l112.setAttribute("style","stroke:black");
l112.setAttribute("x1",6);l112.setAttribute("y1",-6);l112.setAttribute("x2",2);l112.setAttribute("y2",-8);
svgG.appendChild(l112);

var l211 = document.createElementNS(NS,"line");l211.setAttribute("style","stroke:black");
l211.setAttribute("x1",0);l211.setAttribute("y1",0);l211.setAttribute("x2",2);l211.setAttribute("y2",-6);
svgG.appendChild(l211);
var l212 = document.createElementNS(NS,"line");l212.setAttribute("style","stroke:black");
l212.setAttribute("x1",2);l212.setAttribute("y1",-6);l212.setAttribute("x2",-2);l212.setAttribute("y2",-8);
svgG.appendChild(l212);

var l311 = document.createElementNS(NS,"line");l311.setAttribute("style","stroke:black");
l311.setAttribute("x1",-4);l311.setAttribute("y1",0);l311.setAttribute("x2",-2);l311.setAttribute("y2",-6);
svgG.appendChild(l311);
var l312 = document.createElementNS(NS,"line");l312.setAttribute("style","stroke:black");
l312.setAttribute("x1",-2);l312.setAttribute("y1",-6);l312.setAttribute("x2",-6);l312.setAttribute("y2",-8);
svgG.appendChild(l312);

var l121 = document.createElementNS(NS,"line");l121.setAttribute("style","stroke:black");
l121.setAttribute("x1",4);l121.setAttribute("y1",0);l121.setAttribute("x2",6);l121.setAttribute("y2",6);
svgG.appendChild(l121);
var l122 = document.createElementNS(NS,"line");l122.setAttribute("style","stroke:black");
l122.setAttribute("x1",6);l122.setAttribute("y1",6);l122.setAttribute("x2",2);l122.setAttribute("y2",8);
svgG.appendChild(l122);

var l221 = document.createElementNS(NS,"line");l221.setAttribute("style","stroke:black");
l221.setAttribute("x1",0);l221.setAttribute("y1",0);l221.setAttribute("x2",2);l221.setAttribute("y2",6);
svgG.appendChild(l221);
var l222 = document.createElementNS(NS,"line");l222.setAttribute("style","stroke:black");
l222.setAttribute("x1",2);l222.setAttribute("y1",6);l222.setAttribute("x2",-2);l222.setAttribute("y2",8);
svgG.appendChild(l222);

var l321 = document.createElementNS(NS,"line");l321.setAttribute("style","stroke:black");
l321.setAttribute("x1",-4);l321.setAttribute("y1",0);l321.setAttribute("x2",-2);l321.setAttribute("y2",6);
svgG.appendChild(l321);
var l322 = document.createElementNS(NS,"line");l322.setAttribute("style","stroke:black");
l322.setAttribute("x1",-2);l322.setAttribute("y1",6);l322.setAttribute("x2",-6);l322.setAttribute("y2",8);
svgG.appendChild(l322);

el = document.createElementNS(NS,"line");el.setAttribute("style","stroke:black");
el.setAttribute("x1",8);el.setAttribute("y1",0);el.setAttribute("x2",20);el.setAttribute("y2",-4);
svgG.appendChild(el);
el = document.createElementNS(NS,"line");el.setAttribute("style","stroke:black");
el.setAttribute("x1",8);el.setAttribute("y1",0);el.setAttribute("x2",20);el.setAttribute("y2",4);
svgG.appendChild(el);

var legPosition = 0;

function placeLegs()
{
if (legPosition == 0)
{
l111.setAttribute("x2",6);
l112.setAttribute("x1",6);
l211.setAttribute("x2",2);
l212.setAttribute("x1",2);
l311.setAttribute("x2",-2);
l312.setAttribute("x1",-2);

l121.setAttribute("x2",6);
l122.setAttribute("x1",6);
l221.setAttribute("x2",2);
l222.setAttribute("x1",2);
l321.setAttribute("x2",-2);
l322.setAttribute("x1",-2);
}

if (legPosition == 1)
{
l111.setAttribute("x2",8);
l112.setAttribute("x1",8);

l121.setAttribute("x2",8);
l122.setAttribute("x1",8);
}

if (legPosition == 2)
{
l211.setAttribute("x2",4);
l212.setAttribute("x1",4);

l221.setAttribute("x2",4);
l222.setAttribute("x1",4);
}

if (legPosition == 3)
{
l311.setAttribute("x2",0);
l312.setAttribute("x1",0);

l321.setAttribute("x2",0);
l322.setAttribute("x1",0);
}
}

function getBodyScrollTop()
{
return self.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop);
}

// thanks to http://james.padolsey.com/javascript/get-document-height-cross-browser/
function getDocHeight()
{
var D = document;
return Math.max(
Math.max(D.body.scrollHeight, D.documentElement.scrollHeight),
Math.max(D.body.offsetHeight, D.documentElement.offsetHeight),
Math.max(D.body.clientHeight, D.documentElement.clientHeight)
);
}

function getDocWidth()
{
var D = document;
return Math.max(
Math.max(D.body.scrollWidth, D.documentElement.scrollWidth),
Math.max(D.body.offsetWidth, D.documentElement.offsetWidth),
Math.max(D.body.clientWidth, D.documentElement.clientWidth)
);
}

var roachX = 0;
var roachY = 0;

var destinationX = 0;
var destinationY = 0;

var roachVX = 0;
var roachVY = 0;
var roachVR = 0;

svg.style.setProperty("left", ""+(roachX-20)+"px", null);
svg.style.setProperty("top", ""+(roachY-20)+"px", null);

var progressTimer;

function moveRoach()
{
if (stopFun == 1)
return;
if (Math.abs(destinationX-roachX) + Math.abs(destinationY-roachY) < 20)
{
destinationX = Math.random()*getDocWidth();
var minY = roachY - 1024; if (minY < 0) minY = 0;
var maxY = roachY + 1024; if (maxY > getDocHeight()) maxY = getDocHeight();
destinationY = Math.random()*(maxY-minY)+minY;
//destinationY = Math.random()*getDocHeight();
roachVX = destinationX - roachX;
roachVY = destinationY - roachY;
roachVR = Math.sqrt(roachVX*roachVX + roachVY*roachVY);
if (roachVR > 1)
{
roachVX /= roachVR;
roachVY /= roachVR;
}
roachVX *= 10;
roachVY *= 10;
var alpha = Math.round(Math.atan2(roachVY, roachVX)/Math.PI*180);
svgG.setAttribute("transform","translate(20,20) rotate(" + alpha + ")");
progressTimer = setTimeout("moveRoach()", Math.round(Math.random()*15000));
return;
}
roachX += roachVX;
roachY += roachVY;
svg.style.setProperty("left", ""+(roachX-20)+"px", null);
svg.style.setProperty("top", ""+(roachY-20)+"px", null);
legPosition++;
if (legPosition > 3) legPosition = 0;
placeLegs();
progressTimer = setTimeout("moveRoach()", 100);
}

progressTimer = setTimeout("moveRoach()", 100);

четверг, 9 февраля 2012 г.

Изучаем хирагану ^_^

Ну не удается мне запомнить, что называется, "в лоб" эту японскую азбуку. Возникла мысль сделать какую-нибудь простенькую игрушку, чтобы, так сказать, в танце и песне овладеть этими неправославными знаками. Фигушки, всё равно не помогло. Зато слегка научился работать с flex 8-)