Как стать автором
Обновить

Создаем UWP приложение на языке SPL

Время на прочтение6 мин
Количество просмотров1.8K
На данный момент существует не так много языков программирования, которые годятся для написания UWP приложений (UWP — Universal Windows Platform, или «Универсальная платформа Windows»), одинаково подходящих для запуска на десктопах, планшетах, смартфонах и других устройствах, работающих под управлением Windows 10.

Теперь язык программирования SPL, который я разрабатываю, также может использоваться для создания UWP приложений благодаря наличию SPL SDK. Расскажу об этом подробнее.

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

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

#.output("Hello, world!")

но это было бы слишком просто :)

Приведенная ниже программа является простым браузером фрактала Мандельброта, навигация по которому осуществляется с помощью стандартных мультитачевых жестов перемещения и масштабирования. Также в этом примере показана возможность выполнения задач в параллельных потоках, работа закадрового буфера для графических функций и использование мультитачевых жестов для работы интерфейса программы. Этот пример специально не перегружен дополнительным функционалом, чтобы сосредоточиться на главном — продемонстрировать, как программа на SPL может стать самостоятельным UWP приложением, которое потом может быть загружено в магазин Microsoft Store.

Текст программы браузера фрактала:

w,h = #.scrsize()
sfx = -2.5; sfy = -2*h/w; sfs = 4
#.aaoff()
:again
end,redo,update = 0
-> draw() 'посчитаем фрактал в отдельном потоке

>
  moved = 0
  > #.pan() 'мультитач навигация
    moved = 1
    x,y,s = #.pan(3)
    x -= (s-1)/2*w
    y -= (s-1)/2*h
    #.drawoff(x,y,s)
  <
  ? moved 'пересчитаем фрактал заново
    sfs /= s
    sfx -= x*sfs/w
    sfy -= y*sfs/w
    again -> end
    redo = 1
  .
  ? update & !redo 'отобразим фрактал
    update = 0
    #.drawoff(0,0)
  .
  wn,hn = #.scrsize()
  ? wn!=w | hn!=h 'изменился размер экрана
    w = wn; h = hn
    again -> end
    redo = 1
  .
<

draw()=
  :loop
  #.offon() 'рисуем в закадровом буфере
  #.scrclear(0,0,0)
  .redo = 0
  sfx = .sfx; sfy = .sfy; fs = .sfs/.w
  > y, 1...h
    > x, 1...w
      fx = sfx + x*fs
      fy = sfy + y*fs
      #.drawpoint(x,y,color(fx,fy):3)
    <
    loop -> .redo
    .update = 1
  <
  .end = 1
.

color(x,y)=
  zr = x; zi = y; n = 0
  maxn = #.int(200*(1-#.log10(.sfs/4)))
  > zr*zr+zi*zi<4 & n<maxn
    zrn = zr*zr-zi*zi+x; zin = 2*zr*zi+y
    zr = zrn; zi = zin; n += 1
  <
  ? n=maxn; <= 0,0,0; .
  <= #.hsv2rgb(n/maxn*360,1,1):3
.

image

Теперь разберем подробнее этот пример.

w,h = #.scrsize()
sfx = -2.5; sfy = -2*h/w; sfs = 4
#.aaoff()
:again
end,redo,update = 0
-> draw() 'посчитаем фрактал в отдельном потоке

В первых двух строчках получаем размер экрана, который нужен для рисования фрактала во весь экран, а также устанавливаем начальное положение и масштаб фрактала. В третьей строке выключаем антиалиасинг графики (который по умолчанию включен). В последней строке запускаем функцию расчета фрактала draw в параллельном потоке.

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

Основное тело программы представляет собой бесконечный цикл, оформленный командами > и < без всяких условий, потому что нам нужно просто непрерывно циклить в ожидании действий пользователя, по ходу дела отображая нарисованную картинку фрактала.

moved = 0
> #.pan() 'мультитач навигация
  moved = 1
  x,y,s = #.pan(3)
  x -= (s-1)/2*w
  y -= (s-1)/2*h
  #.drawoff(x,y,s)
<

Этот цикл включается и работает, если пользователь совершает мультитачевый жест. Во второй строке условием работы цикла является индикатор работы мультитача. В четвертой строке мы получаем параметры мультитачевого жеста — координаты смещения и масштаб. Затем функцией #.drawoff отображаем закадровый буфер с нарисованным в нем фракталом таким образом, чтобы картинка соответствовала текущему смещению и масштабу мультитача. Таким образом получаем интерактивную навигацию по фракталу.

? moved 'пересчитаем фрактал заново
  sfs /= s
  sfx -= x*sfs/w
  sfy -= y*sfs/w
  again -> end
  redo = 1
.

Здесь срабатывает условие, если пользователь только что закончил жест навигации. Пересчитываются новые параметры фрактала: масштаб sfs и координаты sfx,sfy. В пятой строке программа идет на метку again, чтобы заново запустить расчет фрактала, если он уже успел завершиться, то есть переменная end не равна нулю. Если же фрактал еще считается, то он просто отправляется на пересчет благодаря установке переменной redo в 1.

? update & !redo 'отобразим фрактал
  update = 0
  #.drawoff(0,0)
.

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

wn,hn = #.scrsize()
? wn!=w | hn!=h 'изменился размер экрана
  w = wn; h = hn
  again -> end
  redo = 1
.

Назначение этого условия — определить, не изменился ли размер экрана — пользователь может развернуть окно на весь экран, просто изменить размер окна или повернуть планшет в другую ориентацию — тогда новые значения ширины и высоты окна wn,hn не совпадут со старыми w,h.

Если такое произошло, то аналогично тому, что мы уже делали ранее, либо перезапускаем поток с расчетом фрактала в четвертой строке, либо в пятой строке сигнализируем этому потоку, что ему нужно начать расчет заново.

draw()=
  :loop
  #.offon() 'рисуем в закадровом буфере
  #.scrclear(0,0,0)
  .redo = 0
  sfx = .sfx; sfy = .sfy; fs = .sfs/.w
  > y, 1...h
    > x, 1...w
      fx = sfx + x*fs
      fy = sfy + y*fs
      #.drawpoint(x,y,color(fx,fy):3)
    <
    loop -> .redo
    .update = 1
  <
  .end = 1
.

Это основной цикл рисования фрактала. В третьей строке поток графических команд перенаправлется в закадровый буфер, чтобы потом именно оттуда отображать фрактал с учетом его текущего смещения и масштаба при интерактивной навигации. Как видно из этого примера, работа с графикой безопасна для многопоточных расчетов в SPL.

Основной функцией рисования здесь является функция #.drawpoint, которая в качестве аргумента цвета принимает три переданных значения из функции color.

Далее мы видим, что расчет начинается заново путем перехода на метку loop, если глобальная переменная .redo не ноль.

Далее, устанавливая значение глобальной переменной .update в единицу, мы указываем основному потоку, что уже есть, что нового можно отобразить во фрактале.

Если расчет фрактала закончился, то значение глобальной переменной .end устанавливается равной единице. Зная это, при необходимости пересчета фрактала мы заново запустим поток расчета фрактала.

color(x,y)=
  zr = x; zi = y; n = 0
  maxn = #.int(200*(1-#.log10(.sfs/4)))
  > zr*zr+zi*zi<4 & n<maxn
    zrn = zr*zr-zi*zi+x; zin = 2*zr*zi+y
    zr = zrn; zi = zin; n += 1
  <
  ? n=maxn; <= 0,0,0; .
  <= #.hsv2rgb(n/maxn*360,1,1):3
.

Это последняя функция в нашем примере, она выполняет самое важное — возвращает цвет фрактала в каждой точке экрана. В качестве входных параметров x,y функция color принимает координаты точки во фрактале.

Далее в цикле идет расчет фрактала Мандельброта, а в последних двух строках функции возвращается цвет точки в зависимости от полученного расчета фрактала — либо 0,0,0, если фрактал так и не вышел за установленный лимит maxn, либо цвет из цветового пространства HSV в соответствии с полученным значением фрактала.

Здесь лирическая часть статьи заканчивается и переходим к технической части. Сделаем из программы на SPL самостоятельное UWP приложение.

Дело в том, что интерпретатор языка SPL сначала генерирует промежуточный бинарный код, а потом уже выполняет его. Это все происходит «за кадром» и пользователь может даже не иметь понятия о существовании этого промежуточного кода — файла COD. Но, включив соответствующую опцию в программе, этот COD файл можно увидеть и скопировать из SPL. После чего берем SPL SDK, выполненный в виде уже готового проекта Visual Studio 2017, и копируем в него этот COD файл в виде ресурса.



Затем указываем имя программы в первой строке. В нашем случае это «fractal.txt». Расширение COD-файла .cod указывать не нужно.



Сам же SPL SDK представляет собой просто «выполнитель» COD-файлов, который автоматически при старте запустит вашу программу на SPL.

В общем-то, это все. Самостоятельное UWP приложение готово! Теперь его можно компилировать и запускать на любом устройстве на Windows 10. При желании можно отправить это приложение в Microsoft Store.

Сейчас язык программирования SPL находится в стадии активной разработки. Назначение языка SPL (Simple Programming Language) — любительское программирование в качестве хобби. SPL — это язык программирования, который придумал и разрабатываю лично я. Также «SPL» — это мое приложение для Windows 10, которое сейчас находится в стадии закрытой беты. Те пользователи, которых интересует язык программирования SPL и кто хотел бы принять участие в его развитии с помощью тестирования, а также своими предложениями — добро пожаловать на сайт языка SPL. Там вы найдете документацию по SPL и другую полезную информацию.

Всем успехов в программировании!
Теги:
Хабы:
Всего голосов 16: ↑5 и ↓11-6
Комментарии10

Публикации

Истории

Ближайшие события