Страницы

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

среда, 20 февраля 2019 г.

OpenGL Нужно ли постоянно включать/выключать GL_BLEND

когда рисуются прозрачные объекты то включается GL_BLEND, когда рисование заканчивается то GL_BLEND выключается. Может не нужно выключать, один раз включить при запуске программы и всё? Постоянное включение/выключение как то влияет на производительность?


Ответ

Постоянное включение/выключение как то влияет на производительность?
Да, любая функция, изменяющая контекст, сначала дожидается готовности GPU, а затем стопорит и CPU, и GPU до окончания передачи изменений.
Может не нужно выключать, один раз включить при запуске программы и всё?
Если вашим контекстом больше никто не пользуется (а в большинстве случаев так оно и есть), то необходимости в восстановлении настроек нет. В придачу, операционная система сама сохраняет их перед обращением к другому OpenGL-приложению и восстанавливает обратно при возвращении к вам.

Создание REST API Java EE

Дано: встала задача написания простенького REST API, состоящего буквально из нескольких методов. На вход подаем несколько параметров, на выходе JSON. Нет необходимости работать через браузер, то есть никаких страниц красивого отображения всего этого не требуется(может тогда даже более корректно это назвать Веб-сервисом?). Нагрузка предполагается небольшая.
Естественно потребуется какая либо авторизация. В голове сложилась такая схема: при успешной авторизации пользователю передается токен, который в дальнейшем передается при каждом запросе к API. На бэкэнде соответственно проверка этого токена.
Что имею:
так как Java мне наиболее близка, выбор пал на нее; сборка maven в качестве фреймворка был выбран Spring; сервер Apache Tomcat v8; текущая база MSSQL
Вопрос: насколько данный стек технологий целесообразен для решения такой задачи, не является ли это "стрельбой из пушки по воробьям"?
Где то слышал что Tomcat не поддерживает Java EE, или не полностью поддерживает, так ли это?
Вопрос конечно общий, но хотелось бы знать ваше мнение, что бы заведомо не пойти ложным путем.
Заранее спасибо за ответ.
PS: про Node.js слышал, но как то не сложилось по техническим причинам) А может не заморачиваться и заюзать PHP)


Ответ

Да. Tomcat это просто сервлет-контейнер и по сути не является J2EE-сервером, т.к. не имплементирует все необходимые EE-спецификации. Но они вам и не нужны, если вы используете Spring-фреймворк.
Выбранные вами технологии вполне подойдут для решения вашей задачи.
Также советую для легкого конфигурирования использовать Spring Boot, который обеспечит вам множество уже готовых возможностей. Для работы с БД обратите внимание на Spring Data-JPA
В вашем случае даже можно еще более упростить решение, используя Spring Data-Rest
Пример того как можно за 15минут создать простой RESTfull веб-сервис на Spring Data-Rest и Spring Boot.

Как упростить многочисленные else if?

Есть динамические данные от пользователя, которые на сервере необходимо проверять на вхождение в тот или иной диапазон и в зависимости от этого выводить разный результат.
Мне в голову приходит только (пример условный):
if($var < 1000) { do one } else if ($var < 2000) { do two } else if ($var < 3000) { do three } .... и т.д.
Подскажите, как упростить такую конструкцию?


Ответ

Поскольку автор по какой-то причине упорно не признается, какой именно результат ему надо выводить, придется фантазировать.
В случае, если надо просто вывести некий текст, нам пригодятся такие познания из курса начальной школы, как деление и округление.
$results = [ "Герой пионерского лагеря", "Дважды герой пионерского лагеря", "Многократный призер куличиков и совочка", ] $place = floor($var / 1000); echo $results[$place] ?? 'Завхоз по наградам';

Передача аргументов функции в обработчике события

Какие есть способы, а самое главное как правильно передавать аргументы в функцию в обработчике событий Например:
var menu = document.querySelector('.nav'); var ul = menu.querySelector('ul'); function handler(event, bool) { event = event || window.event; if(event.target.tagName == 'LI') { if(bool) { ul.style.backgroundColor = 'red'; } else { ul.style.backgroundColor = ''; }
} } menu.addEventListener('mouseout',handler.bind(null, event, false), false); menu.addEventListener('mouseover',handler.bind(this, event, true), false);
Вроде работает, но разнице нет какой контекст я передаю в bind, хоть this, хоть null, хоть вася.... Далее. Изначально код функции был такой:
function handler(event, bool) { if(event.target.tagName == 'LI') { var ul = menu.querySelector('ul'); if(bool) { ul.style.backgroundColor = 'red'; } else { ul.style.backgroundColor = ''; }
} }
При этом event == undefined, ul == undefined Добавил в начало функции строку: event = event || window.event; - event появился, вынес var ul = menu.querySelector('ul'); из функции стало работать. Так как же правильно передавать аргументы?


Ответ

Используйте handler.bind(null, false)
С сигнатурой function handler(bool, event)
handler.bind(null, false) как раз вернёт функцию с одним аргументом для события.
function handler(bool, event) { console.log(bool, event); } let handleFalse = handler.bind(null, false); let handleTrue = handler.bind(null, true); handleFalse('Event 1'); handleTrue('Event 2');
Либо можно использовать замыкание(если не нужен контекст this):
function getHandler(bool) { return function handler(event) { console.log(bool, event); } } let handleFalse = getHandler(false); let handleTrue = getHandler(true); handleFalse('Event 1'); handleTrue('Event 2');

Ну и соберём в кучу:
var menu = document.querySelector('.nav'); var ul = menu.querySelector('ul'); function handler(bool) { return function (event) { event = event || window.event; if(event.target.tagName == 'LI') { event.target.style.backgroundColor = bool ? 'red' : ''; } } } menu.addEventListener('mouseout',handler(false), false); menu.addEventListener('mouseover',handler(true), false);


Хотя, на мой взгляд, .nav li:hover{} куда лучше

Каким образом можно реализовать бегущую подсветку (выделение) многострочного текста?

Здесь уже был вопрос про закраску текста и на него был дан ответ
Вкратце суть решения состоит в создании FormattedText из содержимого скрытого TextBlock, а затем создается геометрия из FormattedText, которая отображается с помощью Path, который в свою очередь закрашивается с помощью анимации на нем.
И все прекрасно работает пока текст занимает одну строку, а вот когда строк несколько, получается не то, что хотелось бы.

XAML я повторять не буду, т.к. он полностью совпадает с таковым в выше указанном ответе. Мой код тоже мало чем отличается, но я его приведу для ясности вопроса.
private List _RectsForFill; // прямоугольники с каждым символом private double _LengthFillText; // общая ширина рисованного текста
//анимация Storyboard _Storyboard; DoubleAnimation _FromAnimation; DoubleAnimation _ToAnimation;
private void CreateFillText() { //получаем текстовое содержимое TextBlock tb = this.textBlockHidden; var text = tb.Text;
//создаем экземпляр форматированного текста FormattedText formattedText = new FormattedText( text, CultureInfo.GetCultureInfo("en-US"), FlowDirection.LeftToRight, new Typeface( tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black // конкретная кисть нам не важна, мы используем только геометрию );
//берем переносы строк у эталонного текстблока formattedText.MaxTextWidth = this.textBlockHidden.Width; formattedText.MaxTextHeight = this.textBlockHidden.Height;
// стащили геометрию у текста... var geo = formattedText.BuildGeometry(new Point()); // ...и отдали её Path'у Target.Data = geo;
//вычислим прямоугольники для заполнения GetRectsForFill(text, formattedText); }
private void GetRectsForFill(string text, FormattedText formattedText) { var bb = formattedText.BuildHighlightGeometry(new Point()); _LengthFillText = bb.Bounds.Width; // общая ширина
//заполняем коллекцию побуквенных боксов _RectsForFill = Enumerable.Range(0, text.Length) .Select(k => formattedText.BuildHighlightGeometry(new Point(), k, 1) .Bounds) .ToList();
//ссылки на анимацию для дальнейшей работы с ней _Storyboard = (Storyboard)Target.Resources["AnimationStoryboard"]; _FromAnimation = (DoubleAnimation)_Storyboard.Children[0]; _ToAnimation = (DoubleAnimation)_Storyboard.Children[1]; }
Вот метод, заполняет черным отрисованный текст
///

/// Закрашивание рисованного текста /// /// начальная позиция слова /// число закрашиваемых букв в слове public void FillTextPath(int startPos, int count) { if (count == 0) throw new ArgumentException(nameof(count));
//вычисляем индекс необходимого прямоугольника int index = startPos + count; if (index >= _RectsForFill.Count) index = _RectsForFill.Count - 1;
//необходимый прямоугольник Rect box = _RectsForFill[index];
//закрашиваем _FromAnimation.From = box.Left / _LengthFillText; _FromAnimation.To = box.Right / _LengthFillText; _ToAnimation.From = box.Left / _LengthFillText; _ToAnimation.To = box.Right / _LengthFillText; _Storyboard.Begin(); }


Ответ

Вы пошли правильным путём, разбить на отдельные Path'ы — хорошая идея.
Давайте её реализуем до конца.
Для начала, функциональность проигрывания уже достаточно сложна, так что вынесем её в отдельный UserControl. Затем, каждый контрол пусть отвечает за одну строку. Чтобы не искать куски по геометрии, просто отрежем эту строку при помощи Clip'а. На вход в UserControl будем подавать результаты разбора текста на геометрию (функцией Create).

В code-behind будет анимация:
public partial class SingleLine : UserControl { List boundingBoxes; double extent;
public SingleLine() { InitializeComponent(); }
public SingleLine(Geometry geo, List boundingBoxes, double totalExtent) : this() { Target.Data = geo; extent = totalExtent; Rect clip = boundingBoxes.Aggregate(Rect.Union); Clip = new RectangleGeometry(clip); this.boundingBoxes = boundingBoxes; }
public async Task Play() { var storyboard = (Storyboard)Target.Resources["AnimationStoryboard"]; var fromAnimation = (DoubleAnimation)storyboard.Children[0]; var toAnimation = (DoubleAnimation)storyboard.Children[1];
foreach (var b in boundingBoxes) { await Task.Delay(250); // перерыв между буквами fromAnimation.From = b.Left / extent; fromAnimation.To = b.Right / extent; toAnimation.From = b.Left / extent; toAnimation.To = b.Right / extent; storyboard.Begin(); await Task.Delay(250); // дождёмся конца анимации } } }
Зачем нам нужна такая сложность с Clip и totalExtent? К сожалению, я не нашёл метода выкусить только нужную часть геометрии. Поэтому мы даём на вход всю геометрию, а хотим показывать только текущую строку. Для этого мы вычисляем прямоугольник, соответствующий нужной части геометрии (текущей строке), и отрезаем отображение остального при помощи Clip'а. Но наши вычисления коэффициентов (b.Left / extent и т. д.) требуют процента от общей ширины Path'а, а не ширины текущей строки! (Напомню, наш Path получает геометрию всей строки, включая остальные строки тоже.) Поэтому приходится передавать ещё и общую ширину.
Теперь основной код. Он стал проще, так как часть функциональности отделилась. В нём мы не можем положить один, фиксированный Path, так как у нас количество строк не известно заранее. Поэтому контролы будем добавлять динамически.
Главное окно выглядит просто:

И code-behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Loaded += (o, args) => Create(); // вначале запустим Create PreviewKeyDown += (o, args) => Play(); // а по нажатию клавиши - Play }
// список контролов, отображающих строки List lineControls = new List();
void Create() // https://msdn.microsoft.com/en-us/library/ms745816(v=vs.110).aspx { TextBlock tb = Source; var text = tb.Text; FormattedText formattedText = new FormattedText( text, CultureInfo.GetCultureInfo("en-US"), FlowDirection.LeftToRight, new Typeface( tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black);
// установили максимальную ширину, чтобы текст был разбит на части formattedText.MaxTextWidth = Source.ActualWidth;
var boundingBoxes = // побуквенная ширина и позиции Enumerable.Range(0, text.Length) .Where(k => !char.IsWhiteSpace(text[k])) .Select(k => formattedText.BuildHighlightGeometry(new Point(), k, 1) .Bounds) .ToList();
// вычисляем охватывающий прямоугольник всех прямоугольников var totalBb = boundingBoxes.Aggregate(Rect.Union); var totalExtent = totalBb.Width;
List> boundingBoxesByLine = new List>(); List currentLine = null; double lastRectBottom = double.NegativeInfinity; foreach (var rect in boundingBoxes) { // проверка на новую строку. если верх текущего прямоугольника там же, // где низ предыдущего прямоугольника, или ещё ниже - новая строка, иначе нет if (rect.Top >= lastRectBottom) { // добавим старую строку в список строк if (currentLine != null) boundingBoxesByLine.Add(currentLine); // новый пустой контейнер прямоугольников для новой строки currentLine = new List(); } currentLine.Add(rect); lastRectBottom = rect.Bottom; } if (currentLine != null) // последнюю строку не теряем boundingBoxesByLine.Add(currentLine);
// стащили геометрию у текста... var geo = formattedText.BuildGeometry(new Point());
// строим по контролу для каждой строки: foreach (var line in boundingBoxesByLine) { // ... отдавая ему геометрию: var lineControl = new SingleLine(geo, line, totalExtent); Container.Children.Add(lineControl); lineControls.Add(lineControl); } }
async void Play() { // проигрываем просто построчно foreach (var line in lineControls) await line.Play(); } }
Всё!

Результат:

Стилизация input color

Появилась потребность в стилизации input[type="color"]. Уже сколько пытаюсь, никак не могу понять всех стилей этого объекта. Помогите стилизовать и сделать так, как на картинке:

Моя попытка:
input[type="color"] { width: 100px; height: 100px; border: none; border-radius: 50%; overflow: hidden; } .color {position: relative;} .color:after { display: block; position: absolute; content: ''; top: 25px; left: 25px; width: 50px; height: 50px; border-radius: 50%; background-color: white; }



Ответ

Стилизовать такое с помощью одного input color вряд ли выйдет. Я бы советовал воспользоваться дополнительными элементами:
*{ box-sizing: border-box; } label { display: inline-block; position: relative; width: 150px; height: 150px; cursor: pointer; overflow: hidden; } input[type="color"] { -webkit-appearance: none; border: none; background: #fff; width: 150px; height: 150px; border-radius: 50%; overflow: hidden; outline: none; cursor: inherit; } .circle { position: absolute; left: 30px; top: 30px; width: 90px; height: 90px; background-color: white; border-radius: 50%; cursor: inherit; } /* -webkit */ input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; } input[type="color"]::-webkit-color-swatch { border: none; border-radius: 50%; } /* firefox */ input[type=color]::-moz-focus-inner { border: none; padding: 0; border-radius: 50%; } input[type=color]::-moz-color-swatch { border: none; border-radius: 50%; height: 140px; }


Каким образом можно реализовать бегущую подсветку (выделение) многострочного текста?

Здесь уже был вопрос про закраску текста и на него был дан ответ
Вкратце суть решения состоит в создании FormattedText из содержимого скрытого TextBlock, а затем создается геометрия из FormattedText, которая отображается с помощью Path, который в свою очередь закрашивается с помощью анимации на нем.
И все прекрасно работает пока текст занимает одну строку, а вот когда строк несколько, получается не то, что хотелось бы.

XAML я повторять не буду, т.к. он полностью совпадает с таковым в выше указанном ответе. Мой код тоже мало чем отличается, но я его приведу для ясности вопроса.
private List _RectsForFill; // прямоугольники с каждым символом private double _LengthFillText; // общая ширина рисованного текста
//анимация Storyboard _Storyboard; DoubleAnimation _FromAnimation; DoubleAnimation _ToAnimation;
private void CreateFillText() { //получаем текстовое содержимое TextBlock tb = this.textBlockHidden; var text = tb.Text;
//создаем экземпляр форматированного текста FormattedText formattedText = new FormattedText( text, CultureInfo.GetCultureInfo("en-US"), FlowDirection.LeftToRight, new Typeface( tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black // конкретная кисть нам не важна, мы используем только геометрию );
//берем переносы строк у эталонного текстблока formattedText.MaxTextWidth = this.textBlockHidden.Width; formattedText.MaxTextHeight = this.textBlockHidden.Height;
// стащили геометрию у текста... var geo = formattedText.BuildGeometry(new Point()); // ...и отдали её Path'у Target.Data = geo;
//вычислим прямоугольники для заполнения GetRectsForFill(text, formattedText); }
private void GetRectsForFill(string text, FormattedText formattedText) { var bb = formattedText.BuildHighlightGeometry(new Point()); _LengthFillText = bb.Bounds.Width; // общая ширина
//заполняем коллекцию побуквенных боксов _RectsForFill = Enumerable.Range(0, text.Length) .Select(k => formattedText.BuildHighlightGeometry(new Point(), k, 1) .Bounds) .ToList();
//ссылки на анимацию для дальнейшей работы с ней _Storyboard = (Storyboard)Target.Resources["AnimationStoryboard"]; _FromAnimation = (DoubleAnimation)_Storyboard.Children[0]; _ToAnimation = (DoubleAnimation)_Storyboard.Children[1]; }
Вот метод, заполняет черным отрисованный текст
///

/// Закрашивание рисованного текста /// /// начальная позиция слова /// число закрашиваемых букв в слове public void FillTextPath(int startPos, int count) { if (count == 0) throw new ArgumentException(nameof(count));
//вычисляем индекс необходимого прямоугольника int index = startPos + count; if (index >= _RectsForFill.Count) index = _RectsForFill.Count - 1;
//необходимый прямоугольник Rect box = _RectsForFill[index];
//закрашиваем _FromAnimation.From = box.Left / _LengthFillText; _FromAnimation.To = box.Right / _LengthFillText; _ToAnimation.From = box.Left / _LengthFillText; _ToAnimation.To = box.Right / _LengthFillText; _Storyboard.Begin(); }


Ответ

Вы пошли правильным путём, разбить на отдельные Path'ы — хорошая идея.
Давайте её реализуем до конца.
Для начала, функциональность проигрывания уже достаточно сложна, так что вынесем её в отдельный UserControl. Затем, каждый контрол пусть отвечает за одну строку. Чтобы не искать куски по геометрии, просто отрежем эту строку при помощи Clip'а. На вход в UserControl будем подавать результаты разбора текста на геометрию (функцией Create).

В code-behind будет анимация:
public partial class SingleLine : UserControl { List boundingBoxes; double extent;
public SingleLine() { InitializeComponent(); }
public SingleLine(Geometry geo, List boundingBoxes, double totalExtent) : this() { Target.Data = geo; extent = totalExtent; Rect clip = boundingBoxes.Aggregate(Rect.Union); Clip = new RectangleGeometry(clip); this.boundingBoxes = boundingBoxes; }
public async Task Play() { var storyboard = (Storyboard)Target.Resources["AnimationStoryboard"]; var fromAnimation = (DoubleAnimation)storyboard.Children[0]; var toAnimation = (DoubleAnimation)storyboard.Children[1];
foreach (var b in boundingBoxes) { await Task.Delay(250); // перерыв между буквами fromAnimation.From = b.Left / extent; fromAnimation.To = b.Right / extent; toAnimation.From = b.Left / extent; toAnimation.To = b.Right / extent; storyboard.Begin(); await Task.Delay(250); // дождёмся конца анимации } } }
Зачем нам нужна такая сложность с Clip и totalExtent? К сожалению, я не нашёл метода выкусить только нужную часть геометрии. Поэтому мы даём на вход всю геометрию, а хотим показывать только текущую строку. Для этого мы вычисляем прямоугольник, соответствующий нужной части геометрии (текущей строке), и отрезаем отображение остального при помощи Clip'а. Но наши вычисления коэффициентов (b.Left / extent и т. д.) требуют процента от общей ширины Path'а, а не ширины текущей строки! (Напомню, наш Path получает геометрию всей строки, включая остальные строки тоже.) Поэтому приходится передавать ещё и общую ширину.
Теперь основной код. Он стал проще, так как часть функциональности отделилась. В нём мы не можем положить один, фиксированный Path, так как у нас количество строк не известно заранее. Поэтому контролы будем добавлять динамически.
Главное окно выглядит просто:

И code-behind:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Loaded += (o, args) => Create(); // вначале запустим Create PreviewKeyDown += (o, args) => Play(); // а по нажатию клавиши - Play }
// список контролов, отображающих строки List lineControls = new List();
void Create() // https://msdn.microsoft.com/en-us/library/ms745816(v=vs.110).aspx { TextBlock tb = Source; var text = tb.Text; FormattedText formattedText = new FormattedText( text, CultureInfo.GetCultureInfo("en-US"), FlowDirection.LeftToRight, new Typeface( tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch), tb.FontSize, Brushes.Black);
// установили максимальную ширину, чтобы текст был разбит на части formattedText.MaxTextWidth = Source.ActualWidth;
var boundingBoxes = // побуквенная ширина и позиции Enumerable.Range(0, text.Length) .Where(k => !char.IsWhiteSpace(text[k])) .Select(k => formattedText.BuildHighlightGeometry(new Point(), k, 1) .Bounds) .ToList();
// вычисляем охватывающий прямоугольник всех прямоугольников var totalBb = boundingBoxes.Aggregate(Rect.Union); var totalExtent = totalBb.Width;
List> boundingBoxesByLine = new List>(); List currentLine = null; double lastRectBottom = double.NegativeInfinity; foreach (var rect in boundingBoxes) { // проверка на новую строку. если верх текущего прямоугольника там же, // где низ предыдущего прямоугольника, или ещё ниже - новая строка, иначе нет if (rect.Top >= lastRectBottom) { // добавим старую строку в список строк if (currentLine != null) boundingBoxesByLine.Add(currentLine); // новый пустой контейнер прямоугольников для новой строки currentLine = new List(); } currentLine.Add(rect); lastRectBottom = rect.Bottom; } if (currentLine != null) // последнюю строку не теряем boundingBoxesByLine.Add(currentLine);
// стащили геометрию у текста... var geo = formattedText.BuildGeometry(new Point());
// строим по контролу для каждой строки: foreach (var line in boundingBoxesByLine) { // ... отдавая ему геометрию: var lineControl = new SingleLine(geo, line, totalExtent); Container.Children.Add(lineControl); lineControls.Add(lineControl); } }
async void Play() { // проигрываем просто построчно foreach (var line in lineControls) await line.Play(); } }
Всё!

Результат: