[Из песочницы] Любопытная особенность языка Java и коварные ошибки, которые она может повлечь |
package p;
import static p.A.x;
class A {
static String x = "A.x";
}
class B {
String x = "B.x";
}
class C {
String x = "C.x";
class D extends B {
void m() {
System.out.println(x);
}
}
}
public class X {
public static void main(String[] args) {
new C().new D().m();
}
}
B.x
Члены суперкласса B скрывают все вложенные элементы класса C, которые, в свою очередь, перекрывают статический импорт класса A.
package p;
import static p.A.x;
class A {
static String x = "A.x";
}
class B {
private String x = "B.x"; // Здесь изменен модификатор доступа
}
class C {
String x = "C.x";
class D extends B {
void m() {
System.out.println(x);
}
}
}
public class X {
public static void main(String[] args) {
new C().new D().m();
}
}
Вложенные элементы (классы) скрывают статический импорт
C.x
package p;
import static p.A.x;
class A {
static String x = "A.x";
}
class B {
private String x = "B.x";
}
class C {
String xOld = "C.x"; // Здесь внесены изменеия
class D extends B {
void m() {
System.out.println(x);
}
}
}
public class X {
public static void main(String[] args) {
new C().new D().m();
}
}
A.x
Метки: author Fisfis java статический импорт вложенные классы область видимости модификаторы доступа подкласс наследование |
15 лет в ISDEF: опыт старейшего участника |
Метки: author Spbwriter конференции growth hacking блог компании ассоциация isdef isdef soft софт маркетинг ит приложение |
tig — улучшаем продуктивность работы с git |
Всем привет,
хочу рассказать о консольной утилите, которая значительно увеличила мою продуктивность работы с Git, и, надеюсь, ускорит и вашу также. Называется она tig и была написана канадским программистом Джонасом Фонсека (Jonas Fonseca) ещё в далёком 2006-м году, но по настоящий день она активно развивается и поддерживается в великолепном состоянии. Я хочу показать её функционал (внимание, есть относительно тяжелые gif-ки внутри) и поделиться самыми удобными способами использования.
Несмотря на обилие визуальных GUI для работы с git, многие разработчики всё же предпочитают работать в консоли. В git есть много средств для увеличения производительности — алиасы, автодополнение, автоматическая коррекция ошибок и т.д., но всё же многие рядовые действия, такие как навигация по истории коммитов, анализ diff-ов, просмотр git blame и прочее — отнимают прилично времени и не всегда удобны.
Установка подробно описана в документации на главной странице проекта.
Если вы работаете в MacOS, то tig
доступен через Homebrew:
brew install tig
В Linux она тоже доступна для всех основных репозиториев:
apt-get install tig
yum install tig
Для остальных вариантов, стандартная схема — скачать исходный код (из релизов или через git clone
и выполнить make && make install
. Это подробно описано в ссылке выше.
Вот коротенькое демо того, как выглядит интерфейс при запуске команды tig
из директории репозитория:
В целом, как и следует ожидает от графического (пусть и текстового) интерфейса, но использование программы достаточно интуитивное, но всё же для начала нужно узнать несколько базовых сочетаний клавиш, на которых вся работа с программой и строится.
Главная клавиша это — h
: показать окно помощи :)
Как видим, есть несколько режимов (views) — главный, режим просмотра diff-ов, режим лога, режим просмотра дерева файлов, blame режим, просмотр текущего статуса и т.д.
Между всеми этими режимами переключаться довольно просто, обычно достаточно стрелочками (или j
/k
) выбрать нужную запись в логе или файл и нажать Enter
, либо, как, например, в случае с blame
— соответствующую клавишу (b
).
Режимы отображаются либо на весь экран либо в dual-split режиме. Последний бывает либо горизонтальный, либо вертикальный — программа сама выбрает его в зависимости от соотношения сторон терминала, и умеет обновлять на лету. Чтобы закрыть текущий режим (и вернуться к предыдущему) — просто нажимаем q
(quit). Чтобы, наоборот, развернуть окно с текущим режимом на полный экран — O
. Переключаться между режимами в dual-split режиме — Tab
.
В режиме просмотра diff
очень удобно увеличивать количество строк сверху и снизу изменённой строки с помощью [
и ]
(уменьшить и увеличить, соответственно):
Также в программе есть функция поиска по файлам (g
— grep
), поиска в окне (/
), открытия файла во внешнем редакторе (e
) и, в целом, интерфейс будет особенно комфортен тем, кто знаком с vim
. Например, с помощью :
можно вводить различные команды, переходить к нужной строке и т.д.
Для некоторых команд tig
может спокойно работать как drop-in замена git
.
tig status
tig log
tig show
tig blame file
tig grep pattern
tig refs
tig stash
tig status
Также, в режиме pager-а:
git show | tig
tig
достаточно гибкий в плане настройки — у него есть файл конфигурации (путь к которому также можно конфигурировать через TIGRC_USER) — ~/.tigrc
и поддержка readline.
Можно создавать свои собственные команды. Например, следующая команда копирует ID коммита в буфер обмена на MacOS X:
bind generic 5 !@sh -c "echo %(commit) | pbcopy"
У проекта отличная документация, больше о расширении функционала можно прочесть в ней.
tig
это одна из тех утилит, которые ускорили мою продуктивность ежедневной работы с git очень значительно. Не переключаясь между окнами, из терминала вы получаете очень удобный и интуитивный интерфейс для быстрой навигации по истории и изменениям вашего git-репозитория.
Программа написана на C и работает чудовищно быстро, что всегда приятно, особенно на больших репозиториях.
Метки: author imrobot2002 системы управления версиями git tig ui |
PDF отчеты по дашбордам Kibana |
"pages":[
{
"url": "http://0.0.0.0:5601/app/kibana#/dashboard/ca460a50-8cb0-11e7-a2bb-9b97b18ab1d8?embed=true&_g=()",
"title": "Заголовок 1",
"description": "За неделю"
},
{
"url": "http://0.0.0.0:5601/app/kibana#/dashboard/e05c6b70-8cb2-11e7-a2bb-9b97b18ab1d8?embed=true&_g=()",
"title": "Заголовок 2 ",
"description": "За неделю"
},
{
"url": "http://0.0.0.0:5601/app/kibana#/dashboard/233acc20-8cb3-11e7-a2bb-9b97b18ab1d8?embed=true&_g=()",
"title": "Заголовок 3",
"description": "За неделю"
}
]
"smtp_server": "smtp.yandex.ru",
"email_subject": "Регулярный отчет",
"email_text": "Регулярный отчет со статистикой по",
"email_from": "email@fr.om",
"email_password":"password",
"time_interval": 120,
"email_to": "email@t.o",
docker-compose up -d
Метки: author Crait системное администрирование серверное администрирование devops kibana pdf python python3 elasticsearch отчетность отчеты |
[Из песочницы] Learnopengl. Урок 2.5 — Источники света |
Метки: author UberSchlag разработка игр программирование c++ перевод glsl opengl opengl 3 light casters shading |
Кто все эти люди? Давайте спросим К50 |
Метки: author i-cat разработка мобильных приложений разработка веб-сайтов программирование блог компании voximplant calltracking |
Монетизация приложений в iOS 11: таргетируем встроенные покупки в новом App Store |
- (BOOL)paymentQueue:(SKPaymentQueue *)queue
shouldAddStorePayment:(SKPayment *)payment
forProduct:(SKProduct *)product;
- (void)requestProducts
{
SKProductsRequest* productRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"gold.iap.example.com"]];
productRequest.delegate = self;
[productRequest start];
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
NSArray* products = response.products;
// do some stuff
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if (@available(iOS 11.0, *)) {
NSArray* products = response.products;
[[SKProductStorePromotionController defaultController] updateStorePromotionOrder:products completionHandler:^(NSError * _Nullable error) {
if(error != nil) {
NSLog(@"Update store promotion order failed with error: %@", [error description]);
} else {
NSLog(@"Success");
}
}];
} else {
// Fallback on earlier versions
}
}
- (void)fetchPromotionOrder
{
if (@available(iOS 11.0, *)) {
[[SKProductStorePromotionController defaultController] fetchStorePromotionOrderWithCompletionHandler:^(NSArray * _Nonnull storePromotionOrder, NSError * _Nullable error) {
if(error != nil) {
NSLog(@"Fetch store promotion order failed with error: %@", [error description]);
} else {
NSMutableString* productIds = [NSMutableString string];
for (SKProduct* product in storePromotionOrder) {
[productIds appendString:product.productIdentifier];
[productIds appendString:@"; "];
}
NSLog(@"Got promotion order: %@", productIds);
}
}];
} else {
// Not supported
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if (@available(iOS 11.0, *)) {
SKProduct* product = [response.products objectAtIndex:0];
[[SKProductStorePromotionController defaultController] updateStorePromotionVisibility:SKProductStorePromotionVisibilityHide forProduct:product completionHandler:^(NSError * _Nullable error) {
if(error != nil) {
NSLog(@"Update store promotion visibility failed with error: %@", [error description]);
} else {
NSLog(@"Success");
}
}];
} else {
// Fallback on earlier versions
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if (@available(iOS 11.0, *)) {
SKProduct* product = [response.products objectAtIndex:0];
[[SKProductStorePromotionController defaultController] fetchStorePromotionVisibilityForProduct:product completionHandler:^(SKProductStorePromotionVisibility storePromotionVisibility, NSError * _Nullable error) {
if(error != nil) {
NSLog(@"Fetch store promotion visibility failed with error: %@", [error description]);
} else {
NSLog(@"Promotion visibility %ld", (long)storePromotionVisibility);
}
}];
} else {
// Fallback on earlier versions
}
}
|
STM32 без HAL и SPL |
STM32F10x Standard Peripherals Library (сокр. STM32F10x SPL) — библиотека, созданная компанией STMicroelectronics на языке Си для своих микроконтроллеров семейства STM32F10x. Содержит функции, структуры и макросы для облегчения работы с периферией микроконтроллера."
STM32Cube embedded software libraries, including:
The HAL hardware abstraction layer, enabling portability between different STM32 devices via standardized API calls
The Low-Layer (LL) APIs, a light-weight, optimized, expert oriented set of APIs designed for both performance and runtime efficiency
A collection of Middleware components, like RTOS, USB library, file system, TCP/IP stack, Touch sensing library or Graphic Library (depending on the MCU series)
//инициализация порта A*************************
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
//PA0 - PA0/ADC1_ADC2_ADC3_IN0
// GPIO_Pin_0 порта A аналоговый вход
GPIOA->MODER |= GPIO_MODER_MODER0_0;
GPIOA->MODER |= GPIO_MODER_MODER0_1;
GPIOA->MODER |= GPIO_MODER_MODER2_0;
//PA8/TIM1_CH1
// Alternate function mode
GPIOA->MODER &= ~GPIO_MODER_MODER8_0; //0
GPIOA->MODER |= GPIO_MODER_MODER8_1; //1
//GPIO alternate function high register (GPIOx_AFRL)
//AFR8[3:0] = 0001: AF1
GPIOA -> AFR[1] |= 0x00000001;
//PB13/TIM1_CH1N
// Alternate function mode
GPIOB->MODER &= ~GPIO_MODER_MODER13_0; //0
GPIOB->MODER |= GPIO_MODER_MODER13_1; //1
//GPIO alternate function high register (GPIOx_AFRL)
//AFR13[3:0] = 0001: AF1
GPIOB -> AFR[1] |= 0x00100000;
void init_ADC1(void)
{
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; //подаем тактирование АЦП
ADC1->CR2 |= ADC_CR2_ADON; //включить АЦП
ADC1->CR1 |= ADC_CR1_EOCIE;
ADC1->CR1 |= ADC_CR1_SCAN; // Bit 8 SCAN: Scan mode
ADC1->CR2 |= ADC_CR2_EOCS; //Bit 10 EOCS: End of conversion selection
ADC1->CR2 |= ADC_CR2_DMA; //Bit 8 DMA: Direct memory access mode (for single ADC mode)
ADC1->CR2 |= ADC_CR2_DDS; //Bit 9 DDS: DMA disable selection (for single ADC mode
//Bits 23:20 L[3:0]: Regular channel sequence length (4)
//0003: 4 conversion
ADC1->SQR1 |= ADC_SQR1_L_0; //1
ADC1->SQR1 |= ADC_SQR1_L_1; //1
ADC1->SQR1 &= ~ADC_SQR1_L_2; //0
ADC1->SQR1 &= ~ADC_SQR1_L_3; //0
//Bits 4:0 SQ1[4:0]: 1st conversion in regular sequence PC0/ADC1_ADC2_ADC3_IN10
ADC1->SQR3 &= ~ADC_SQR3_SQ1_0; //0
ADC1->SQR3 |= ADC_SQR3_SQ1_1; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ1_2; //0
ADC1->SQR3 |= ADC_SQR3_SQ1_3; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ1_4; //0
//Bits 4:0 SQ2[4:0]: 2st conversion in regular sequence PC1/ADC1_ADC2_ADC3_IN11
ADC1->SQR3 |= ADC_SQR3_SQ2_0; //1
ADC1->SQR3 |= ADC_SQR3_SQ2_1; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ2_2; //0
ADC1->SQR3 |= ADC_SQR3_SQ2_3; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ2_4; //0
//Bits 4:0 SQ3[4:0]: 3st conversion in regular sequence PC2/ADC1_ADC2_ADC3_IN12
ADC1->SQR3 &= ~ADC_SQR3_SQ3_0; //0
ADC1->SQR3 &= ~ADC_SQR3_SQ3_1; //0
ADC1->SQR3 |= ADC_SQR3_SQ3_2; //1
ADC1->SQR3 |= ADC_SQR3_SQ3_3; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ3_4; //0
//Bits 4:0 SQ4[4:0]: 4st conversion in regular sequence PC3/ADC1_ADC2_ADC3_IN13
ADC1->SQR3 |= ADC_SQR3_SQ4_0; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ4_1; //0
ADC1->SQR3 |= ADC_SQR3_SQ4_2; //1
ADC1->SQR3 |= ADC_SQR3_SQ4_3; //1
ADC1->SQR3 &= ~ADC_SQR3_SQ4_4; //0
NVIC_EnableIRQ (ADC_IRQn);
}
//Тактирование DMA2
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
//Stream3 Channel 1 DMA2 - в массив ADC2_array 2 каналa
//Bits 27:25 CHSEL[2:0]: Channel selection (1)
DMA2_Stream3->CR |= DMA_SxCR_CHSEL_0; //1
DMA2_Stream3->CR &= ~DMA_SxCR_CHSEL_1; //0
DMA2_Stream3->CR &= ~DMA_SxCR_CHSEL_2; //0
//Bits 14:13 MSIZE[1:0]: Memory data size (16 bit)
DMA2_Stream3->CR |= DMA_SxCR_MSIZE_0; //1
DMA2_Stream3->CR &= ~DMA_SxCR_MSIZE_1; //0
//Bits 12:11 PSIZE[1:0]: Peripheral data size (16 bit)
DMA2_Stream3->CR |= DMA_SxCR_PSIZE_0; //1
DMA2_Stream3->CR &= ~DMA_SxCR_PSIZE_1; //0
//Bits 10 MINC: Memory increment mode
DMA2_Stream3->CR |= DMA_SxCR_MINC;
//Bits 7:6 DIR[1:0]: Data transfer direction (00: Peripheral-to-memory)
DMA2_Stream3->CR &= ~DMA_SxCR_DIR_0; //0
DMA2_Stream3->CR &= ~DMA_SxCR_DIR_1; //0
//Bits 4 TCIE: Transfer complete interrupt enable
DMA2_Stream3->CR |= DMA_SxCR_TCIE;
//Bits 15:0 NDT[15:0]: Number of data items to transfer
//1000 point x 4 channel
DMA2_Stream3-> NDTR = 4000;
//Bits 31:0 PAR[31:0]: Peripheral address
DMA2_Stream3->PAR = (uint32_t) &(ADC2->DR);
//Bits 31:0 M0A[31:0]: Memory 0 address
DMA2_Stream3->M0AR = (uint32_t) ADC2_array;
//Bits 0 EN: Stream enable / flag stream ready when read low
DMA2_Stream3->CR |= DMA_SxCR_EN;
NVIC_EnableIRQ (DMA2_Stream0_IRQn);
// TIM1 PWM
RCC -> APB2ENR |= RCC_APB2ENR_TIM1EN; //тактирование TIM1
TIM1->CR1 |= TIM_CR1_CMS_0; //Center-aligned mode 1
TIM1->CR1 |= TIM_CR1_ARPE;
//частота ШИМ
//прескалер 8 и период 4000 - 3000 Гц
//частота шины 108 МГц
TIM1->PSC = 8;
TIM1->ARR = 4000;
TIM1->CCR1 = 1000; //начальные значения
TIM1->CCR2 = 1000;
TIM1->CCR3 = 1000;
TIM1->CCMR1 &= ~TIM_CCMR1_OC1M_0;
TIM1->CCMR1 |= TIM_CCMR1_OC1M_1;
TIM1->CCMR1 |= TIM_CCMR1_OC1M_2; //110: PWM mode 1
TIM1->CCMR1 &= ~TIM_CCMR1_OC2M_0;
TIM1->CCMR1 |= TIM_CCMR1_OC2M_1;
TIM1->CCMR1 |= TIM_CCMR1_OC2M_2; //110: PWM mode 1
TIM1->CCMR2 &= ~TIM_CCMR2_OC3M_0;
TIM1->CCMR2 |= TIM_CCMR2_OC3M_1;
TIM1->CCMR2 |= TIM_CCMR2_OC3M_2; //110: PWM mode 1
TIM1->CCER |= TIM_CCER_CC1E; // Capture/Compare 1 output enable
TIM1->CCER |= TIM_CCER_CC1NE; // Capture/Compare 1 complementary output enable
TIM1->CCER |= TIM_CCER_CC2E; // Capture/Compare 2 output enable
TIM1->CCER |= TIM_CCER_CC2NE; // Capture/Compare 2 complementary output enable
TIM1->CCER |= TIM_CCER_CC3E; // Capture/Compare 3 output enable
TIM1->CCER |= TIM_CCER_CC3NE; // Capture/Compare 3 complementary output enable
//DTG[7:0]: Dead-time generator setup 1 mks
TIM1->BDTR |= TIM_BDTR_DTG_0;
TIM1->BDTR |= TIM_BDTR_DTG_1;
TIM1->BDTR |= TIM_BDTR_DTG_2;
TIM1->BDTR |= TIM_BDTR_DTG_3;
TIM1->BDTR |= TIM_BDTR_DTG_4;
TIM1->BDTR |= TIM_BDTR_DTG_5;
TIM1->BDTR |= TIM_BDTR_DTG_6;
TIM1->BDTR |= TIM_BDTR_DTG_7;
TIM1->DIER |= TIM_DIER_CC1IE; //Capture/Compare 1 interrupt enable
//TIM1->DIER |= TIM_DIER_CC2IE; //Capture/Compare 2 interrupt enable
//TIM1->DIER |= TIM_DIER_CC3IE; //Capture/Compare 3 interrupt enable
TIM1->CR1 |= TIM_CR1_CEN; //Bit 0 CEN: Counter enable
TIM1->BDTR |= TIM_BDTR_MOE; //MOE: Main output enable
NVIC_EnableIRQ (TIM1_CC_IRQn); //разрешить прерывания от таймера
// TIM3 100 мсек
RCC -> APB1ENR |= RCC_APB1ENR_TIM3EN; //TIM3 Timer clock enable
TIM3->CR1 |= TIM_CR1_CEN; //Bit 0 CEN: Counter enable
TIM3->CR1 |= TIM_CR1_ARPE; //Bit 7 ARPE: Auto-reload preload enable
TIM3->DIER |= TIM_DIER_UIE; //Bit 0 UIE: Update interrupt enable
TIM3->PSC = 2000;
TIM3->ARR = 5400;
NVIC_EnableIRQ (TIM3_IRQn); //разрешить прерывания от таймера
Метки: author sanders1967 промышленное программирование программирование микроконтроллеров stm32 microcontrollers hal stm32cube |
Быстрый рендеринг океанских волн на мобильных устройствах |
Моделирование воды в компьютерной графике в реальном времени до сих пор остается весьма сложной задачей. Особенно актуально это при разработке компьютерных игр, в которых требуется создать визуально привлекательную картинку для игрока в рамках жесткого ограничения вычислительных ресурсов. И если на десктопах программист еще может рассчитывать на наличие мощной видеокарты и процессора, то в мобильных играх необходимо опираться на значительно более слабое железо.
В этой статье мы хотели поговорить о моделировании волн в открытом море и представить алгоритм, который позволил достичь достаточно интересные результаты при приемлемых 25-30Fps на среднем китайфоне.
В общем виде для моделирование поверхности волн в открытом море обычно используют экспериментально подобранный спектр Филлипса , т.е. разложение всего спектра волн на Фурье составляющие, которые анимируются во времени. Однако, данное решение весьма ресурсоемкое и, хотя быстрое Фурье разложение может выполняться на видеокарте, его практически невозможно использовать на слабых смартфонах как за счет быстродействия, так и из-за ограничения функционала видеокарты (поддержка рендеринга во float текстуру, ограничение в точности вычислений). Пример такого метода можно найти здесь и здесь.
Более простым методом является генерация распределения волн заранее (или непосредственно в шейдере), а затем сложение волн разной фазы и амплитуды.
Несмотря на простоту метода он может обеспечить весьма впечатляющие результаты, однако требует точной настройки и имеет ряд ограничений. Рассмотрим этот подход подробнее и попробуем разобраться с возникающими нюансами как в части качества картинки так и быстродействия.
Для генерации волны нам нужно знать ее высоту в определенной точке. Ее можно получить множеством способов. Например, просто использовать комбинацию синусов, косинусов от координат этой точки, но очевидно, что полученное распределение высот выглядит слишком искусственно и не подходит для решения нашей задачи. В этом случае наблюдается периодичность даже если менять направление волн относительно друг друга.
Забегая немного вперед, на изображении внизу представлена поверхность воды, полученное путем сложения волн разной амплитудой и высоты.
Тот же алгоритм, но издали наблюдается периодичность.
Более оптимально использовать шум Перлина, пример генерации которого на шейдерах представлен ниже (код для Cg, для GLSL требует косметических изменений):
float rand(float2 n) {
return frac(sin(dot(n, float2(12.9898, 4.1414))) * 43758.5453);
}
float noise(float2 n) {
const float2 d = float2(0.0, 1.0);
float2 b = floor(n), f = smoothstep(0, 1, frac(n));
return lerp(lerp(rand(b), rand(b + d.yx), f.x), lerp(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
}
rand — генерирует псевдослучайное значение, а noise интерполирует четыре случайный числа в углах квадрата на любую точку внутри него.
Периодичности не наблюдается
Результат уже лучше, распределение волн заметно изменилось, но их форма все также отличается от естественных волн. При небольшой амплитуде это еще приемлемо, но для больших волн характерно наличие резких пиков.
При использовании спектра Филлипса это решается смещением сетки поверхности воды к пикам, что придает необходимую форму. Однако, более простым методом и существенно более эффективным в нашем случае является использование простой формулы, что приводит к удовлетворительному результату в виде остроконечных волн
Недостаток метода в том, что он приводит к появлению кругов и прочих замкнутых фигур, заметных глазу, но при соответствующем подборе параметров этот недостаток становится несущественным.
Очевидно, что наличие только одной фазы (или иначе октавы) для воды недостаточно для получения реалистичной воды и требуется наложить несколько волн с разной амплитудой и фазой, что позволяет получить как большие волны так и мелкую рябь на поверхности.
float amp = maxHeight / 2;
for (int i = 0; i < count; ++i){
h += amp * phase(pos + v[i]*t);
pos *= sp;
amp /= sa;
}
Хорошие результаты получаются при выборе sp, sa равным 2, но мы вольны выбирать любые значения, обеспечивающие приемлемые результаты. Подбор этих параметров позволяет получить разнообразные типы волн и управлять их изменением вплоть до полного штиля.
Для анимации волн достаточно каждую фазу смещать в своем направлении. При этом необходимо учесть, что скорость движения больших волн больше и волны двигаются преимущественно в одном направлении.
Как показали эксперименты вполне достаточно иметь порядка 7 фаз для получения “вкусной” картинки и, в принципе, в очередном велосипеде не было необходимости. Однако, первые же тесты на смартфонах повергли в шок, т.к. фпс неумолимо стремился к 0, что не могло не огорчать. Посмотрим сколько операций в шейдере потребуются для отображения одной точки:
В целом можно не согласиться с приведенными выше рассуждениями, т.к. высоту поверхности в целом достаточно вычислить для каждой вершины меша, что заведомо быстрее, чем считать для каждой точки экрана. Но в таком случае ни о каком реализме можно и не мечтать. Максмум, что мы получим очень грубое приближение к желаемому результату. Таким образом, все приведенные выше операции приходится вычислять именно во фрагментном шейдере. Варианты оптимизации:
Последний вариант имеет право на жизнь, но fps также слишком мал — порядка 3-4 кадров в секунду. Кроме того, в этом случае при расчете нормали мы упираемся в точность хранения данных в текстуре, что приводит к появлению "ступенек" на воде. Конечно можно использовать текстуру с вещественными числами, но тогда мы дополнительно ограничим количество поддерживаемых устройств.
В тоже время, для получения высоты точки необходимо считать значение высоты из текстуры, но нам ничего не мешает запечь в эту текстуру еще и карту нормалей. Таким образом, одной выборкой из текстуры мы можем получить высоту точки и одновременно нормаль к ней. Запекание данных в текстуру можно выполнить заранее или же непосредственно на видеокарте с помощью рендеринга в текстуру (например перед запуском приложения или смене параметров).
При вычислении текстуры необходимо обеспечить достаточную точность хранения нормалей. Если сохранять нормаль в привычном виде карты нормалей, то эта точность оказывается недостаточной, что проявляется в артефактах изображения.
Действительно, для получения нормали нам достаточно сохранять лишь проекцию нормали в горизонтальной плоскости (nx, ny). В общем виде, каждая из этих компонент меняется в диапазоне [-1,1]. Но в случае воды используемый диапазон оказывается существенно меньше, т.к. нормали в основном ориентированы вверх (что особенно заметно при генерации волн малой амплитуды). Таким образом, если нормализовать этот диапазон по максимальному значению, то мы сможем существенно увеличить точность хранения нормалей и, соответственно, качество картинки.
При этом для генерации результирующей волны надо аккуратно преобразовать нормаль каждой фазы с учетом амплитуды волны, ее фазы, а также подобранного выше коэффициента масштабирования.
Несмотря на выполненные оптимизации у нас до сих пор происходит выборка из 7 текстур и хотелось бы уменьшить это количество. Как упоминалось ранее, уменьшение этого числа в общем виде не желательно.
Однако, форму волн мы храним в текстуре, в которой можем заранее сгенерить вместо одной фазы сразу несколько.
Это решает проблему генерации множества волн небольшим количество проходов, однако при анимации становится заметно, что часть волн движется с одной скоростью. Для уменьшения этого эффекта можно сохранять в текстуру волны с большей разницей фаз, например 1 — 3 — 5, а при рендеринге мы получим 1-1'-3-3'-5-5'. Также мы использовали подход при котором первые две фазы из четырех использовали одну текстуру, а последние две уже другую с иным распределением и количеством фаз. Именно этим способом получено изображение, приведенное в начале поста
Кроме описанных выше методов мы тестировали несколько других вариантов оптимизации. Наиболее интересным из них нам показалась интерполяция волн по времени.
Смысл этого метода в том, что мы можем отрендерить в текстуру результирующую карту высот и нормалей в некоторые моменты времени, а для прочих сделать интерполяцию высот и нормалей.
Таким образом раз в N кадров требуется сделать полный расчет поверхности, а N-1 кадр можно считать путем простой выборки из двух текстур.
В таком случае получается, что одна волна уменьшается и рядом появляется следующая. При уменьшении N анимация становится более плавной, хотя эффективность метода и снижается.
Таким образом его можно вполне эффективно использовать в определенных условиях, например, при малой скорости волн или на относительном удалении от поверхности, когда недостаток метода становится менее заметным.
На данный момент мы получили вполне жизнеспособную и симпатичную водичку, однако не обговаривали какую сетку будем использовать для поверхности воды. Очевидно, что для отображения воды до линии горизонта нам фактически придется использовать бесконечно большой меш точек (по крайней мере растянуть его до плоскости отсечения камеры), в то время как, количество точек в нем весьма ограничено). Простое линейное масштабирование не работает, т.к вблизи камеры меш становится слишком разреженным, а в вблизи линии горизонта, наоборот, излишне густым.
Самым простым способом решения этого проблемы является масштабирование меша в вершинном шейдере в зависимости от расстояния до камеры. Недостаток же вполне очевиден — сложно подобрать необходимые параметры, а полученное распределение точек все также будет неравномерно.
Другой вариант — это использование меша с разной детализации в зависимости от расстояния. Но это может приводит к рывкам при смене уровня детализации, а также требует введения дополнительной логики контроля этих уровней.
Наиболее удобным является использование метода projected Grid, который в общем можно описать следующим образом:
В качестве аналогии можно представить слайд с точками, прикрепленный к прожектору (камере). Там где тень от точки попадает на плоскость и находится искомая точка. В то же время с точки расположения камеры наблюдатель увидит всю ту же равномерную сетку
Этот метод имеет несколько преимуществ:
В основе получения корректного изображения лежит правильный учет всех составляющих светового потока — отраженный свет, свет рассеянный в толще воды, блики от солнца и т.д.
В общем виде это является нетривиальной задачей, но в нашем случае мы использовали более простой подход, т.к. не было необходимости отображать поверхность дна, каустики и прочие.
Подробное описание расчета освещения не будем, т.к. есть много подробных статей посвященных этой тематики (например здесь, здесь). Хотелось бы только отметить необходимость выбора "правильной" формулы для вычисления коэффициента Френеля.
В первой версии шейдера мы никак не могли добиться реалистично выглядящей воды. Полученный результат был больше похож на нарисованную или пластиковую воду. Оказалось, что мы использовали самую примитивную версию для вычисления коэффициента Френеля:
Приведенный способ симуляции воды мы реализовали в Unity3D. Для расчета освещения и создания формы волн мы использовали явные вертексные и фрагментные шейдеры (можно было реализовать и на поверхностных шейдерах, но это не играет принципиальной роли). При тестах на андроид смартфонах мы получили от 25 к/c (Adreno 405 + MediaTek MT6735P) до 45 (Adreno 505 + Snapdragon 430). На части смартфонах, как и ожидалось, приложение на заработало из-за отсутствия поддержки чтения из текстуры в вершинном шейдере. При этом интересно отметить, что расчет освещения в результате оказался сопоставим со сложностью c генерацией волн. При необходимости можно поднять fps за счет использования других моделей освещения или отключения части элементов как карта окружения, блики и т.д
Метки: author SSul программирование алгоритмы unity3d блог компании simbirsoft моделирование воды шейдеры unity волны |
Pygest #17. Релизы, статьи, интересные проекты из мира Python [29 августа 2017 — 11 сентября 2017] |
Метки: author andrewnester разработка веб-сайтов программирование машинное обучение python django digest pygest machine learning flask дайджест web deep learning |
Использование системных функций D-Bus в Sailfish OS |
Visual D-Bus
и D-Bus Inspector
) или командной строки (рисунок 1). Второй подход использует описанную в предыдущей статье методологию изучения исходного кода.org.nemomobile.dbus
; во-вторых, о способе обращения к D-Bus из desktop-файлов.org.nemomobile.dbus
предоставляет два основных компонента для работы с D-Bus: DBusAdaptor
и DBusInterface
. Первый позволяет создать приложению свой интерфейс, второй — использовать существующий. При общем поиске информации интерес представляют оба компонента, так как позволяют узнать, как можно взаимодействовать с приложением извне, и как приложение использует сторонние интерфейсы соответственно./usr/share/jolla-settings
и выполнить проверку на использование D-Bus:$ cd /usr/share/jolla-settings/
$ grep -i -H 'dbus' *
settings.qml:import org.nemomobile.dbus 2.0
settings.qml: DBusAdaptor {
$ grep -A 5 'DBusAdaptor' ./settings.qml
DBusAdaptor {
service: "com.jolla.settings"
path: "/com/jolla/settings/ui"
iface: "com.jolla.settings.ui"
function showSettings() {
/usr/share/applications
; во-вторых, использование описанных функций запускает экземпляр приложения; в-третьих, при описании взаимодействия с D-Bus используются поля с префиксом X-Maemo
:X-Maemo-Service
— сервис, или местоположение приложения на пользовательской или системной шине;X-Maemo-Object-Path
— путь, или наименование объекта;X-Maemo-Method
— наименование вызываемого метода.X-Maemo
появились в Sailfish OS как наследство от операционной системы Maemo (рисунок 2), ранее разрабатываемой компанией Nokia. Они позволяют объявить функции D-Bus таким образом, что к ним можно обращаться без предварительного запуска программы. Эти функции позволяют осуществить запуск программы с осуществлением перед её открытием некоторой предварительной работы.MimeType
для указания типов файлов, которые может обработать программа. Пример реализации доступен на GitHub./usr/share/applications
и выполнить поиск по одному из ключевых слов:$ cd /usr/share/applications/
$ grep -H 'X-Maemo-Service' *
jolla-calendar-import.desktop:X-Maemo-Service=com.jolla.calendar.ui
jolla-calendar.desktop:X-Maemo-Service=com.jolla.calendar.ui
jolla-camera-viewfinder.desktop:X-Maemo-Service=com.jolla.camera
jolla-clock.desktop:X-Maemo-Service=com.jolla.clock
jolla-contacts-import.desktop:X-Maemo-Service=com.jolla.contacts.ui
jolla-email.desktop:X-Maemo-Service=com.jolla.email.ui
jolla-gallery-openfile.desktop:X-Maemo-Service=com.jolla.gallery
jolla-gallery-playvideostream.desktop:X-Maemo-Service=com.jolla.gallery
jolla-mediaplayer-openfile.desktop:X-Maemo-Service=com.jolla.mediaplayer
jolla-mediaplayer.desktop:X-Maemo-Service=com.jolla.mediaplayer
jolla-messages-openurl.desktop:X-Maemo-Service=org.nemomobile.qmlmessages
jolla-messages.desktop:X-Maemo-Service=org.nemomobile.qmlmessages
jolla-notes-import.desktop:X-Maemo-Service=com.jolla.notes
jolla-notes.desktop:X-Maemo-Service=com.jolla.notes
jolla-settings.desktop:X-Maemo-Service=com.jolla.settings
new-mail.desktop:X-Maemo-Service=com.jolla.email.ui
open-url.desktop:X-Maemo-Service=org.sailfishos.browser.ui
ovpn-import.desktop:X-Maemo-Service=com.jolla.settings
sailfish-office-openfile.desktop:X-Maemo-Service=org.sailfish.office
sailfish-office.desktop:X-Maemo-Service=org.sailfish.office
simkit.desktop:X-Maemo-Service=org.sailfish.simkit
voicecall-ui-openurl.desktop:X-Maemo-Service=com.jolla.voicecall.ui
voicecall-ui.desktop:X-Maemo-Service=com.jolla.voicecall.ui
/usr/share/jolla-calendar
(принципы именования директорий описывались в предыдущей статье) и узнать, в каких файлах имеется обращение к D-Bus:$ cd /usr/share/jolla-calendar/
$ grep -r -i -H 'dbus' *
DbusInvoker.qml:import org.nemomobile.dbus 2.0
DbusInvoker.qml:DBusAdaptor {
calendar.qml:import org.nemomobile.dbus 2.0
calendar.qml: DbusInvoker {}
DbusInvoker.qml
, который содержит только определения интерфейса и функций D-Bus:$ cat ./DbusInvoker.qml -n
1 import QtQuick 2.0
2 import Sailfish.Silica 1.0
3 import org.nemomobile.dbus 2.0
4 import Calendar.dateParser 1.0
5
6 DBusAdaptor {
7 service: "com.jolla.calendar.ui"
8 path: "/com/jolla/calendar/ui"
9 iface: "com.jolla.calendar.ui"
10
11 function viewEvent(id, recurrenceId, startDate) {
12 var occurrence = DateParser.parseTime(startDate)
13 if (isNaN(occurrence.getTime())) {
14 console.warn("Invalid event start date, unable to show event")
15 return
16 }
17
18 if (pageStack.currentPage.objectName === "EventViewPage") {
19 pageStack.currentPage.uniqueId = id
20 pageStack.currentPage.recurrenceId = recurrenceId
21 pageStack.currentPage.startTime = occurrence
22 } else {
23 pageStack.push("pages/EventViewPage.qml",
24 { uniqueId: id, recurrenceId: recurrenceId, startTime: occurrence },
25 PageStackAction.Immediate)
26 }
27 requestActive.start()
28 }
29
30 function viewDate(dateTime) {
31 var parsedDate = new Date(dateTime)
32 if (isNaN(parsedDate.getTime())) {
33 console.warn("Invalid date, unable to show events for date")
34 return
35 }
36
37 if (pageStack.currentPage.objectName === "DayPage") {
38 pageStack.currentPage.date = parsedDate
39 } else {
40 pageStack.push("pages/DayPage.qml", { date: parsedDate }, PageStackAction.Immediate)
41 }
42 requestActive.start()
43 }
44
45 function importFile(fileName) {
46 if (pageStack.currentPage.objectName === "ImportPage") {
47 pageStack.currentPage.fileName = fileName
48 } else {
49 pageStack.push("pages/ImportPage.qml", { "fileName": fileName }, PageStackAction.Immediate)
50 }
51 requestActive.start()
52 }
53
54 function activateWindow(arg) {
55 app.activate()
56 }
57 }
com.jolla.calendar.ui
и использовать путь /com/jolla/calendar/ui
и интерфейс com.jolla.calendar.ui
(строки 7-9). После этого станут доступными объявленные функции, из которых, в рамках поставленной задачи, интерес представляет только viewDate
(строки 30-43), принимающая в качестве аргумента одно из представлений даты, распознаваемое объектом Date
. Полученные результаты позволяют реализовать свой компонент для работы с календарём:import QtQuick 2.0
import Sailfish.Silica 1.0
import org.nemomobile.dbus 2.0
Item {
id: calendarControl
/* Открыть календарь для текущей даты.
*/
function showAgenda() {
calendar.call('viewDate', Date.now())
}
DBusInterface {
id: calendar
service: 'com.jolla.calendar.ui'
path: '/com/jolla/calendar/ui'
iface: 'com.jolla.calendar.ui'
}
}
/usr/share/jolla-settings/pages/flashlight
, необходимо подключиться к сервису com.jolla.settings.system.flashlight
и использовать путь /com/jolla/settings/system/flashlight
и интерфейс com.jolla.settings.system.flashlight
. После этого, вызывая функцию toggleFlashlight без параметров, становится возможным включать и выключать вспышку.getProperty
с передаваемым в неё параметром "flashlightOn"
. В данном случае возвращается булево значение.$ cd /usr/share/jolla-settings
$ grep -i -H "dbus" ./pages/flashlight/*
./pages/flashlight/Flashlight.qml:import org.nemomobile.dbus 2.0
./pages/flashlight/Flashlight.qml: flashlightDbus.call("toggleFlashlight", undefined, handleToggle, handleError)
./pages/flashlight/Flashlight.qml: property QtObject flashlightDbus: DBusInterface {
./pages/flashlight/Flashlight.qml: flashlight.flashlightOn = flashlightDbus.getProperty("flashlightOn")
$ grep -A 5 -i -H "DBusInterface" ./pages/flashlight/Flashlight.qml
./pages/flashlight/Flashlight.qml: property QtObject flashlightDbus: DBusInterface {
./pages/flashlight/Flashlight.qml- signalsEnabled: true
./pages/flashlight/Flashlight.qml- service: "com.jolla.settings.system.flashlight"
./pages/flashlight/Flashlight.qml- path: "/com/jolla/settings/system/flashlight"
./pages/flashlight/Flashlight.qml- iface: "com.jolla.settings.system.flashlight"
./pages/flashlight/Flashlight.qml- function flashlightOnChanged(newOn) {
import QtQuick 2.0
import Sailfish.Silica 1.0
import org.nemomobile.dbus 2.0
Item {
id: flashlightControl
// Состояние вспышки.
property bool flashlightOn
// Включает или выключает вспышку.
function toggleFlashlight() {
flashlightOn = !flashlightOn;
flashlight.call("toggleFlashlight", undefined);
}
DBusInterface {
id: flashlight
service: "com.jolla.settings.system.flashlight"
path: "/com/jolla/settings/system/flashlight"
iface: "com.jolla.settings.system.flashlight"
signalsEnabled: true
function flashlightOnChanged(newOn) {
flashlightControl.flashlightOn = newOn
}
}
Component.onCompleted: {
flashlightControl.flashlightOn = flashlight.getProperty("flashlightOn")
}
}
sailfish-browser https://google.com/
. Но это выходит за рамки описываемого в статье материала.$ sailfish-browser https://google.com/
[D] unknown:0 - Using Wayland-EGL
greHome from GRE_HOME:/usr/bin
libxul.so is not found, in /usr/bin/libxul.so
Created LOG for EmbedLite
[D] onCompleted:103 - ViewPlaceholder requires a SilicaFlickable parent
Loaded xulDir:/usr/lib/xulrunner-qt5-38.8.0/libxul.so, appDir:/usr/bin
EmbedLiteExt virtual nsresult EmbedChromeManager::Observe(nsISupports*, const char*, const char16_t*):82: obj:(nil), top:app-startup
EmbedLiteExt virtual nsresult EmbedTouchManager::Observe(nsISupports*, const char*, const char16_t*):86: obj:(nil), top:app-startup
EmbedLiteGlobalHelper app-startup
EmbedLiteSyncService app-startup
PREFS SERVICE INITAILIZED
EmbedPrefService app-startup
EmbedliteDownloadManager initialized
UserAgentOverrideHelper app-startup
1505073762747 addons.manager DEBUG Application has been upgraded
1505073762892 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/XPIProvider.jsm: ["XPIProvider"]
1505073762912 addons.manager DEBUG Loaded provider scope for resource://gre/modules/LightweightThemeManager.jsm: ["LightweightThemeManager"]
1505073762942 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/GMPProvider.jsm
1505073762961 addons.manager DEBUG Loaded provider scope for resource://gre/modules/addons/PluginProvider.jsm
1505073762968 addons.manager DEBUG Starting provider: XPIProvider
1505073762973 addons.xpi DEBUG startup
1505073762982 addons.xpi DEBUG checkForChanges
1505073762993 addons.xpi DEBUG Loaded add-on state from prefs: {}
1505073763000 addons.xpi DEBUG getInstallState changed: false, state: {}
1505073763009 addons.xpi DEBUG Empty XPI database, setting schema version preference to 16
1505073763012 addons.xpi DEBUG No changes found
1505073763015 addons.manager DEBUG Registering shutdown blocker for XPIProvider
1505073763021 addons.manager DEBUG Provider finished startup: XPIProvider
1505073763022 addons.manager DEBUG Starting provider: LightweightThemeManager
1505073763024 addons.manager DEBUG Registering shutdown blocker for LightweightThemeManager
1505073763029 addons.manager DEBUG Provider finished startup: LightweightThemeManager
1505073763032 addons.manager DEBUG Starting provider: GMPProvider
1505073763046 addons.manager DEBUG Registering shutdown blocker for GMPProvider
1505073763050 addons.manager DEBUG Provider finished startup: GMPProvider
1505073763052 addons.manager DEBUG Starting provider: PluginProvider
1505073763055 addons.manager DEBUG Registering shutdown blocker for PluginProvider
1505073763059 addons.manager DEBUG Provider finished startup: PluginProvider
1505073763060 addons.manager DEBUG Completed startup sequence
Created LOG for EmbedPrefs
[D] QMozWindowPrivate::setSize:71 - Trying to set empty size: QSize(-1, -1)
Attempting load of libEGL.so
EmbedLiteExt virtual nsresult EmbedTouchManager::Observe(nsISupports*, const char*, const char16_t*):86: obj:0xb225a130, top:domwindowopened
EmbedLiteExt void EmbedChromeManager::WindowCreated(nsIDOMWindow*):91: WindowOpened: 0xb225a140
EmbedLiteExt void EmbedTouchManager::WindowCreated(nsIDOMWindow*):95: WindowOpened: 0xb225a140
EmbedLiteExt void EmbedTouchManager::WindowCreated(nsIDOMWindow*):108: id for window: 1
###################################### SelectAsyncHelper.js loaded
###################################### embedhelper.js loaded
### ContextMenuHandler.js loaded
### SelectionPrototype.js loaded
### SelectionHandler.js loaded
Init Called:[object Object]
JavaScript warning: https://www.google.ru/xjs/_/js/k=xjs.mhp.en_US.2vKAz7DqmvI.O/m=sb_mobh,hjsa,d,csi/am=AAAD/rt=j/d=1/t=zcms/rs=ACT90oFx8AHVqc9lMfPQBwURKXyQ4qaFiA, line 7: mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create
Описание | Необходимые данные |
---|---|
Создать атмосферу |
|
Настройка яркость экрана |
|
Настройка ориентации экрана |
|
Автоматическая настройка яркости экрана |
|
Настройка времени включения спящего режима |
|
Настройка состояния дисплея во время зарядки |
|
Настройка системного размера шрифта |
|
Настройка системной громкости |
|
Настройка вибрации |
|
Включить/выключить звуки на системные события |
|
Включить/выключить WLAN |
|
Включить/выключить общий доступ к интернету |
|
Включить/выключить режим полёта |
|
Включить/выключить Bluetooth |
|
Описание | Необходимые данные |
---|---|
Просмотр события в календаре | Сервис:com.jolla.calendar.ui Путь: /com/jolla/calendar/ui Интерфейс: com.jolla.calendar.ui Функция: viewEvent(id, recurrenceId, startDate) |
Просмотр дня в календаре | Сервис:com.jolla.calendar.ui Путь: /com/jolla/calendar/ui Интерфейс: com.jolla.calendar.ui Функция: viewDate(dateTime) |
Открытие камеры в последнем состоянии | Сервис:com.jolla.camera Путь: / Интерфейс: com.jolla.camera.ui Функция: showViewfinder() |
Открытие фронтальной камеры | Сервис:com.jolla.camera Путь: / Интерфейс: com.jolla.camera.ui Функция: showFrontViewfinder() |
Создание будильника | Сервис:com.jolla.clock Путь: / Интерфейс: com.jolla.clock Функция: newAlarm() |
Просмотр контакта | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: showContact(int contactId) |
Редактирование контакта | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: editContact(int contactId) |
Импортирование контактов | Сервис:com.jolla.contacts.ui Путь: /com/jolla/contacts/ui Интерфейс: com.jolla.contacts.ui Функция: importWizard() |
Воспроизвести аудиофайл по URL | Сервис:com.jolla.mediaplayer Путь: /com/jolla/mediaplayer/ui Интерфейс: com.jolla.mediaplayer.ui Функция: openUrl(url) |
Создание новой заметки | Сервис:com.jolla.notes Путь: / Интерфейс: com.jolla.notes Функция: newNote() |
Просмотр настроек | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showSettings() |
Просмотр списка загрузок | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showTransfers() |
Просмотр списка аккаунтов | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showAccounts() |
Просмотр настройки записи телефонных разговоров | Сервис:com.jolla.settings Путь: /com/jolla/settings/ui Интерфейс: com.jolla.settings.ui Функция: showCallRecordings() |
Включить/выключить поддержку Android | Сервис:com.jolla.apkd Путь: /com/jolla/apkd Интерфейс: com.jolla.apkd Функция: controlService(true/false) |
Включить/выключить вспышку | Сервис:com.jolla.settings.system.flashlight Путь: /com/jolla/settings/system/flashlight Интерфейс: com.jolla.settings.system.flashlight Функция: toggleFlashlight() |
Перезагрузить устройство | Сервис:com.nokia.dsme Путь: /com/nokia/dsme/request Интерфейс: com.nokia.dsme.request Функция: req_reboot() |
Выключить устройство | Сервис:com.nokia.dsme Путь: /com/nokia/dsme/request Интерфейс: com.nokia.dsme.request Функция: req_shutdown |
Позвонить | Сервис:com.jolla.voicecall.ui Путь: / Интерфейс: com.jolla.voicecall.ui Функция: dial(number) |
Открыть изображения в галерее | Сервис:com.jolla.gallery Путь: /com/jolla/gallery/ui Интерфейс: com.jolla.gallery.ui Функция: showImages(array_of_urls) |
Открыть видео в галерее | Сервис:com.jolla.gallery Путь: /com/jolla/gallery/ui Интерфейс: com.jolla.gallery.ui Функция: playVideoStream(url) |
Создать новое письмо | Сервис:com.jolla.email.ui Путь: /com/jolla/email/ui Интерфейс: com.jolla.email.ui Функция: mailto() |
Поиск WLAN-сетей | Сервис:com.jolla.lipstick.ConnectionSelector Путь: / Интерфейс: com.jolla.lipstick.ConnectionSelectorIf Функция: openConnectionNow('wifi') |
Метки: author osanwe разработка под sailfish os разработка мобильных приложений qt api qml sailfish os мобильная разработка операционные системы |
[recovery mode] Индия приняла закон о «выключении» интернета |
Метки: author VASExperts it- инфраструктура блог компании vas experts vas experts индия блокировка закон |
Новый VPU Myriad X от Intel обучит дронов самостоятельности |
Метки: author 1cloud разработка для интернета вещей блог компании 1cloud.ru 1cloud myriad x intel movidius |
[Перевод] Погружение в CSS: метрики шрифтов, line-height и vertical-align |
Метки: author pimenov-o javascript css блог компании positive technologies fonts front-end разработка css font |
Зачем мы сделали VOD на WebRTC |
vod://sample.mp4
vod-live://sample.mp4
|
[Перевод] Создание языка программирования с использованием LLVM. Часть 10: Заключение и другие вкусности LLVM |
#ifdef __i386__
int X = 1;
#else
int X = 42;
#endif
Метки: author 32bit_me программирование компиляторы open source c++ llvm компилятор llvm ir |
PHP-Дайджест № 116 – свежие новости, материалы и инструменты (27 августа – 10 сентября 2017) |
Свежая подборка со ссылками на новости и материалы. В выпуске: PHP 7.2.0 RC 1, Laravel 5.5 LTS, чистый код на PHP, предложения из PHP Internals, видео с конференций и митапов, и многое другое.
Приятного чтения!
// Суммирует элементы массива (Эквивалент array_sum())
$sum = array_reduce($terms, '+', 0);
Спасибо за внимание!
Если вы заметили ошибку или неточность — сообщите, пожалуйста, в личку.
Вопросы и предложения пишите на почту или в твиттер.
Прислать ссылку
Быстрый поиск по всем дайджестам
<- Предыдущий выпуск: PHP-Дайджест № 115
Метки: author pronskiy разработка веб-сайтов php блог компании zfort group дайджест php- ссылки symfony yii laravel zend |
Контроль на рабочем месте: новое постановление Европейского Суда |
Метки: author iou-iou законодательство и it-бизнес контроль на рабочем месте мониторинг работодателем европейский суд barbulesku yahoo yahoo messenger |
Работа и жизнь в Болгарии. Часть вторая |
Метки: author slamon карьера в it-индустрии болгария работа за границей жизнь за рубежом |
Дайджест свежих материалов из мира фронтенда за последнюю неделю №279 (4 — 10 сентября 2017) |
Просим прощения за возможные опечатки или неработающие/дублирующиеся ссылки. Если вы заметили проблему — напишите пожалуйста в личку, мы стараемся оперативно их исправлять.
|