Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Ошибка при подключении сокета к серверу: a bytes-like object is required, not 'str'

#python #сокет #python_3x


Собственно, ошибка: TypeError: 


  a bytes-like object is required, not 'str'


Ошибка в строке " sock.send('Hello, World") "

Ошибка нашлась в приложении клиента, вот собственно код клиента:

import socket

sock = socket.socket()
sock.connect(('localhost', 9090))
sock.send('hello, world!')

data = sock.recv(1024)
sock.close()

print(data)


Вот код сервера:

import socket

sock = socket.socket()
sock.bind(('', 9090))
sock.listen(1)
conn, addr = sock.accept()

print('connected:', addr)

while True:
    data = conn.recv(1024)
    if not data:
        break
    conn.send(data.upper())

conn.close()


Я этот пример вообще списал, т.к только-только (буквально пару минут назад) начал
изучение сокетов, а тут такое.
    


Ответы

Ответ 1



Метод socket.send ожидает последовательность байтов, а не строку. Чтобы преобразовать строку в последовательность байтов, можно воспользоваться методом str.encode: sock.send("hello, world!".encode()) Аналогично, если вы хотите получить не последовательность байтов, а строку, то можно воспользоваться методом str.decode: data = sock.recv(1024).decode() По умолчанию в качестве кодировки будет использован UTF-8.

Как происходит создание объекта функции?

#python


Помогите, пожалуйста, разобраться в закулисьях этого кода:

def someFunction():
    list = []
    for i in range(5):
        list.append(lambda x: i ** x)
    return list

#вызов функции
list = someFunction()
print(list[1](2))


По идее, результатом этого кода должна выступать единица, но на самом деле выводится
16. Я не совсем понимаю, как работает эта программа. По-моему:

Компилятор начинает свою работу с 1-ой строки. Видит def и пропускает всю функцию.
Затем добирается до строки 

list = someFunction()


после которой начинает интерпретировать функцию someFunction. Добирается до цикла
for, и начинается первая итерация. Но команда, добавляющая элемент в конец списка,
в качестве аргумента принимает функцию, которая, в свою очередь, еще не получила в
распоряжение свой аргумент. Что добавилось в список? Я предполагаю, что создался объект
функции, который связался с конкретным элементом списка. Так, я думаю, происходит на
всех итерациях. После чего функция возвращает список, в котором находятся ссылки на
созданные объекты функций. Если всё так и есть на самом деле, то это логично.

Но что происходит при чтении элемента из списка? Окей. Если выплывает 16 вместо 1,
скорее всего, все объекты функций в качестве итерационной переменной принимают 4-ку,
т.е. последнюю итерацию. И здесь не совсем понятно, почему при создании объекта функции
не сохранилось значение счетчика цикла? Предполагаю, что вы можете мне сказать: "А
как, по-твоему, это значение сохранится, если его нет в параметрах lambda-функции?".
Но тогда как она находит и подставляет последнюю итерацию? Да и вообще, пока писал
этот пост еще больше запутался: с чего бы это компилятор создает объект-функции до
того, как эта функция вызывается? Ведь объект функции someFunction конструировался
тогда, когда мы вызвали эту функцию. А объекты lambda-функции создались ещё до их вызова.

Будьте добры, помогите разобраться в данном вопросе. Спасибо. 
    


Ответы

Ответ 1



Переменная в вашем примере всегда будет равна 4 потому, что поиск переменной в объемлющей области видимости (по правилу LEGB) происходит в момент вызова вложенной функции, а не при создании объекта. Т.е. внутри лямбда-функции i - это "ссылка на i в объемлющей области", а не "ссылка на int(0)" или "ссылка на int(4)". Пример из ответа @aleks.andr с аргументом по умолчанию сработает потому, что вычисление значения по умолчанию происходит один раз в момент создания вложенной функции (что, кстати, тоже способно приводить к интересным эффектам, если создавать mutable-объект и изменять его внутри функции: def f(a=[]): a.append(0) ; print(a)) P.S. Не перекрывайте builtin list в своем коде, это может привести к удивительным последствиям.

Ответ 2



И здесь не совсем понятно, почему при создании объекта функции не сохранилось значение счетчика цикла - она же ссылается на переменную i, которая после всех итераций осталась равна 4. Вам действительно нужно захватить переменную i при объявлении лямбды. Вот рабочий код, возвращающий вашу единицу: def someFunction(): list = [] for i in range(5): list.append(lambda x,i=i: i ** x) return list #вызов функции list = someFunction() print(list[1](2))

Зависает exe после компиляции С#

#c_sharp #visual_studio #компиляция #exe


Всем привет. Сегодня впервые столкнулся с проблемой, и не могу понять, в чем дело.
В Visual Studio 2015 скомпилил программу, все запускается нормально. Но из папки
проекта - никак. Тупо висит в процессах. Пробовал менять версии .net, менять разрядность
систем для запуска - 0 эмоций. Просто висит в процессах и все. При запуске (на F5)
из среды - запускается и работает отлично. В чем может быть проблема? Может, сталкивался
кто? Заранее спасибо.

P.S - все библиотеки стандартные, новых не подключал. 
    


Ответы

Ответ 1



Если антивирус AVAST - пробуйте отключить или занести каталог с файлом в исключения.

Git Bash изменить домашний каталог

#git #git_bash


Установил клиента Git, для изучения и дальнейшего использования был выбран Git Bash,
и вроде все работает, но есть один нюанс: Каталог по умолчанию, почему то установлен
сетевой диск, хотелось бы его изменить, как это сделать?

OS version: Windows 7 x64;

Git version: 2.6.3-64-bit;
    


Ответы

Ответ 1



Решение было найдено здесь Необходимо изменить ярлык запуска Git Bash: Вызываем контекстное меню для ярлыка: Клик правой кнопкой мыши по ярлыку, в открывшемся меню выбираем свойства. Устанавливаем необходимый путь: В поле Рабочая папка устанавливаем необходимый путь. Так же мне потребовалось изменить строку запуска: в поле Объект удаляем в конце строки --cd-to-home. Запускаем клиента и убеждаемся в что что команда pwd отдает необходимый нам каталог

Ответ 2



Необходимо в проводнике вызывать контекстное меню правой кнопкой мыши и выбрать пункт Git Bush here. Git Bush автоматически откроется в выбранной папке без изменения свойств ярлыка.

Вывод всех возможных ip-адресов из диапазона

#powershell


Нужно написать скрипт на powershell. Есть любой IP адрес сети и маска (к примеру:
10.151.96.0/21), нужно вывести все возможные ip-адреса, удовлетворяющие адресу сети
и его маске. Как организовать этот цикл, чтобы работал с любым начальным условием?
    


Ответы

Ответ 1



Пока суть да дело, вот код на C# накатал: using System; using System.Net; string rangeIP = "10.151.96.0"; int rangeMask = 21; int wildcard = 32 - rangeMask; byte[] ipb = IPAddress.Parse(rangeIP).GetAddressBytes(); Array.Reverse(ipb); int ip = BitConverter.ToInt32(ipb, 0); for (int i = 1; i < (1 << wildcard) - 1; i++) { int ipi = ip | i; byte[] ipib = BitConverter.GetBytes(ipi); Array.Reverse(ipib); Console.WriteLine(new IPAddress(ipib)); } Сейчас попробую PowerShell вспомнить... Во, так получилось: $rangeIP = "10.151.96.0" $rangeMask = 21 $wildcard = (32 - $rangeMask) $ipb = [Net.IPAddress]::Parse($rangeIP).GetAddressBytes() [Array]::Reverse($ipb) $ip = [BitConverter]::ToInt32($ipb, 0) For ($i = 1; $i -lt ((1 -shl $wildcard) - 1); $i++) { $ipi = ($ip -bor $i) $ipib = [BitConverter]::GetBytes($ipi) [Array]::Reverse($ipib) Write-Host (New-Object IPAddress(@(,$ipib))) } За соответствие кода канонам не отвечаю, я PS не знаю. :)

Как реализовать IComparer<T> для сравнения нескольких свойств?

#c_sharp #net


Положим, есть следуюший класс:

class Operation
{
    public string Foo { get; set; }
    public string Bar { get; set; }
    public int Baz { get; set; }
}


Я хочу реализовать компарер IComparer. Код получается такой:

class OperationComparer : IComparer
{
    public int Compare(Operation x, Operation y)
    {
        if (x.Foo == y.Foo && x.Bar == y.Bar && x.Baz == y.Baz)
            return 0;
        else
            return 1; // ???
    }
}


Однако непонятно, что возвращать, если свойства не равны. Когда возвращать 1, а когда −1?
    


Ответы

Ответ 1



Общий порядок написания компарера следующий: Сравнить первое свойство, по которому происходит сравнение (сортировка), получив −1/0/1 (меньше/равно/больше). Если свойства не равны, вернуть −1 или 1 в соответствии с результатом сравнения. Для изменения порядка сортировки вернуть противоположное число. Если свойства равны, перейти к пункту 1 со следующим свойством. Если свойства кончились, вернуть результат последнего сравнения как есть. Код получается следующий: class OperationComparer : IComparer { public int Compare(Operation x, Operation y) { int cmp = string.Compare(x.Foo, y.Foo); if (cmp != 0) return cmp; cmp = string.Compare(x.Bar, y.Bar); if (cmp != 0) return cmp; cmp = x.Baz.CompareTo(x.Baz); return cmp; } } Стоит отметить, что в современном коде подобные компареры обычно не нужны, так как есть LINQ, в котором с помощью методов OrderBy и ThenBy ту же логику можно выразить гораздо проще.

В каких случаях стоит выполнять проверку интернет соединения?

#java #android


Пишу REST API приложение и при каждом запросе к серверу, сперва проверяю на наличие
интернет соединения, и только в случае положительного результата, посылаю запрос на
сервер. 

Правильно ли я делаю?  Или в каких случаях стоит выполнять проверку?
    


Ответы

Ответ 1



Ну когда правильно проверять наличие интернет соединения вопрос в общем случае довольно спорный. У нас в компании в основном если и проверяем наличие соединения, то только при запуске приложения что бы уведомить пользователя что без интернета приложение работать не сможет. Если же интернет обрывается после открытия приложеньки, то просто обрабатываем ошибки выполнения запросов и выводим пользователю соответствующие уведомления или вместо очередного экрана приложеньки показываем заглушку с описанием что случилось. Проверять при каждом запросе в любом случае бессмысленно ибо интернет может пропасть после проверки, но до окончания выполнения запроса. Да и то что проверка прошла как правило не гарантирует что интернет действительно есть, может соединение с каким нить гуглом и проходит, а до вашего REST сервера не достучаться.

Проблемы с кодировкой ADO.NET

#c_sharp #sql #net #sql_server #adonet


Создал таблицу в БД, но при добавлении символов кириллицы, символ '?', знаю что это
проблемы с кодировкой, но как установить UTF-8 в БД?


public static void Add_User(string login, string password, string firstName, string
secondName, string secretQuestion, string responseQuestion, DateTime birthDay)
{
    string sqlExpression = "INSERT INTO Users (Login, Password, FirstName, SecondName,
Birthday, SecretQuestion, ResponseQuestion) " +
        string.Format("VALUES (" + @"'{0}', '{1}', '{2}', '{3}', {4}, '{5}', '{6}'"
+ ")", login, password, firstName, secondName, birthDay.ToString("u").Replace(" 00:00:00Z",
""), secretQuestion, responseQuestion);
    SqlCommand comAdd = new SqlCommand(sqlExpression, _dbConnection);
    comAdd.ExecuteNonQuery();
}

    


Ответы

Ответ 1



Если вам нужен именно Unicode, то встроенной поддержки для хранения Unicode-ных строк в кодировке UTF-8 в SqlServer нет, поддерживается только кодировка UTF-16. Для хранения Unicode-ных строк существуют типы данных nchar и nvarchar. Соответственно нужные столбцы в таблице должны иметь соответствующий тип. Например: create table Users ( FirstName nvarchar(20), LastName nvarchar(30) ); Если с типами всё в порядке, но, к примеру, такая insert into Users (FirstName, LastName) values ('Имя', 'Фамилия'); вставка создаёт запись с ???, то скорее всего collation базы данных отличается от кириллического (проверить collation заданный для БД можно выполнив скрипт select collation_name from sys.databases where database_id = db_id()). В этом случае перед строковым литералом, содержащим нелатинские символы, добавьте N, чтобы SqlServer воспринимал его как Unicode. Т.е., например, N'Имя' вместо 'Имя': insert into Users (FirstName, LastName) values (N'Имя', N'Фамилия'); Вообще для nvarchar литералов с нелатинскими символами я бы посоветовал добавлять N всегда, чтобы не зависеть от collation базы. Что касается кода вашего метода Add_User, то он уязвим для sql-injection атаки. Вместо формирования строки с литералами безопаснее использовать параметризованный запрос.

Ответ 2



В SQL Server нет поддержки UTF-8, но есть UTF-16. Подробнее тут. Установить кодировку в базе данных можно так. ALTER DATABASE DBNAME COLLATE SQL_Latin1_General_CP1_CI_AS или для cyrillic ALTER DATABASE DBNAME COLLATE Cyrillic_General_CI_AS Проверить кодировку в базе данных можно так SELECT SERVERPROPERTY('Collation') См. MSDN "Поддержка параметров сортировки и Юникода"

Заменить слово в файле

#python


Нужно открыть файл, найти и заменить в нем одно слово (оно встречается в файле 1
раз) и сохранить под другим названием.

На баше эта задача решается вот так:

cat /tmp/test/file | sed "s/TEST_OLD/TEST_OLD/"> /tmp/test/file_new


C Питоном не очень, представляю что проблема решается как-то примерно так:

import re
output_file = open("/tmp/test/file").readlines()
string = "TEST_OLD"
string.replace(string,"TEST_NEW")
open("/tmp/test/file_new", "w")

    


Ответы

Ответ 1



Ну в целом верно, только модуль re не нужен, также стоит либо использовать with, либо закрывать файлы file.close() после использования. Как то так в конечном итоге: with open('test1') as file_in: text = file_in.read() text = text.replace("TEST_OLD", "TEST_NEW") with open("test2", "w") as file_out: file_out.write(text) Да и readlines() возвращает список строк и это не совсем то что нам здесь нужно. Update 0: Для замены лучше использовать регулярные выражения, чтобы избежать проблем при включении слова в другие и т.д. Спасибо @ReinRaus за замечание. Обрати внимание на ответ @andy.37 Если файл объемный, то read() плохая идея, поскольку так мы загрузим все содержимое файла в память. Правильный выбор в этом случае - построчное чтение/запись.

Ответ 2



Не имеет смысл читать файл целиком и забивать память. Ну и вопрос при замене word -> WORD, менять ли что-то в слове 'sword'? def replace_in_file(file1, file2, pattern, subst, wordonly=True): with open(file1, 'r') as f1: with open(file2, 'w') as f2: for line in f1: if wordonly: # заменяем word -> WORD, но не sword -> sWORD f2.write(re.sub('\b{}\b'.format(pattern), subst, line) else: # заменяем и word -> WORD и sword -> sWORD f2.write(line.replace(pattern, subst))

Ответ 3



oldfile = open("file", "r") newfile = open("file_new", "w") for line in oldfile: s = line.replace("TEST_OLD","TEST_NEW") newfile.write(s)

Ответ 4



Чтобы выполнить аналог: sed 's/OLD/NEW/' file >file_new: from pathlib import Path Path('file_new').write_text(Path('file').read_text().replace('OLD', 'NEW')) Чтобы не заменять все подстроки, а только целые слова, можно регулярные выражения использовать: Замена в файле списка слов. Чтобы не загружать весь файл целиком в память, можно построчно замену производить. См. Как заменить строчку в .txt файле через python 3? В случае, если замена имеет ту же длину в байтах и результат вы хотите в тот же файл записать (по месту замена), можно mmap воспользоваться: from mmap import ACCESS_WRITE, mmap old, new = b'OLD', b'NEW' with open('file', 'r+b') as file, mmap(file.fileno(), 0, access=ACCESS_WRITE) as s: i = s.find(old) if i != -1: s[i:i+len(old)] = new В этом случае файл не переписывается целиком, а только нужное место обновляется.

Какой UnmanagedType соответствует UnicodeString из Delphi?

#c_sharp #delphi #net


Delphi (UnicodeString):

function ShowDelphiMsg(inputStr : UnicodeString) : UnicodeString; stdcall;
var a : UnicodeString;
begin
    ShowMessage(inputStr);
    a := 'Тест!';
    result := a;
end;


C#

[DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention
= CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.LPWStr)]
internal static extern string ShowDelphiMsg([MarshalAs(UnmanagedType.LPWStr)] string
inputStr);


Такой вариант отлично передает из C# в Delphi строку, Delphi выводит строку. Но основная
проблема - ошибка при возврате строки из Delphi.

Впорос: Какой UnmanagedType соответствует UnicodeString из Delphi?

p.s. архитектура у обоих приложений x64.



Delphi (WideString):

function ShowDelphiMsg(inputStr : WideString) : WideString; stdcall;
var a : WideString;
begin
    ShowMessage(inputStr);
    a := 'Тест!';
    result := a;
end;


Так же вызывает ошибку при возврате значения.



Упростил функцию

C#:

 [DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention
= CallingConvention.StdCall)]
 [return: MarshalAs(UnmanagedType.BStr)]
 internal static extern string ShowDelphiMsg();


Delphi:

function ShowDelphiMsg() : WideString; stdcall;
var a : WideString;
begin
    a := 'Тест!';
    result := a;
end;


Та же ошибка.
    


Ответы

Ответ 1



Судя по коду UnmanagedType.LPWStr тут используется PWideChar, что есть указатель на WideString. Из функции возвращать надо именно WideString. UPD: Оказывается, из-за особенностей C# (?) нужно использовать вот такой способ: Delphi: function SomeFunction2(out OutVar: Widestring): BOOL; stdcall; begin OutVar := 'Hello'; Result := True; end; C# [DllImport(@"Test.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res); т.е. возвращать WideString через результат нельзя, а вот делать out параметр (но не var см. ответ Alex) - запросто. Нагуглено: https://stackoverflow.com/questions/9331026/why-can-delphi-dlls-use-widestring-without-using-sharemem

Ответ 2



Проблема состоит в том, что Delphi и Visual Studio расходятся во мнении как трактовать соглашение вызова stdcall. Любая функция, возвращающая сложный (в т.ч. автоуправляемый) тип: function SomeFunction: WideString; Трактуется Delphi как: procedure SomeFunction(var AResult: WideString); В то же время, Visual Studio трактует её либо как: procedure SomeFunction(out AResult: WideString); Либо как: function SomeFunction: Pointer; // действительно возврат через EAX/RAX (в зависимости от того, какой именно тип используется в Result - размера Pointer или больше) В результате из-за этих отличий мы либо получаем Access Violation, либо утечку памяти. Варианты правильных решений: (Самый правильный) Использовать safecall вместо stdcall (Допустимый - ответ zed) Явно описывать выходной параметр на Delphi (Хак - ответ Alexis) Привести Result к простому типу и вручную управлять им Если интересно, можно почитать.

Ответ 3



В процессе изучения нашел еще один вариант, более приемлемый на мой взгляд: Delphi: uses ActiveX; function GetWideString(str: WideString): TBStr; stdcall; begin result := SysAllocString(POleStr(str)); end; C# [DllImport("Native.dll", SetLastError = true, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] [return: MarshalAs(UnmanagedType.BStr)] internal static extern string GetWideString([MarshalAs(UnmanagedType.BStr)]string inputString);

spring boot, multiple datasource

#java #spring #spring_boot


Есть приложение на spring boot, которое использует подключение к двум бд:
application.properties 

spring.datasource.url=jdbc:h2:mem
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

another.datasource.url=jdbc:mysql://localhost:3306/test
another.datasource.username=root
another.datasource.password=
another.datasource.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
another.datasource.driverClassName=com.mysql.jdbc.Driver


RepositoryConfig

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "anotherEntityManagerFactory",
        transactionManagerRef = "anotherTransactionManager",
        basePackages = {"test.repository.mysql"})
public class RepositoryConfig {

    @Autowired
    JpaVendorAdapter jpaVendorAdapter;

    @Value("${another.datasource.url}")
    private String databaseUrl;

    @Value("${another.datasource.username}")
    private String username;

    @Value("${another.datasource.password}")
    private String password;

    @Value("${another.datasource.driverClassName}")
    private String driverClassName;

    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl,
username, password);
        dataSource.setDriverClassName(driverClassName);
        return dataSource;
    }

    @Bean(name = "anotherEntityManager")
    public EntityManager entityManager() {
        return entityManagerFactory().createEntityManager();
    }

    @Bean(name = "anotherEntityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
        Properties properties = new Properties();
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setJpaVendorAdapter(jpaVendorAdapter);
        emf.setPackagesToScan("test.entity.mysql");   // <- package for entities
        emf.setPersistenceUnitName("anotherEntity");
        emf.setJpaProperties(properties);
        emf.afterPropertiesSet();
        return emf.getObject();
    }

    @Bean(name = "anotherTransactionManager")
    public PlatformTransactionManager transactionManager() {
        return new JpaTransactionManager(entityManagerFactory());
    }
}


При такой конфигурации работает подключение к двум бд, но в дефолтном datasource
используются все entity, как сделать, чтобы первый datasource, который в application.properties
как spring.datasource, использовал только свои entity?
    


Ответы

Ответ 1



Элегантное решение предложено в документации к spring-boot Суть в создании собственных ConfigurationProperty и использования атрибута prefix для спецификации. Далее можно разнести их по разным конфигурациям и воспользоваться аннотацией EntityScan Ну и конечно же можно сделать то, что вы пытались сделать, только более аккуратно @Bean public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(customerDataSource()) .packages(Customer.class) .persistenceUnit("customers") .build(); } @Bean public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory( EntityManagerFactoryBuilder builder) { return builder .dataSource(orderDataSource()) .packages(Order.class) .persistenceUnit("orders") .build(); } Для изоляции Entity я использую разные репозитории, пример из документации: @Configuration @EnableJpaRepositories(basePackageClasses = Customer.class, entityManagerFactoryRef = "customerEntityManagerFactory") public class CustomerConfiguration { ... } @Configuration @EnableJpaRepositories(basePackageClasses = Order.class, entityManagerFactoryRef = "orderEntityManagerFactory") public class OrderConfiguration { ... } PS: Если не запуститься с ошибкой об уникальности бинов, то нужно один из бинов пометить как @Primary :)

Ответ 2



Нужно создать 2 бина DataSource. Добавить аннотацию @Qualifier. Дать им разные имена. ссылка на английском @Bean(name = "mysql") @Primary @ConfigurationProperties(prefix="datasource.mysql") public DataSourceProperties fooDataSourceProperties() { return DataSourceBuilder .create() .build(); } @Bean(name = "postgres") public DataSource fooDataSource() { return fooDataSourceProperties().initializeDataSourceBuilder().build(); } Далее используем @Qualifier для определения того, какой именно бин нужен.

std::enable_if_t

#cpp


При попытке использования std::enable_if_t в параметрах шаблона выдает ошибку 


  enable_if_t doesn't name a type


, хотя type_traits подключён, стандарт 11. В чём может быть проблема?
    


Ответы

Ответ 1



std::enable_if_t это из С++14, в С++11 есть только std::enable_if::type Впрочем enable_if_t это псевдоним и его можно написать самому: template using enable_if_t = typename std::enable_if::type;

TaskScheduler и балансировка по ядрам для процессов

#c_sharp #tpl


Как известно, TaskScheduler в TPL раскидывает таски по ядрам (хоть и не гарантирует это).

Возьмем другой случай - порождаются много копий процессов, где внутри поток с многочисленными
Thread.Sleep. В таком варианте поток намертво прилипнет к какому то ядру.

Вопрос в том, если этот поток переделать на task-модель, то будут ли эти таски перемалываться
на разных ядрах или же будут тяготеть к одному и тому же ядру? Хоть это и таски, но
по факту один поток разбивается на таски, чтобы избежать sleep и TaskScheduler может
тяготеть переиспользовать этот же поток (других то в пуле нет)
    


Ответы

Ответ 1



Окей, вам нужно перебрасывать выполнение между ядрами. Это можно сделать вот как. Подсчитываем количество ядер. Это легко: Environment.ProcessorCount. Запускаем столько UI-потоков, сколько у нас ядер. Для этого берём код отсюда, и заимствуем из него класс DispatcherThread. Каждый из них представляет собой поток, в который можно переключиться при помощи await AsyncHelper.RedirectTo(t.Dispatcher); (оттуда же). Нам нужно разбросать эти потоки по ядрам. Это можно сделать как описано здесь. Теперь в нашей async-функции, если мы хотим поменять ядро, просто пишем currentCore = (currentCore + 1) % Environment.ProcessorCount; await AsyncHelper.RedirectTo(threads[currentCore].Dispatcher); Полный код: using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Threading; namespace SO5 { class Program { static List threads; static int currentCoreNo = 0; static void Main(string[] args) { threads = Enumerable.Range(0, Environment.ProcessorCount) .Select(coreNo => new CoreAffineDispatcherThread(coreNo)) .ToList(); Run().Wait(); foreach (var t in threads) t.Dispose(); } static async Task Run() { for (int i = 0; i < 10; i++) { await Task.Delay(100); currentCoreNo = (currentCoreNo + 1) % Environment.ProcessorCount; await AsyncHelper.RedirectTo(threads[currentCoreNo].Dispatcher); var t = Thread.CurrentThread; Console.WriteLine($"Task reporting from thread {t.ManagedThreadId}," + $" thread pool: {t.IsThreadPoolThread}"); } } public class CoreAffineDispatcherThread : IDisposable { public Dispatcher Dispatcher { get; private set; } Thread thread; public CoreAffineDispatcherThread(int coreNumber) { using (var barrier = new AutoResetEvent(false)) { thread = new Thread(() => { Dispatcher = Dispatcher.CurrentDispatcher; barrier.Set(); Thread.BeginThreadAffinity(); #pragma warning disable 618 // The call to BeginThreadAffinity guarantees stable results // for GetCurrentThreadId, so we ignore the obsolete warning int osThreadId = AppDomain.GetCurrentThreadId(); #pragma warning restore 618 // Find the ProcessThread for this thread. ProcessThread thread = Process.GetCurrentProcess() .Threads.Cast() .Where(t => t.Id == osThreadId) .Single(); // Set the thread's processor affinity var cpuMask = 1 << coreNumber; thread.ProcessorAffinity = new IntPtr(cpuMask); Dispatcher.Run(); Thread.EndThreadAffinity(); }); thread.SetApartmentState(ApartmentState.STA); thread.Start(); barrier.WaitOne(); } } public void Dispose() { Dispatcher.InvokeShutdown(); if (thread != Thread.CurrentThread) thread.Join(); } } } static class AsyncHelper { public static DispatcherRedirector RedirectTo(Dispatcher d) { return new DispatcherRedirector(d); } } public struct DispatcherRedirector : INotifyCompletion { public DispatcherRedirector(Dispatcher dispatcher) { this.dispatcher = dispatcher; } #region awaiter public DispatcherRedirector GetAwaiter() { // combined awaiter and awaitable return this; } #endregion #region awaitable public bool IsCompleted { get { // true means execute continuation inline return dispatcher.CheckAccess(); } } public void OnCompleted(Action continuation) { dispatcher.BeginInvoke(continuation); } public void GetResult() { } #endregion Dispatcher dispatcher; } } При тестовом пробеге выдаёт: Task reporting from thread 10, thread pool: False Task reporting from thread 11, thread pool: False Task reporting from thread 12, thread pool: False Task reporting from thread 13, thread pool: False Task reporting from thread 14, thread pool: False Task reporting from thread 15, thread pool: False Task reporting from thread 16, thread pool: False Task reporting from thread 9, thread pool: False Task reporting from thread 10, thread pool: False Task reporting from thread 11, thread pool: False

в чем разница между двумя функциями

#cpp


template  struct A
{
   A foo (A, A) {return A();}
   A foo (A,A) {return A();}
};


В чём разница между 2 функциями?
    


Ответы

Ответ 1



Разницы, как таковой, нет. Просто в одном случае шаблонный класс явно параметризован типом T, а в другом - неявно. В этом можно убедиться на соответствующем примере: template struct A { A foo (A, A) {return A();} A foo (A,A) {return A();} }; int main() { A a; a.foo(a,a); } При компиляции будет выведена ошибка: error: class member cannot be redeclared Т.е. компилятор посчитал функции идентичными. При этом использование краткого имени в данном случае предпочтительнее, т.к. не зависит от изменения имени шаблонного параметра или их кол-ва. В Стандарте языка данный момент описан в п.14.6.1/1: Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). [...] is equivalent to the template-name followed by the template-parameters of the class template enclosed in <>.

Emacs. Можно ли переназначить “без последствий” часть клавиатурных сокращений?

#vim #emacs #hotkeys


Можно ли в Emacs переназначить часть операций с текстом/файлами под более привычные(удобные)
для себя комбинации клавиш, без конфликтов с другими командами? Или стоит изучить те
которые по умолчанию? Пробовал Vim, но там ІМНО не стоит изменять хоткеи по умолчанию,
да и там все довольно логично,да и много команд реализуются одной клавишей, так что
не увидел особой необходимости в замене.
    


Ответы

Ответ 1



Стандартные сочетания архаичны и загадочны, существует несколько подходов в основе. Хорошие примеры: Evil Mode (vim-аддиктам), Spacemacs, Ergo Emacs, Boon, fingers. Технической проблемы тут нет, это вопрос эргономики и управления сложностью конфигурации. Вот из Vim на EMACS можно перейти только благодаря Evil-mode, ценой громоздкого конфига, однако с божьей помощью можно отказаться от Evil-mode, сохранив при том модальность. К вопросу, HJKL по-умолчанию в EMACS выполняют несущественные функции (С-h дублируется ), следовательно, такое можно и перемапить. FBNP-навигацию же, естественную для EMACS, и C-x-... M-x-..., C-c-..., повсеместно используемое - проще оставить нетронутым: (use-package god-mode :bind ( ( "M-i" . god-local-mode ) ( "" . god-local-mode ) ( "C-j" . next-line ) ( "C-k" . previous-line ) ( "C-h" . backward-char ) ( "C-l" . forward-char ) ( "C-d C-f" . delete-forward-char ) ( "C-d C-b" . backward-delete-char-untabify ) ( "C-d C-e" . kill-line ) ( "C-d C-w" . delete-trailing-whitespace ) ( "C-d C-a" . delete-to-begin ) ) ) В стиле Vi, одну клавишу можно использовать для целого ряда действий. Например, удаление строки dd, до конца строки: de, до начала строки: da, вперёд: df, назад: db etc. См. также Hydras. Хороший стиль - повторные нажатия одной клавиши + модификатор: v - старт выделения слова, повторное v - expand-region (увеличить выделение), M-v - contract-region (уменьшить выделение), Shift-V - выделение строки, но и стандартный C-Space продолжает работать. Вариант простого конфига в этом духе Есть альтернативы god-mode, например Modalka.

Ответ 2



Попробовал различные варианты. IMHO стоит изучить клавиатурные комбинации установленные по умолчанию, т.к. и в справке и разной документации везде идут именно они. Как вариант можно назначать неиспользуюмые комбинации или клавиши под свои задачи. Ну, а насчет переназначения стандартных клавиатурных сокращений, то как писал в комментариях @avp, просто потом нужно переназначить и ту функцию, комбинацию которой переназначили другой функции. Например, хотим функцию undo включать по C-z. Смотрим чем занято C-z, нажимаем C-h k C-z: C-z runs the command suspend-frame, which is an interactive compiled Назначаем этой функции другую комбинацию.

Ответ 3



CUA-mode остальное как угодно.

IMEI код на планшетах без 3G

#android #imei


Здравствуйте. Есть программа, где мне нужно получить IMEI код устройства. Проверял
на многих девайсах, но сегодня обнаружил, что его нельзя получить на устройствах без
3G (и соответственно без dialer'а). Получал его таким способом.

TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String l1 = tm.getDeviceId();


На таких устройствах ничего не выдает. Сталкивался ли кто-нибудь с такой проблемой
и как ее можно обойти?

P.S. честно говоря, я даже в настройках устройства не нашел его.
    


Ответы

Ответ 1



Сталкивался ли кто-нибудь с такой проблемой Да, для самого однажды это было неожиданно. P.S. честно говоря, я даже в настройках устройства не нашел его. Все верно, Устройства не имеющие слота под сим-карту, не имеют радиомодуля, следовательно не имеют и IMEI. как ее можно обойти? Использовать в этом случае вместоIMEI, какой либо другой псевдо-уникальный идентификатор. Например: String id = Secure.getString(getContentResolver(), Secure.ANDROID_ID);

Как вывести несколько чисел используя только sql-запрос?

#sql


Нужно написать sql-запрос, который бы возвращал точно такой вывод:

id|
---
1 |
2 |
3 | 
5 |


Представьте, что в базе данных нет ни одной таблицы, и создавать их нельзя.

mysql> SELECT 1 id UNION SELECT 2 id UNION SELECT 3 id UNION SELECT 5 id;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  5 |
+----+
4 rows in set (0,00 sec)


Существует ли более элегантное решение?
    


Ответы

Ответ 1



В MySQL единственный вариант (Алиас нужен только у первого числа): select 1 ID union select 2 union select 3 union select 5 Самый красивый (для малого количества чисел) вариант можно написать в MS-SQL и Postrgess: select * from (values (1),(2),(3),(5)) as t(id) Oracle (С использованием системного типа в качестве коллекции): select column_value ID from table(sys.odcinumberlist(1,2,3,5)) Если чисел значительно больше и они идут просто подряд, то почти универсальный (Из широко распространенных СУБД НЕ работает только в MySQL): with Q as ( select 1 ID union all select ID+1 from Q where ID<5 ) select * from Q Самый лаконичный (IMHO) вариант для большого количества чисел подряд в Oracle: select rownum id from DUAL connect by rownum<6

Ответ 2



Есть такой вариант, используя T-SQL. Физически таблица не создается, так что условие выполнено) Плюс это решение намного более гибкое, если вдруг кто-то решит изменить задачу. declare @t1 as table(ID int) declare @i int=0 while (@i<5) begin set @i+=1 if (@i<>4) insert into @t1 values(@i) end select * from @t1

MySQL. Фигурные скобки. Недокументированная особенность?

#mysql #sql #база_данных #mariadb


Добрый день!

Хотелось бы узнать, почему в MySQL и MariaDB (а может и в других БД) проходит такой
запрос:

SELECT * FROM table WHERE id = {some_text(some_number)};


some_text - любой текст,
some_number - любое целое число (даже отрицательное).

При этом СУБД не ругается на синтаксис, если значение соответствует регулярному выражению.
И даже возвратит результат аналогично запросу

SELECT * FROM table WHERE id = some_number;


Это какой-то специальный синтаксис или что? Спасибо.
    


Ответы

Ответ 1



В документации на MySQL читаем: {identifier expr} is ODBC escape syntax and is accepted for ODBC compatibility. The value is expr. The curly braces in the syntax should be written literally; they are not metasyntax as used elsewhere in syntax descriptions. В ODBC в фигурных скобках задаются некоторые особые последовательности, такие как указание интерпретации текста как даты {d '2016-04-28'} (так же времени 't' и временной метки 'ts'). Вызова функции {fn curdate()} (так же 'call'), задания GUID {guid '00123...'}. Некоторые СУБД поддерживают этот функционал в полной мере, например MS SQL. В них работают только конкретные последовательности. В MySQL для синтаксической совместимости запросов с ODBC внутри фигурных скобок принимается произвольный текст в качестве идентификатора, значение после него интерпретируется как будто оно написано само по себе.

Есть название у “такой” операции произведения векторов?

#математика #геометрия #терминология


Получаю вектор путем произведения двух единичных векторов. Складываю их так как складывают
косинусы и синусы. 

v3.x = v1.x * v2.x - v1.y * v2.y
v3.y = v1.y * v2.x + v1.x * v2.y


Есть ли у этой операции название в контексте векторов?
    


Ответы

Ответ 1



Комплексное умножение примерно так и выглядит. (a + ib)(c + id) = (ac - bd) + i(bc + ad) ...где i - мнимая единица, для которой i^2 = -1 ...которое в геометрической модели для векторов (на комплексной плоскости) единичной длины даёт другой вектор единичной длины, аргумент которого (угол с Re+, горизонтальной осью "вправо") является суммой аргументов составляющих. Если бы они были не единичными, длина результата была бы произведением их длин.

Ответ 2



Подозреваю, что названия нет, т.к. операция несимметричная - для X вы считаете разницу квадратов (похоже на скалярное произведение, только знак не тот), а для Y - кросс продукт (и опять знак не тот). Интересно, а как вы пришли к этой формуле и как используете результаты ее вычисления? Может быть это 2 несвязанных параметра и знаки не те - тогда это просто скалярное и векторное произведения.

Как правильно задать переменные окружения в Ubuntu? [дубликат]

#ubuntu


        
             
                
                    
                        
                            This question already has answers here:
                            
                        
                    
                
                        
                            Не сохраняются переменные XUBUNTU
                                
                                    (3 ответа)
                                
                        
                                Closed 3 года назад.
            
                    
Здравствуйте,я недавно стал осваивать данную ОС. Если в Windows было понятно, куда
прописывать переменные окружения(для java, gradle и ant) то в Ubuntu у меня возникли
трудности. В документации анта написано,что надо воспользоваться командой export, однако
потом после завершения сеанса, эта переменная слетает. Я так понял, что дабы переменную
прописать в env "насовсем" надо ее указать в неком bash. Но я толком не нашел пошаговой
инструкции в сети(хотя может неправильно спрашивал гуггл) о том, какую команду написать,
чтобы зайти в bash и прописать нужные переменные. Объясните пожалуйста как это сделать?
    


Ответы

Ответ 1



ls ~ Там увидишь либо .bashrc либо .bash_profile Открыть для изменения nano ~/.bashrc В конце файла вписать необходимое, например export DEMO_ENV=success Нажать ctrl+x затем y и enter После выполнить source ~/.bashrc

Ответ 2



Был ответ вот открываете файл /etc/environment записываете нужные переменные: export VAR_NAME=VAR_VALUE

Сортировка ключей словаря по возрастанию

#python #django


Приветствую.

Дан словарь 

{'2.3': ['0', '1'], '4.9': ['0', '1'], '4.7': ['0', '1'], '3.5': ['0', '1'], '2.4':
['0', '1'], '3.17': ['0', '1'], '4.8': ['0', '1'], '3.6': ['0']}


Необходимо отсортировать его по ключам в порядке возрастания. Пытаюсь сделать так:

for x in sorted(dict_list.keys(), key=lambda x : float(x[:len(x)-1])):
    print(x)


Получается 

2.3, 2.4, 3.5, 3.6, 3.17, 4.9, 4.7, 4.8


В общем, сортирует он явно как-то не так. Подскажите, пожалуйста, как отсортировать
в порядке возрастания ключей? Заранее спасибо.
    


Ответы

Ответ 1



Если вы хотите вернуть отсортированный список ключей/значений, можно просто отсортировать словарь по его ключам: for k in sorted(dict_list.keys()): print (k, ':', dict_list[k]) Если нужно хранить отсортированные пары ключ/значение, попробуйте использовать OrderedDict from collections import OrderedDict #отсортирует по возрастанию ключей словаря OrderedDict(sorted(dict_list.items(), key=lambda t: t[0])) Документация по OrderedDict UPD по просьбе автора сортировка по возрастанию с условием, что 3.17 должно быть последним for i in sorted(dict_list.items(), key=lambda x: (len(x[0]), x[0])): print (i[0]) Если 3.17 должно быть первым, можно просто добавить - в условие: ..lambda x: (-len(x[0]), x[0]))

Обучалка пользователей интерфейсу на сайте

#javascript #html #css


Наверняка каждый натыкался на сайт при посещении в первый раз которого, темнел экран
и загорался только какой то конкретный элемент на сайте с выводом сообщения о том что
это за элемент и для чего он, как им пользоваться. Этакое обучение сложному интерфейсу
сайтов. Помогите пожалуйста найти примеры таких сайтов. Возможно кто то знает как такое
называется, есть ли какие то наработки для создания такого обучения на сайте или это
всё исключительно с нуля на js+css писать?
    


Ответы

Ответ 1



Это называется экскурсия по сайту или тур по сайту и другими похожими названиями ) Например тут: http://www.coolwebmasters.com/reviews/3969-11-awesome-jquery-site-tour-plugins-for-guiding-users-with-style.html http://dicim.net/delam-ekskursiyu-po-sajte.html http://ruseller.com/lessons.php?rub=32&id=820 (вроде похожий тур как во 2 пункте) https://myrusakov.ru/jquery-tour.html

Ответ 2



Я рекомендую этот плагин: https://github.com/easelinc/tourist пример можно посмотреть здесь: http://easelinc.github.io/tourist/

Ответ 3



Мы использовали introJS, рекомендую: просто, бесплатно, надёжно.

FTP сервер возвращает ошибку 227

#c_sharp #ftp


Доброго времени суток. Собственно, сабж в титле.

Сама ошибка выглядит по меньшей мере непонятно:


  "Удаленный сервер возвратил ошибку: 227 Entering Passive Mode
  (192,168,1,136,39,43)"


при чём тут ошибка? мне же и нужно в пассивный режим зайти... ладно, едем дальше.

Опыта работы с ftp нет, нашел какие то исходники, собрал проект, выдаётся вышеупомянутое
исключение. Упростил код до невозможности, ошибка осталась. Странность ещё в том, что
в totalCommander соединение удаётся,

    private void btn_connect_Click_2(object sender, RoutedEventArgs e)
    {
        FtpWebRequest requestDir = (FtpWebRequest)FtpWebRequest.Create("ftp://192.168.1.136/");
        requestDir.Credentials = new NetworkCredential("username", "password");
        requestDir.Method = "LIST";
        requestDir.UseBinary = true;
        requestDir.EnableSsl = false;
        requestDir.UsePassive = true;

        try
        {
            WebResponse response = requestDir.GetResponse();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString() + ": \n" + ex.Message);
        }
    }


пробовал так же ставить 


  requestDir.UsePassive = false;


Получил ошибку 500, синтаксическую, мол, команда не опознана.
Буду благодарен, если мне объяснят, что я делаю не так)
    


Ответы

Ответ 1



Задайте requestDir.UsePassive = false; Далее, в чем возможно заключается сама ошибка и почему сервер возвращает ошибку 500. Дело в том что Вы задали команду вывода списка файлов: requestDir.Method = "LIST"; Попробуйте сначала вывести список директорий: requestDir.Method = WebRequestMethods.Ftp.ListDirectory; или так: requestDir.Method = "NLST"; Список предустановленных команд FTP в .NET: https://msdn.microsoft.com/ru-ru/library/system.net.webrequestmethods.ftp(v=vs.110).aspx

Ответ 2



Без костылей решить не удалось. Банально скачал левую dll для работы с FTP (сам тут человек новый, так что ссыль на Cyberforum, где первоначально задавал вопрос, как залить архив - не увидел))). Далее работал так: FTP _ftp; private void btn_Connect_click(object sender, RoutedEventArgs e) { _ftp = new FTP(); _ftp.TransferCompleteEvent += new FTP.TransferCompleteEventHandler(ftp_TransferCompleted); _ftp.HostAddress = "192.168.1.136"; _ftp.UserName = "username"; _ftp.Password = "password"; _ftp.Port = 21; _ftp.UseBinary = true; _ftp.OverwriteFiles = true; _ftp.OverwriteReadOnlyFiles = false; _ftp.EnableSSL = false; _ftp.UsePassive = true; _ftp.Connect(); string localPath = new FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).DirectoryName; _ftp.CurrentDirectory = @""; _ftp.DownloadDirectoryAsync(localPath, @"datalog"); } private void ftp_TransferCompleted(Object sender, TransferCompletedEventArgs e) { _ftp.Disconnect(); //... }

Структура данных для упорядочения двумерных точек

#cpp #алгоритм #вычислительная_геометрия


Столкнулся с задачей - имеется достаточно много (ну, скажем, десятки тысяч) точек
на плоскости (возможно, позже будут в трехмерном пространстве, но пока вопрос о плоскости).
Требуется много раз решать подзадачу - выбирать для итераций точки множества, находящиеся
на расстоянии не более чем L от некоторой точки (вообще говоря, не входящей в это множество
точек).

Подскажите, какую структуру данных использовать, чтоб не перебирать все точки подряд.
Не могу даже сообразить, как правильно проГУГЛяться, что именно искать - как запрос
сформулировать.

Рабочий язык - С++.
    


Ответы

Ответ 1



Можно разбить плоскость на квадраты размера L. Для каждого квадрата сохраняем список попавших в него точек. Для новой точки находим ее квадрат и перебираем содержимое этого, а также соседних квадратов. Как это сделать эффективно по памяти описано, например, здесь: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.35.2471&rep=rep1&type=pdf

Ответ 2



Если задача массовая, т.е. если исходный набор точек стабилен и к этому стабильному набору точек делается относительно большое количество запросов, то хороший вариант: евклидова диаграмма Вороного для исходного набора плюс какой-нибудь алгоритм для быстрого point-location. Когда на вход приходит точка-запрос, то мы выполняем point-location, чтобы определить, в какой регион диаграммы Вороного попала точка-запрос. После этого рассматриваем этот регион Вороного и обходим поиском ширину соседние регионы Вороного до тех пор, пока мы заведомо не выйдем за радиус L. Понятное дело, что построение диаграммы Вороного и подготовка к point-location - это относительно "тяжелый" препроцессинг, по каковой причине, как я сказал выше, такой подход имеет смысл при стабильном входном наборе и относительно большом количестве запросов к нему, т.е. когда результаты препроцессинга сохраняют свою актуальность "долго". Другой вариант - k-d-tree.

Равносторонний треугольник на Canvas

#java #android #canvas


Хочу нарисовать равносторонний треугольник на Canvas, у меня получается четырехугольник,
как это исправить?  

Point a = new Point(0, 100);
Point b = new Point(50, 0);
Point c = new Point(100, 100);

Paint triangle = new Paint();
triangle.setColor(Color.RED);

Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.lineTo(b.x, b.y);
path.lineTo(c.x, c.y);
path.lineTo(a.x, a.y);
path.close();
canvas.drawPath(path, triangle);

    


Ответы

Ответ 1



Point a = new Point(0, 100); Point b = new Point(50, 0); Point c = new Point(100, 100); Paint triangle = new Paint(); triangle.setColor(Color.RED); Path path = new Path(); path.setFillType(Path.FillType.EVEN_ODD); path.moveTo(a.x, a.y); //добавьте эту строку path.lineTo(b.x, b.y); path.lineTo(c.x, c.y); path.lineTo(a.x, a.y); path.close(); canvas.drawPath(path, triangle); Это потому, что вы не выставляете начальную точку (которая поумолчанию получается в 0,0)

Живой поиск System.Windows.Controls.ComboBox C# WPF

#c_sharp #wpf #combobox #фильтр


Добрый день/вечер, столкнулся с задачей сделать контекстный поиск в Windows.Controls.ComboBox.
Поиск производится по любому вхождению.
Например имеем: Вася, Федя, Вика
Набираем "в", получаем: Вася, Вика; "ва" остается Вася; "дя" - Федя.
Надеюсь смысл понятен.
Доработал класс ComboBox:

using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
using ProgrammingWeapons;

namespace Telemetria.UI
{
    public class ComboBoxFinder : ComboBox
    {
        private bool _isAutoFilter = true;
        public bool IsAutoFilter {
            get { return _isAutoFilter; }
            set { _isAutoFilter = value; }
        }

        private readonly List _fullList;
        private string _filterText { get; set; }
        public ComboBoxFinder() {
            IsTextSearchEnabled = false; // отключение автовыбора первого элемента,
иначе не работает поиск
            _fullList = new List();
            _filterText = "";

            KeyUp += (sender, args) => Filter(Text);
            KeyDown += (sender, args) => {
                if (_isAutoFilter) IsDropDownOpen = true;
                Filter(Text);
            };
            SelectionChanged += (sender, args) => {
                //if(SelectedItem != null)
                    Filter("");
            };
        }

        public void Filter(string text) {
            if (!_isAutoFilter) return;
            if (text.IsNull()) return;

            _filterText = text;
            if (_fullList.Count < 1)
                foreach (var isc in Items.SourceCollection)
                    _fullList.Add(isc);

            var tmpSourceList = _fullList.Where(fl => fl != null && fl.ToString().ToUpper().Contains(_filterText.ToUpper())).ToList();
            //if (tmpSourceList.Count == 1) return;
            ItemsSource = tmpSourceList;
        }
    }
}


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

Какая ошибка когда появляется пока не пойму. Подскажите куда смотреть. WinForms использовать
нельзя, т.к. не вписывается в проект. В интернете ничего не нарыл, такое ощущение что
все под WinForms пишут (
    


Ответы

Ответ 1



У меня работает так: XAML: Code-behind: void OnComboboxTextChanged(object sender, RoutedEventArgs e) { CB.IsDropDownOpen = true; // убрать selection, если dropdown только открылся var tb = (TextBox)e.OriginalSource; tb.Select(tb.SelectionStart + tb.SelectionLength, 0); CollectionView cv = (CollectionView)CollectionViewSource.GetDefaultView(CB.ItemsSource); cv.Filter = s => ((string)s).IndexOf(CB.Text, StringComparison.CurrentCultureIgnoreCase) >= 0; } Результат:

Битрикс и Яндекс касса , почему разные номера заказов

#битрикс #яндекс


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

Check URL https://site1.ru/bitrix/tools/sale_ps_result.php

Aviso URL https://site1.ru/bitrix/tools/sale_ps_result.php

Success URL http://site1.ru/bitrix/tools/sale_ps_success.php

Fail URL http://site1.ru/bitrix/tools/sale_ps_fail.php


т.е ссылки от первого сайта, когда клиент делает заказа на втором сайте site2.ru
то в случае успешной оплаты соответственно от попадает на site1.ru , мне нужно сделать
перенаправление на site2.ru т.е на тот сайт в котором был сделан заказ. 

В файле /bitrix/tools/sale_ps_success.php я хочу сделать редирект , как мне показалось
что это можно сделать получив ID заказа из URL который формирует яндекс касса в случае
успешной оплаты, в котором есть параметр orderNumber=273 . Но заметил что номер заказа
в яндекс кассе не совпадают с номером заказа на сайте. К примеру если а ЯК это 273
то на сайте 280 . 
И итог того что хочу сделать:
Получить номер заказа из URL, в файле sale_ps_success.php проверить  какому сайту
пренадлежит заказ и сделать редиррект на нужный сайт https://site2/personal/order/make/?ORDER_ID=".$OrderID

Кто нибудь сталкивался с подобным? Почему номера заказов разные?  

По совету Mihanik71  использовать класс Bitrix\Sale\Internals\PaymentTable оказалось
верным решением за что ему большое спасибо, ниже пример кода который в файле(sale_ps_success.php)
я запилил у себя на сайте:



   $orderNumbder = $_GET['orderNumber']; // тут приходит номер оплаты 


CModule::IncludeModule("sale");
 c
$obj = new Bitrix\Sale\Internals\PaymentTable; // класс с помощью которого можно
вытащить id заказа, был обнаружен с помощью модуля live ip

$shimpentCode = $obj->getlist(array('select' => array('ID','ORDER_ID'),'filter' =>
array('ID' => $orderNumbder)))->fetch();

$SiteOrder = CSaleOrder::GetByID($shimpentCode['ORDER_ID']); // вытаскиваем свойства
заказа, нас интересует поле LID

$SiteID = $SiteOrder['LID']; // id сайта на котором была покупка 

$OrderID = $shimpentCode['ORDER_ID'];

$successUrl = "";
// простое условие , выбираем куда редиректить покупателя 
if($SiteID == 's1'){
   $successUrl = "https://site1.ru/personal/order/make/?ORDER_ID=".$OrderID . "&PAY=Y";
// параметр PAY=Y добавил для своих нужд   
}
if($SiteID == 's2'){
   $successUrl = "https://site2.ru/personal/order/make/?ORDER_ID=".$OrderID . "&PAY=Y";
}


$successPath = COption::GetOptionString("sale", "sale_ps_success_path", "/");

   if(!empty($successUrl)){
      LocalRedirect($successUrl);   
   }
}

    


Ответы

Ответ 1



Есть класс Bitrix\Sale\Internals\PaymentTable там по номеру оплаты поле ID можно получить номер заказа ORDER_ID.

Ответ 2



Ваше решение не совсем верно с точки зрения нового ядра D7 Битрикс. Нежелательно использовать API старого ядра, когда есть аналоги нового. К тому же было бы неверно использовать Bitrix\Sale\Internals\PaymentTable, потому что нет гарантий, что данное поведение функции сохраниться в дальнейшем, а именно: Подпространство Bitrix\Sale\Internals содержит классы для работы с таблицами модуля Интернет-магазин. Важно! Прямое использование данных классов не рекомендуется и совместимость не гарантируется. https://dev.1c-bitrix.ru/api_d7/bitrix/sale/internals/ Гораздо лучше использовать доступные публичные статические методы: //Вместо CModule::IncludeModule("sale"); \Bitrix\Main\Loader::includeModule('sale'); //Вместо создания объекта класса Bitrix\Sale\Internals\PaymentTable $payment = \Bitrix\Sale\Payment::getList( array( "filter" => array('ID' => $_GET['orderNumber']), "select" => array('ORDER_ID') ) ); $paymentFields = $payment->fetch(); //Поднимаем объект заказа по id $order = \Bitrix\Sale\Order::load($paymentFields['ORDER_ID']); //Получаем поле 'LID' $SiteID = $order->getField('LID'); //'ID' заказа можно получить и из заказа тоже, например: $OrderID = $order->getId() $OrderID = $paymentFields['ORDER_ID']; $successUrl = ""; // простое условие , выбираем куда редиректить покупателя if($SiteID == 's1') { $successUrl = "https://site1.ru/personal/order/make/?ORDER_ID=".$OrderID . "&PAY=Y"; // параметр PAY=Y добавил для своих нужд } if($SiteID == 's2') { $successUrl = "https://site2.ru/personal/order/make/?ORDER_ID=".$OrderID . "&PAY=Y"; } if(!empty($successUrl)) { LocalRedirect($successUrl); } Можно еще и $_GET['orderNumber'] через API получать, но это уже так, легкое ООП извращение: $request = \Bitrix\Main\Context::getCurrent()->getRequest(); $request->get("orderNumber");

SQL, BETWEEN по дате без учета года

#mysql #sql


Добрый день, подскажите пожалуйста как правильно написать запрос для выборки интервала
времени но с игнорированием года.
Те. например есть интервал '31.07 22:00:00' - '31.08 22:00:00' и нужно пробежатся
по таблице игнорируя год и выбрать в таком промежутке все вхождения.
(вариант создания отдельного запроса для каждого года отдельно -  не комильфо)

для Between нужен год, подскажите пожалуйста как быть в таком случае?

Использую mysql 5.6
    


Ответы

Ответ 1



select * from table where DATE_FORMAT(date,'%m%d%h%i') between '07312200' and '08312200' Обратите внимание, формат записи даты Месяц-День-Часы-Минуты, что бы сравнение на интервал правильно работало.

Где можно найти Java / Android проекты для новичков? [закрыт]

#java #android


        
             
                
                    
                        
                            Closed. This question is opinion-based. It is not currently
accepting answers.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Want to improve this question? Update the question so
it can be answered with facts and citations by editing this post.
                        
                        Closed 3 года назад.
                                                                                
           
                
        
У меня не очень обширные знания в программировании: обычно создавал программы типа
баз данных, например "Студенты" (кто и когда опоздал, и тому подобное). Также создавал
телефонную книжку и прогноз погоды на Android.

Собственно последнее мне очень понравилось. Udacity предоставляет курс, где поэтапно
рассказано, как создать программу. Аналогов я больше не находил (чтобы какой-то большой
и интересный проект так классно объясняли). Но очень хотелось бы! Ну или хотя бы идеи,
что создать новичку. Вот сейчас подсказали счётчик коммунальных услуг; также в интернете
нашел этот список

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


Ответы

Ответ 1



Рекомендую тебе Android 6 для профессионалов 2015 Актуальная книга на русском языке с упором на практику со множеством интересных проектов в коробке, в ходе рассмотрения которых автор затрагивает большинство аспектов фреймфорка, дающие достаточно знаний для старта кодинга на Android. Ну а после книги развивай умения и знания в ходе поиска решений для конкретных задач, чтением литературы и в процессе общения с хорошими людьми. PS (Не смотря на то, что в русском варианте книга называется для "... профессионалов", книга читается легко и после прочтения глав не остается неясностей, так автор тщательно разжевывает понятия и неявные участки кода) Где-то еще скачивал исходники проектов книги, но, к сожалению, ссылку не смог найти вновь. Ну, а если же тебе интересны видеоуроки, то в России самый крупнейший - это безусловно StartAndroid. Так же тоже следует обратить внимание на http://developer.alexanderklimov.ru/android/ (P.S наимилейший сайт в рунете)

Можно ли убрать с карты Google Maps значки “станции метро”, “зоопарки”, “остановки”?

#google_maps_api #карты




Реализована ли в Google Maps такая возможность?
    


Ответы

Ответ 1



Можно с использованием Styled Maps. Например, чтобы убрать все автобусные остановки, при создании объекта google.maps.Map в options нужно указать: styles: [{ featureType: 'transit.station.bus', elementType: 'all', stylers: [ { visibility: 'off' } ] ]}

Ответ 2



Тут можно сгенерировать карту выставив все настройки через интерфейс и получить код: https://mapstyle.withgoogle.com/ Вот код полученный из того генератора для решения Вашей проблемы: [ { "featureType": "poi.attraction", "elementType": "labels", "stylers": [ { "visibility": "off" } ] }, { "featureType": "transit.station.bus", "elementType": "labels", "stylers": [ { "visibility": "off" } ] }, { "featureType": "transit.station.rail", "elementType": "labels", "stylers": [ { "visibility": "off" } ] } ]

Передача сообщений между потоками - C#

#c_sharp #wpf #многопоточность #task


Есть у меня простой класс-логгер:

static class Logger
{
    public delegate void Message(string msg);

    static public event Message OnMessage;

    static public void SendMessage(string msg)
    {
        OnMessage?.Invoke(msg);
    }
}


Я из любых мест приложения отправляю ему сообщения по типу: Logger.SendMessage("Получена
команда на запуск");

При загрузке window (WPF) я подписываюсь на события логгера и вывожу лог в textbox

Пока приложение было однопоточным всё отлично работало, но теперь методы отлажены
и надо всё распараллелить (идет обращение к 40 БД на разных хостах поэтому всё хорошо
параллелится)

Но теперь возникла проблема - при попытке прочитать отправленное сообщение из другого
потока возникает Exception 


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


Как это грамотно и с малой кровью исправить?
    


Ответы

Ответ 1



Смотрите. Проблема в том, что события доставляются в том потоке, который отправляет события. Поэтому у вас подписчики событий получают событие каждый раз в разных потоках. Если подписчик — UI-код, который просто выводит текст в UI, то при приходе сообщения из неглавного потока происходит проблема. Есть несколько путей починки вашего кода. Можно привязать логгер к главному потоку. При этом сообщения будут доставляться только в главном потоке, и соответственно UI-код будет всегда работать «как надо». static class Logger { static Lazy dispatcher = new Lazy(() => Application.Current.Dispatcher); public delegate void Message(string msg); static public event Message OnMessage; static public void SendMessage(string msg) { if (dispatcher.Value.CheckAccess()) OnMessage?.Invoke(msg); else dispatcher.Value.InvokeAsync(() => OnMessage?.Invoke(msg)); } } Это, наверное, не самое лучшее архитектурное решение, т. к. при этом логгер получается зависимым от WPF, то есть модель получает зависимость от VM (что не позволит использовать её повторно в других программах). Зато этот метод решает задачу наиболее просто: другие переделки при этом не нужны. Можно считать логгер не привязанным ни к какому потоку, тогда UI-код должен проверять, в каком потоке он запущен, и при необходимости пользоваться Dispatcher.InvokeAsync. Logger.OnMessage += s => { if (Dispatcher.CheckAccess()) LogContainer.Text += (s + "\n"); else Dispatcher.InvokeAsync(() => LogContainer.Text += (s + "\n")); }; Это более правильный подход, но здесь придётся потенциально править все места, где происходит подписка на сообщения от логгера. Впрочем, такое место в программе, судя по всему, одно. Вы можете использовать модные в этом сезоне Reactive Extensions, и переписать ваш класс на них: using System.Reactive.Linq; using System.Reactive.Subjects; static class Logger { static ISubject subject = Subject.Synchronize(new Subject()); public static IObservable Messages => subject; static public void SendMessage(string msg) => subject.OnNext(msg); } Подписка при этом выглядит так: Logger.Messages.ObserveOnDispatcher().Subscribe(s => LogContainer.Text += (s + "\n")); Максимальная гибкость, LINQ на сообщениях, доставка в произвольный поток, навесные плюшки наподобие подавления слишком частых или повторяющихся сообщений поставляется в комплекте, бонусом ощущение собственной крутости, функциональности и трендовости. Минус — вам придётся-таки разобраться с этим самым Rx (муа-ха-ха!). Или это можно считать плюсом, да. (Think positive.) Не забудьте подключить из nuget System.Reactive.Core, System.Reactive.Interfaces, System.Reactive.Linq и System.Reactive.Windows.Threading.

Слияние в пользу определенной ветки

#git


Допустим я нахожусь в ветке dev. Ввожу команду git merge new_change, после этого
появляется много участков в коде по типу этого:

<<<<<<< HEAD
            message_copy = {'filepath': filepath, 'guid': guid}
=======
            message_copy = {"filepath": filepath, "guid": guid}
>>>>>>> new_change


Как мне сделать слияние таким образом, что при возникновении подобных проблем они
решались в пользу dev ?
    


Ответы

Ответ 1



Если под термином "подобные проблемы" понимаются любые конфликты, то у команды git merge есть параметры -Xours и -Xtheirs, позволяющие автоматически разрешать конфликт в пользу "нашей" или "их" версии соответственно. Например: git merge -Xours new_change Но я настоятельно не рекомендую их использовать в автоматическом режиме.

Как сделать, чтобы при создании разметки по умолчанию создавался LinearLayout вместо RelativeLayout?

#android #android_studio


Как сделать, чтобы при создании разметки по умолчанию создавался LinearLayout вместо
RelativeLayout?
    


Ответы

Ответ 1



https://stackoverflow.com/questions/36645161/how-to-change-default-template-layout-files-in-android-studio тут есть ответ. Измени шаблон по умолчанию в файле по пути: ...\Android\Android Studio\plugins\android\lib\templates\activities Дальше, смотря какой шаблон нужно изменить. Например, для EmptyActivity меняй файл по пути: ...\Android\Android Studio\plugins\android\lib\templates\activities\common\root\res\layout\simple.xml.ftl Там можешь изменить шаблон, как тебе угодно, в том числе изменить корневой элемент на LinearLayout.

Как удалить локальную ссылку на несуществующую уже удаленую ветку?

#git


В локальном репозитории вижу следующую картину:

|
|
*  origin/
|


На сервере (битбакет) этой ветки уже нет и давно. 

git push origin : 


логично выдает

remote ref doesn't exist


Как мне удалить локальную ссылку на несуществующую уже удаленную ветку?
    


Ответы

Ответ 1



Нужно выполнить команду git fetch с флагом prune. git fetch --prune Если удаленных репозиториев несколько, можно указать конкретный: git fetch origin --prune То же самое: git remote prune origin Это удалит референсы на ветки удаленного репозитория, которых больше не существует. При этом у вас могут еще остаться локальные ветки с которыми вы работали и которые раньше ссылались на несуществующие ветки удаленного репо. Эти ветки надо поудалять ручками с помощью: git branch -d

Что это за кнопка?

#java #android #ui #android_button


Подскажите, что это за кнопка? Как она называется? И как такую сделать? 
    


Ответы

Ответ 1



Это switch button. Пример реализации. По ссылке: XML: Activity: import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.CompoundButton; import android.widget.Switch; import android.widget.TextView; public class MainActivity extends AppCompatActivity { Switch switchButton, switchButton2; TextView textView, textView2; String switchOn = "Switch is ON"; String switchOff = "Switch is OFF"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.switch_button_example); // For first switch button switchButton = (Switch) findViewById(R.id.switchButton); textView = (TextView) findViewById(R.id.textView); switchButton.setChecked(true); switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean bChecked) { if (bChecked) { textView.setText(switchOn); } else { textView.setText(switchOff); } } }); if (switchButton.isChecked()) { textView.setText(switchOn); } else { textView.setText(switchOff); } // for second switch button switchButton2 = (Switch) findViewById(R.id.switchButton2); textView2 = (TextView) findViewById(R.id.textView2); switchButton2.setChecked(false); switchButton2.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean bChecked) { if (bChecked) { textView2.setText(switchOn); } else { textView2.setText(switchOff); } } }); if (switchButton2.isChecked()) { textView2.setText(switchOn); } else { textView2.setText(switchOff); } } } Как будет выглядеть:

Шейдеры в unity3d

#unity3d #shader


Рискуя нахватать по шапке, я все таки задам этот вопрос. 

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

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





Прочитав несколько статей все же никак не могу разложить по полочкам это все в голове.
Прочитал как именно реализуется размытие по гауссу (усреднение значение rgb ближайших
пикселей на спрайте по каждому столбцу/линии), понимаю что к этому необходимо прицепить
прозрачность и менять одно в зависимости от другого, но, например, как это все сделать,
непосредственно в коде, прицепив еще и анимацию сюда, абсолютно не понимаю (хотя и
понимаю что есть св-во _TransVal есть параметр #pragma alpha, который за прозрачность
отвечает).

Т.к. писать шейдеры не приходилось ранее и, если честно, те статьи, что нашел в инете,
не сильно внесли ясности - именно в вопрос реализации. Cамо понимание что такое шейдер,
какие они бывают и зачем - у меня присутствует. 

Буду благодарен, если кто-нибудь возьмется поэтапно расписать на примере моей задачи
процесс реализации с пояснениями: мол нам нужно это для этого, а это для этого, вот
так мы будем изменять блюр, вот так прозрачность, а вот так анимацию присобачим.

UPDATE
Чего я смог добиться: Регулируя значения sigma и cutoff собранного мною в бреду непонимания
(в силу отсутствия опыта в написании шейдеров) происходит все то что мне необходимо
кроме одного НО, спрайт позади всего этого откуда он и как от него избавиться я так
и не смог понять. 



http://g.recordit.co/rUaGYdynQ3.gif (картинка в большем разрешении). 

Данный материал (с шейдером) применен на image. image и canvas в котором они находятся
вынесены на слой transparentfx который рендерит отдельная камера (на основной отключен
этот слой). Осталось понять как отключить (или убрать из рендера) этот спрайт позади
и в минимальном виде задача будет решена (прозрачность на краю заполнения уже не суть
важно хотя бы с блюром разобраться).

UPDATE 2 задача решается если выставить на самом Image type filled и крутить ручку
fill amount. Но остается непонятным, как по-человечески отключить рендер image'a, оставив
рендер только того, что есть. 

Результат работы шейдера:



UPDATE от 29 декабря
Поняв что с написанием по человечески у меня все не очень, я попробовал использовать
Shader Forge и в нем вроде бы добился необходимого эффекта (одного из необходимых).
Но получилось очень странно, в том смысле что в окне инспектора в юнити эффект отображался
как надо а в сцене гейм он вел себя "упрощенно" так сказать. Смотри гиф ниже для лучшего
понимания.
Гиф1, окно инспектора.
Гиф2, окно игры
    


Ответы

Ответ 1



А стоит все это через единый шейдер городить? Делал бы так: Чтобы использовать разблуреное изображение вам понадобиться еще одна камера которая через постэффект BLUR будет писать в RenderTexture экран без накладываемого изображения(поместите его в отдельный Layer и в этой камере этот слой выключите). Далее делаем анимацию проявления любой формы которая вас интересует. Cделайте форму с анимированием маски по альфе. А дальше уже все просто, для анимированной формы используем простейший материал который в зависимости от прозрачности текстуры на материале рисует через смешение либо с основной текстуры либо с размытой в RenderTexture.

Ответ 2



Ответ, часть 2. Так как возможно это будет нагрузка на шейдер, то можно некоторые части также довыносить в управляющий скрипт. Оставить тут только применение значений. В итоге шейдер и скрипт могут быть такими: Shader "Custom/RadialFill_MoreScriptControl" { Properties { [PerRendererData]_MainTex ("MainTex", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _OpacityRotator ("Opacity Rotator", Range(-360, 360)) = -360 // два полных оборота _TextureRotator ("Texture Rotator", Range(0, 360)) = 360 [MaterialToggle] _FillClockwise ("Fill Clockwise", int ) = 1 [HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [HideInInspector] _CutoffRightBottomLeftTop ("cRBLT", Float) = 1.0 [HideInInspector] _OpRightBottomLeftTop ("oRBLT", Float) = 1.0 [HideInInspector] _OpVector ("OpVector", Vector) = (1, -1, 0, 0) [HideInInspector] _ReverseMaskCoords ("_ReverseMaskCoords", int) = 0 } SubShader { Tags { "IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent" "CanUseSpriteAtlas"="True" "PreviewType"="Plane" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } Blend One OneMinusSrcAlpha Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #define UNITY_PASS_FORWARDBASE #pragma multi_compile _ PIXELSNAP_ON #include "UnityCG.cginc" #pragma multi_compile_fwdbase #pragma exclude_renderers gles3 metal d3d11_9x xbox360 xboxone ps3 ps4 psp2 #pragma target 3.0 static const float TAU = float(6.283185); // это 2 * PI, кто не знает uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float4 _Color; uniform float _OpacityRotator; uniform float _TextureRotator; uniform fixed _FillClockwise; uniform fixed _CutoffRightBottomLeftTop; uniform fixed _OpRightBottomLeftTop; uniform float2 _OpVector; uniform int _ReverseMaskCoords; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; }; // матрица вращения float2x2 getMatrix(float angle) { float r_cos = cos(angle); float r_sin = sin(angle); return float2x2(r_cos, -r_sin, r_sin, r_cos); } // формирование маски float2x2 getMask(float oAtan2MaskNormalized, float rotator, int isRotatorSubtract) { float oAtan2MaskRotatable = isRotatorSubtract ? oAtan2MaskNormalized - rotator : rotator - oAtan2MaskNormalized; return ceil(oAtan2MaskRotatable); } float getNormalizedAtanMask(float2 maskChannels, int reverseMaskCoords) { float atan2var = reverseMaskCoords ? atan2(maskChannels.r, maskChannels.g) : atan2(maskChannels.g, maskChannels.r); return (atan2var / TAU) + 0.5; } VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.uv0 = v.texcoord0; o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(_Object2World, float4(v.tangent.xyz, 0.0)).xyz); o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); o.posWorld = mul(_Object2World, v.vertex); o.pos = mul(UNITY_MATRIX_MVP, v.vertex ); #ifdef PIXELSNAP_ON o.pos = UnityPixelSnap(o.pos); #endif return o; } float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex)); /*** Общее начало для opacity и cutoff, помогающее переключать вращение по/против часовой стрелки BEGIN ***/ // float2(1, -1) - по часовой, float2(1, 1) - против часовой float2 clockCounterDirection = _FillClockwise ? float2(1, -1) : float2(1, 1); // по умолчанию "обрезание" начинается слева. // умножение на -1 для того, чтоб началось справа.....просто потому, что я так хочу =) float2 CommonStartAndSwitcher = (-1 * (i.uv0 - 0.5)) * clockCounterDirection; /*** Общее начало для opacity и cutoff с переключателем вращения по/против часовой стрелки END ***/ /*** Секция для cutoff ***/ float tRotatorNormalized = _TextureRotator / 360.0; float cutoffRotator_ang = _CutoffRightBottomLeftTop * -TAU; float2x2 cutoffRotationMatrix = getMatrix(cutoffRotator_ang); float2 cutoffRotator = mul(CommonStartAndSwitcher, cutoffRotationMatrix); float whiteToBlackMask = getMask(getNormalizedAtanMask(cutoffRotator, 0), tRotatorNormalized, 1); // Финальная маска float finalMask = 1.0 - whiteToBlackMask; clip(finalMask - 0.5); /*** Секция для opacity ***/ float oRotatorNormalized = _OpacityRotator / 360.0; float2 oVector = float2(_OpVector); float oRotator_ang = _OpRightBottomLeftTop * (oRotatorNormalized * -TAU); float2x2 oRotationMatrix = getMatrix(oRotator_ang); float2 oRotator = mul(oVector * CommonStartAndSwitcher, oRotationMatrix); float oWhiteToBlackMask = getMask(getNormalizedAtanMask(oRotator, _ReverseMaskCoords), oRotatorNormalized, 0); // Финальная прозрачность float oFinalMultiply = _MainTex_var.a * max(getNormalizedAtanMask(oRotator, _ReverseMaskCoords), ceil(oWhiteToBlackMask)); /*** Излучение (Emissive) ***/ // oFinalMultiply чтоб обрезать прозрачную область, где она обрезана в самой текстуре float3 finalColor = _MainTex_var.rgb * _Color.rgb * oFinalMultiply; // Конечный результат (цвет, обработанный маской и повернутый под углом альфа канал) return fixed4(finalColor, oFinalMultiply); } ENDCG } } FallBack "Diffuse" } Скрипт будет таким: using UnityEngine; using System.Collections; public enum FillOrigin { Right, Bottom, Left, Top } public class RadialFill_MoreScriptControl : MonoBehaviour { public float cutoffStartAngle = 5.0f; // градусы public float opacityStartAngle = -350.0f; // градусы, -2 * PI + 10 (небольшой начальный угол) public float deltaAngle = 5f; public bool fillClockwise = true; public FillOrigin fillOrigin = FillOrigin.Right; private const float MAX_ANGLE = 360.0f; private Material material; private float _TextureRotator; // ссылка на переменную _TextureRotator в шейдере private float _OpacityRotator; // ссылка на переменную _TextureRotator в шейдере void Start () { material = GetComponent().material; } void Update () { if (Input.GetMouseButtonDown(0)) //if (Input.GetKeyDown("f")) StartCoroutine(FillSprite()); } IEnumerator FillSprite() { var cOffStart = cutoffStartAngle; var oStart = opacityStartAngle; material.SetFloat("_FillClockwise", fillClockwise ? 1 : 0); material.SetFloat("_TextureRotator", cOffStart); material.SetFloat("_OpacityRotator", oStart); SetCutoffData(); SetOpacityData(); _TextureRotator = cOffStart; _OpacityRotator = oStart; while(_OpacityRotator <= MAX_ANGLE) { if (_TextureRotator >= MAX_ANGLE) _TextureRotator = MAX_ANGLE; if (_OpacityRotator >= MAX_ANGLE) _OpacityRotator = MAX_ANGLE; material.SetFloat("_TextureRotator", _TextureRotator); material.SetFloat("_OpacityRotator", _OpacityRotator); _OpacityRotator += deltaAngle; _TextureRotator += deltaAngle; yield return null; } yield break; } private void SetCutoffData() { var cutoffRightBottomLeftTop = 1.0f; if (fillOrigin == FillOrigin.Bottom) cutoffRightBottomLeftTop = fillClockwise ? 1.75f : 1.25f; else if (fillOrigin == FillOrigin.Left) cutoffRightBottomLeftTop = 1.5f; else if (fillOrigin == FillOrigin.Top) cutoffRightBottomLeftTop = fillClockwise ? 1.25f : 1.75f; cutoffRightBottomLeftTop += 0.001f; material.SetFloat("_CutoffRightBottomLeftTop", cutoffRightBottomLeftTop); } private void SetOpacityData() { Vector2 oVector = new Vector2(1, -1); var oRightBottomLeftTop = 1.0f; int reverseMaskCoords = (fillOrigin == FillOrigin.Top || fillOrigin == FillOrigin.Bottom) ? 1 : 0; if (fillOrigin == FillOrigin.Left) oVector = new Vector2(-1, 1); else if (fillOrigin == FillOrigin.Top) { oVector = fillClockwise ? new Vector2(-1, -1) : new Vector2(1, 1); oRightBottomLeftTop = -1.0f; } else if (fillOrigin == FillOrigin.Bottom) { oVector = fillClockwise ? new Vector2(1, 1) : new Vector2(-1, -1); oRightBottomLeftTop = -1.0f; } material.SetInt("_ReverseMaskCoords", reverseMaskCoords); material.SetVector("_OpVector", oVector); material.SetFloat("_OpRightBottomLeftTop", oRightBottomLeftTop); } } в инспекторе управление такое: И по поводу размытия...Так как мой ответ уже большой (из-за кода)...и уже вторая часть, то я приведу код шейдера Blur, который вы можете перенести в шейдеры выше. А также дописать управление размытием из скрипта по примеру выше. Shader "Custom/Blur" { Properties { _MainTex ("Texture", 2D) = "white" {} radius ("radius", Range(0, 80)) =0 resolution ("resolution", float) = 800 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; uniform float resolution = 800; uniform float radius = 400; uniform float2 dir = float2(0,1); v2f vert (appdata v) { v2f o; o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { float4 sum = float4(0.0, 0.0, 0.0, 0.0); float2 tc = i.uv; // радиус размытия в пикселях float blur = radius/resolution/4; float hstep = 1; // размытие по горизонтали float vstep = 0; // размытие по вертикали sum += tex2D(_MainTex, float2(tc.x - 5.0 * blur * hstep, tc.y - 5.0 * blur * vstep)) * 0.0052111262; sum += tex2D(_MainTex, float2(tc.x - 4.0 * blur * hstep, tc.y - 4.0 * blur * vstep)) * 0.0162162162; sum += tex2D(_MainTex, float2(tc.x - 3.0 * blur * hstep, tc.y - 3.0 * blur * vstep)) * 0.0540540541; sum += tex2D(_MainTex, float2(tc.x - 2.0 * blur * hstep, tc.y - 2.0 * blur * vstep)) * 0.1216216216; sum += tex2D(_MainTex, float2(tc.x - 1.0 * blur * hstep, tc.y - 1.0 * blur * vstep)) * 0.1945945946; sum += tex2D(_MainTex, float2(tc.x, tc.y)) * 0.2270270270; sum += tex2D(_MainTex, float2(tc.x + 1.0 * blur * hstep, tc.y + 1.0 * blur * vstep)) * 0.1945945946; sum += tex2D(_MainTex, float2(tc.x + 2.0 * blur * hstep, tc.y + 2.0 * blur * vstep)) * 0.1216216216; sum += tex2D(_MainTex, float2(tc.x + 3.0 * blur * hstep, tc.y + 3.0 * blur * vstep)) * 0.0540540541; sum += tex2D(_MainTex, float2(tc.x + 4.0 * blur * hstep, tc.y + 4.0 * blur * vstep)) * 0.0162162162; sum += tex2D(_MainTex, float2(tc.x + 5.0 * blur * hstep, tc.y + 5.0 * blur * vstep)) * 0.0052111262; return float4(sum.rgb, 1); } ENDCG } } } P.S. К сожалению шейдер RadialFill работает только для Sprite Mode → Single. Как сделать для мульти Multiple, я пока не знаю. P.P.S. Можно сделать еще улучшения: Вынести функцию getMatrix (матрица вращения) также в скрипт. Сделать маску не генерируемой внутри шейдера, а взять текстуру в виде картинки atan2 и применять уже её. Глядишь еще чуть снять загрузку с шейдера можно. P.P.P.S. Для попытки понимая шейдеров хоть на маленком уровне можно использовать ассет Shader Forge - Визуальный редактор для программирования шейдеров. Визуальность в данном случае очень в плюс. Из бесплатных пока поиск показывает только uShader FREE - но его я не пробовал, не знаю на сколько хорош. Ссылки к прочтению, которые использовались в шейдерах выше: Стандартные шейдерные предпроцессорные макросы Создание программ с несколькими вариантами шейдеров Директивы компиляции #pragma Culling & Depth Блендинг (Blending) Шейдеры и эффекты в Unity. Книга рецептов Unity 5.x Shaders and Effects Cookbook

Ответ 3



Ух ты, интересный вопрос и даже награаадааа! Вообще, сколько я не читал, я не смог представить зачем эффект размытия применять на прозрачности. Ведь если некая часть спрайта будет прозрачна, то размытия там итак не будет видно и заметно, а там где не прозрачно, то и размытия нет. Либо я чего не понял и имелось виду применение размытия вообще на весь спрайт, не зависимо от того, какой процент радиального заполнения сейчас имеется. Если именно так, то Можно поступить так, как написано в другом ответе: взять из стандартных ассетов Юнити (благо их не мало предоставляется) эффект размытия, применяемый на камере. Добавить еще одну камеру, добавить туда скрипт размытия и шейдер и в нужный момент включать ту камеру и изменять смещение в Blur-эффекте. Реализовать, как и хотели в шейдере)) Об этом в самом конце. (!!!) Да простят меня админы сайта, но в один ответ у меня не вместится (из-за количество кода, а не из-за "воды"). Поэтому ответ будет в двух частях. Ответ, часть 1. Суть, что обрезки, что заливания прозрачностью будет сводиться к тому, что будет браться маска, на основе которой всё будет происходить. Маска генерируется программно через тригонометрическую функцию atan2. На осях она выглядит так: В двумерной системе координат выглядит так: Так как маска — это некая компонента, состоящая из оттенков черного и белого, в которой черный цвет — полностью отсутствие текстуры, а белый — полностью видимая текстура, то для прозрачности маска atan2 будет представлять из себя переход от черного к белому (см. рисунок выше), а для обрезки будет применена дополнительная функция, чтоб было только черное/белое, без плавных переходов. Я попробую просто опубликовать шейдер, в котором будут комментарии того, что сделано. Не уверен, что все будет понятно, но я хоть как-то худо бедно постараюсь. Shader "Custom/RadialFill" { Properties { [PerRendererData]_MainTex ("MainTex", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _OpacityRotator ("Opacity Rotator", Range(-360, 360)) = -360 // два полных оборота _TextureRotator ("Texture Rotator", Range(0, 360)) = 360 [MaterialToggle] _FillClockwise ("Fill Clockwise", Float ) = 1 [HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [KeywordEnum(Right, Bottom, Left, Top)] _Fill_Origin("Fill Origin", Int) = 0 } SubShader { // https://docs.unity3d.com/ru/current/Manual/SL-SubShaderTags.html Tags { "IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent" "CanUseSpriteAtlas"="True" "PreviewType"="Plane" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" // https://docs.unity3d.com/Manual/SL-PassTags.html } Blend One OneMinusSrcAlpha // https://docs.unity3d.com/ru/current/Manual/SL-Blend.html ZWrite Off // https://docs.unity3d.com/ru/current/Manual/SL-CullAndDepth.html CGPROGRAM #pragma vertex vert // vert - имя функции обработки вершин #pragma fragment frag // frag - имя функции обработки пикселей #pragma multi_compile _ PIXELSNAP_ON // как работает shader_feature: https://docs.unity3d.com/ru/530/Manual/SL-MultipleProgramVariants.html // он относится к свойству _Fill_Origin .... по сути - автоматически конвертируем его имя и значения в константы #pragma shader_feature _FILL_ORIGIN_RIGHT _FILL_ORIGIN_BOTTOM _FILL_ORIGIN_LEFT _FILL_ORIGIN_TOP #include "UnityCG.cginc" #pragma exclude_renderers gles3 metal d3d11_9x xbox360 xboxone ps3 ps4 psp2 #pragma target 3.0 uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float4 _Color; uniform float _OpacityRotator; uniform float _TextureRotator; uniform fixed _FillClockwise; static const float TAU = float(6.283185); // это 2 * PI, кто не знает struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; }; VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.uv0 = v.texcoord0; o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(_Object2World, float4(v.tangent.xyz, 0.0)).xyz); o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); o.posWorld = mul(_Object2World, v.vertex); o.pos = mul(UNITY_MATRIX_MVP, v.vertex ); #ifdef PIXELSNAP_ON o.pos = UnityPixelSnap(o.pos); #endif return o; } float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex)); /*** Общее начало для opacity и cutoff, помогающее переключать вращение по/против часовой стрелки BEGIN ***/ // float2(1, -1) - по часовой, float2(1, 1) - против часовой float2 clockCounterDirection = _FillClockwise ? float2(1, -1) : float2(1, 1); // по умолчанию "обрезание" начинается слева. // умножение на -1 для того, чтоб началось справа.....просто потому, что я так хочу =) float2 CommonStartAndSwitcher = (-1 * (i.uv0 - 0.5)) * clockCounterDirection; /*** Общее начало для opacity и cutoff с переключателем вращения по/против часовой стрелки END ***/ /*** Секция для cutoff ***/ float cutoffRightBottomLeftTop = 1.0; // изменение направления // В зависимости от того, что выбрано в качестве старта вращения право/лево/верх/низ // нужно будет провернуть и текстурку. // +0.25 - 90 градусов, +0.5 - 180, +0.75 - 270 #if _FILL_ORIGIN_BOTTOM cutoffRightBottomLeftTop = _FillClockwise ? 1.75 : 1.25; #elif _FILL_ORIGIN_LEFT cutoffRightBottomLeftTop = 1.5; #elif _FILL_ORIGIN_TOP cutoffRightBottomLeftTop = _FillClockwise ? 1.25 : 1.75; #endif cutoffRightBottomLeftTop += 0.001; // Матрица вращения для cutoff float cutoffRotator_ang = cutoffRightBottomLeftTop * -TAU; float cutoffRotator_cos = cos(cutoffRotator_ang); float cutoffRotator_sin = sin(cutoffRotator_ang); float2x2 cutoffRotationMatrix = float2x2(cutoffRotator_cos, -cutoffRotator_sin, cutoffRotator_sin, cutoffRotator_cos); float2 cutoffRotator = mul(CommonStartAndSwitcher, cutoffRotationMatrix); // перевод из системы от 0 до 360 градусов в отсчет от 0 до 1 float tRotatorNormalized = _TextureRotator / 360.0; // Генерирование маски для отсечения пикселей и отсечение пикселей по предоставленной маске // 1. Для генерации нужны исхоные две координаты.... // rg, утрированно, представляют из себя x и y float2 cutoffMaskSource = cutoffRotator.rg; // 2. Формируем начальную маску // в инете рисуночки глянуть как это выглядит =) // Угол задается в радианах и принимает значения от -PI до PI, исключая -PI float atan2Mask = atan2(cutoffMaskSource.g, cutoffMaskSource.r); // 3. Добавляем пол оборота (до целого) и конвертируем в значение от 0 до 1, // для дальнейшей удобной работы в единичном отрезке, т.к tRotatorNormalized меняется от 0 до 1 float atan2MaskNormalized = (atan2Mask / TAU) + 0.5; // 4. Привязка маски к повороту. хз как объяснить float atan2MaskRotatable = atan2MaskNormalized - tRotatorNormalized; // 5. Получаем карту заливки от белого к черному // Белый - полностью видимый участок, Черный - обрезающиеся (не отображающиеся) пиксели float whiteToBlackMask = ceil(atan2MaskRotatable); // 6. Собираем финальную маску от чёрного к белому (т.к. нужно постепенное заполнение) float finalMask = 1.0 - whiteToBlackMask; clip(finalMask - 0.5); /*** Секция для opacity ***/ // oVector меняется в зависимости от начала направления - лево/право/верх/низ float2 oVector = float2(1, -1); // изменение направления в зависимости от лево-право (1.0) или верх-низ (-1.0) float oRightBottomLeftTop = 1.0; // В зависимости от того, что выбрано в качестве старта вращения право/лево/верх/низ // нужно будет провернуть и маску. #if _FILL_ORIGIN_LEFT oVector = float2(-1, 1); #elif _FILL_ORIGIN_TOP oVector = _FillClockwise ? float2(-1, -1) : float2(1, 1); oRightBottomLeftTop = -1.0; #elif _FILL_ORIGIN_BOTTOM oVector = _FillClockwise ? float2(1, 1) : float2(-1, -1); oRightBottomLeftTop = -1.0; #endif float oRotatorNormalized = _OpacityRotator / 360.0; // Матрица вращения для opacity float oRotator_ang = oRightBottomLeftTop * (oRotatorNormalized * -TAU); float oRotator_cos = cos(oRotator_ang); float oRotator_sin = sin(oRotator_ang); float2x2 oRotationMatrix = float2x2(oRotator_cos, -oRotator_sin, oRotator_sin, oRotator_cos); float2 oRotator = mul(oVector * CommonStartAndSwitcher, oRotationMatrix); // Как и у cutoff формируем маску float2 oMask = oRotator.rg; float2 oMaskHorizOrVert = atan2(oMask.g, oMask.r); // при формировании маски по вертикали, нужно поменять x, y местами в функции #if (_FILL_ORIGIN_TOP || _FILL_ORIGIN_BOTTOM) oMaskHorizOrVert = atan2(oMask.r, oMask.g); #endif float oAtan2MaskNormalized = (oMaskHorizOrVert / TAU) + 0.5; // oRotatorNormalized - oAtan2MaskNormalized для того, чтобы первый круг просто провернуться, а на втором // начать обрезку как у cutoff, только начиная схвоста, но при этом продолжая вращаться. // Если было бы oAtan2MaskNormalized - oRotatorNormalized (как в примере с cutoff выше), то, т.к. значение oRotatorNormalized // меняется с -1 до 1 (два полных круга), получается что маска наложена на изображение 2 раза: 1 раз - прозрачность, 2 раз - она же // поэтому увеличивается наложенность, белый цвет. В итоге при изменении с -1 до 1 ушла бы в начале белизна, а потом провернулась бы маска, // и не обрезалась бы float oAtan2MaskRotatable = oRotatorNormalized - oAtan2MaskNormalized; float oWhiteToBlackMask = ceil(oAtan2MaskRotatable); // Финальная прозрачность float oFinalMultiply = _MainTex_var.a * max(oAtan2MaskNormalized, ceil(oWhiteToBlackMask)); /*** Излучение (Emissive) ***/ // oFinalMultiply чтоб обрезать прозрачную область, где она обрезана в самой текстуре float3 finalColor = _MainTex_var.rgb * _Color.rgb * oFinalMultiply; // Конечный результат (цвет, обработанный маской и повернутый под углом альфа канал) return fixed4(finalColor, oFinalMultiply); } ENDCG } } FallBack "Diffuse" } Чтобы в нужный момент запустить заливку, конечно же нужно дать команду. А откуда её можно дать? Правильно — из скрипта. Он будет расположен ниже: using UnityEngine; using System.Collections; public class RadialFill : MonoBehaviour { public float cutoffStartAngle = 5.0f; // градусы public float opacityStartAngle = -350.0f; // градусы, -2 * PI + 10 (небольшой начальный угол) public float deltaAngle = 5f; private const float MAX_ANGLE = 360.0f; private Material material; private float _TextureRotator; // ссылка на переменную _TextureRotator в шейдере private float _OpacityRotator; // ссылка на переменную _TextureRotator в шейдере void Start () { material = GetComponent().material; } void Update () { if (Input.GetMouseButtonDown(0)) //if (Input.GetKeyDown("f")) StartCoroutine(FillSprite()); } IEnumerator FillSprite() { var cOffStart = cutoffStartAngle; var oStart = opacityStartAngle; material.SetFloat("_TextureRotator", cOffStart); material.SetFloat("_OpacityRotator", oStart); _TextureRotator = cOffStart; _OpacityRotator = oStart; while(_OpacityRotator <= MAX_ANGLE) { if (_TextureRotator >= MAX_ANGLE) _TextureRotator = MAX_ANGLE; if (_OpacityRotator >= MAX_ANGLE) _OpacityRotator = MAX_ANGLE; material.SetFloat("_TextureRotator", _TextureRotator); material.SetFloat("_OpacityRotator", _OpacityRotator); _OpacityRotator += deltaAngle; _TextureRotator += deltaAngle; yield return null; } yield break; } } где: cutoffStartAngle — начальный угол обрезки, opacityStartAngle — начальный угол прозрачности. Эти параметры для того, чтобы немного отрегулировать по вкусу площадь сектора, занимаемого прозрачностью. Замечу, что прозрачность изменяется от -360 до 360, потому что первый круг она проворачивается сама по себе, а второй круг — плавно "заходит" за текстуру. deltaAngle - дельта, на которую проворачиваются маски. Что скрипт делает? При нажатии нажатии на клавишу мыши он берет шейдер у спрайта (точнее с его материала), устанавливает изначальные углы, в цикле изменяет угол поворота и передает это значение в шейдер, чтобы он там уже у себя применил значения в frag. Выглядит в инспекторе так: Opacity Rotator - вращение маски прозрачности Texture Rotator - вращение маски обрезки Fill Clockwise - по часовой стрелке или против Fill Origin - с какой стороны начинать (справа/слева/сверху/снизу) Итог будет выглядеть примерно таким: Увы на данной гифке не получается передать то, как это выглядит в правильности Улучшение Так как у секции cutoff и opacity есть общие части, то их можно вынести в общие функции, как и во всех нормальных языках программирования. Shader "Custom/RadialFillCommonFunctions" { Properties { [PerRendererData]_MainTex ("MainTex", 2D) = "white" {} _Color ("Color", Color) = (1,1,1,1) _OpacityRotator ("Opacity Rotator", Range(-360, 360)) = -360 // два полных оборота _TextureRotator ("Texture Rotator", Range(0, 360)) = 360 [MaterialToggle] _FillClockwise ("Fill Clockwise", Float ) = 1 [HideInInspector]_Cutoff ("Alpha cutoff", Range(0,1)) = 0.5 [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0 [KeywordEnum(Right, Bottom, Left, Top)] _Fill_Origin("Fill Origin", Int) = 0 } SubShader { Tags { "IgnoreProjector"="True" "Queue"="Transparent" "RenderType"="Transparent" "CanUseSpriteAtlas"="True" "PreviewType"="Plane" } Pass { Name "FORWARD" Tags { "LightMode"="ForwardBase" } Blend One OneMinusSrcAlpha Cull Off ZWrite Off CGPROGRAM #pragma vertex vert #pragma fragment frag #define UNITY_PASS_FORWARDBASE #pragma multi_compile _ PIXELSNAP_ON #pragma shader_feature _FILL_ORIGIN_RIGHT _FILL_ORIGIN_BOTTOM _FILL_ORIGIN_LEFT _FILL_ORIGIN_TOP #include "UnityCG.cginc" #pragma multi_compile_fwdbase #pragma exclude_renderers gles3 metal d3d11_9x xbox360 xboxone ps3 ps4 psp2 #pragma target 3.0 static const float TAU = float(6.283185); // это 2 * PI, кто не знает uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float4 _Color; uniform float _OpacityRotator; uniform float _TextureRotator; uniform fixed _FillClockwise; struct VertexInput { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord0 : TEXCOORD0; }; struct VertexOutput { float4 pos : SV_POSITION; float2 uv0 : TEXCOORD0; float4 posWorld : TEXCOORD1; float3 normalDir : TEXCOORD2; float3 tangentDir : TEXCOORD3; float3 bitangentDir : TEXCOORD4; }; // матрица вращения float2x2 getMatrix(float angle) { float r_cos = cos(angle); float r_sin = sin(angle); return float2x2(r_cos, -r_sin, r_sin, r_cos); } // формирование маски float2x2 getMask(float oAtan2MaskNormalized, float rotator, int isRotatorSubtract) { //float atan2var = reverseMaskCoords ? atan2(maskChannels.r, maskChannels.g) : atan2(maskChannels.g, maskChannels.r); //float oAtan2MaskNormalized = (atan2var / TAU) + 0.5; float oAtan2MaskRotatable = isRotatorSubtract ? oAtan2MaskNormalized - rotator : rotator - oAtan2MaskNormalized; return ceil(oAtan2MaskRotatable); } float getNormalizedAtanMask(float2 maskChannels, int reverseMaskCoords) { float atan2var = reverseMaskCoords ? atan2(maskChannels.r, maskChannels.g) : atan2(maskChannels.g, maskChannels.r); return (atan2var / TAU) + 0.5; } VertexOutput vert (VertexInput v) { VertexOutput o = (VertexOutput)0; o.uv0 = v.texcoord0; o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(_Object2World, float4(v.tangent.xyz, 0.0)).xyz); o.bitangentDir = normalize(cross(o.normalDir, o.tangentDir) * v.tangent.w); o.posWorld = mul(_Object2World, v.vertex); o.pos = mul(UNITY_MATRIX_MVP, v.vertex ); #ifdef PIXELSNAP_ON o.pos = UnityPixelSnap(o.pos); #endif return o; } float4 frag(VertexOutput i) : COLOR { i.normalDir = normalize(i.normalDir); float4 _MainTex_var = tex2D(_MainTex,TRANSFORM_TEX(i.uv0, _MainTex)); /*** Общее начало для opacity и cutoff, помогающее переключать вращение по/против часовой стрелки BEGIN ***/ // float2(1, -1) - по часовой, float2(1, 1) - против часовой float2 clockCounterDirection = _FillClockwise ? float2(1, -1) : float2(1, 1); // по умолчанию "обрезание" начинается слева. // умножение на -1 для того, чтоб началось справа.....просто потому, что я так хочу =) float2 CommonStartAndSwitcher = (-1 * (i.uv0 - 0.5)) * clockCounterDirection; /*** Общее начало для opacity и cutoff с переключателем вращения по/против часовой стрелки END ***/ /*** Секция для cutoff ***/ float tRotatorNormalized = _TextureRotator / 360.0; float cutoffRightBottomLeftTop = 1.0; // изменение направления #if _FILL_ORIGIN_BOTTOM cutoffRightBottomLeftTop = _FillClockwise ? 1.75 : 1.25; #elif _FILL_ORIGIN_LEFT cutoffRightBottomLeftTop = 1.5; #elif _FILL_ORIGIN_TOP cutoffRightBottomLeftTop = _FillClockwise ? 1.25 : 1.75; #endif cutoffRightBottomLeftTop += 0.001; float cutoffRotator_ang = cutoffRightBottomLeftTop * -TAU; float2x2 cutoffRotationMatrix = getMatrix(cutoffRotator_ang); float2 cutoffRotator = mul(CommonStartAndSwitcher, cutoffRotationMatrix); float whiteToBlackMask = getMask(getNormalizedAtanMask(cutoffRotator, 0), tRotatorNormalized, 1); // Финальная маска float finalMask = 1.0 - whiteToBlackMask; clip(finalMask - 0.5); /*** Секция для opacity ***/ float oRotatorNormalized = _OpacityRotator / 360.0; float2 oVector = float2(1, -1); float oRightBottomLeftTop = 1.0; int reverseMaskCoords = 0; #if (_FILL_ORIGIN_TOP || _FILL_ORIGIN_BOTTOM) reverseMaskCoords = 1; #endif #if _FILL_ORIGIN_LEFT oVector = float2(-1, 1); #elif _FILL_ORIGIN_TOP oVector = _FillClockwise ? float2(-1, -1) : float2(1, 1); oRightBottomLeftTop = -1.0; #elif _FILL_ORIGIN_BOTTOM oVector = _FillClockwise ? float2(1, 1) : float2(-1, -1); oRightBottomLeftTop = -1.0; #endif float oRotator_ang = oRightBottomLeftTop * (oRotatorNormalized * -TAU); float2x2 oRotationMatrix = getMatrix(oRotator_ang); float2 oRotator = mul(oVector * CommonStartAndSwitcher, oRotationMatrix); float oWhiteToBlackMask = getMask(getNormalizedAtanMask(oRotator, reverseMaskCoords), oRotatorNormalized, 0); // Финальная прозрачность float oFinalMultiply = _MainTex_var.a * max(getNormalizedAtanMask(oRotator, reverseMaskCoords), ceil(oWhiteToBlackMask)); /*** Излучение (Emissive) ***/ // oFinalMultiply чтоб обрезать прозрачную область, где она обрезана в самой текстуре float3 finalColor = _MainTex_var.rgb * _Color.rgb * oFinalMultiply; // Конечный результат (цвет, обработанный маской и повернутый под углом альфа канал) return fixed4(finalColor, oFinalMultiply); } ENDCG } } FallBack "Diffuse" }