Страницы

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

понедельник, 18 февраля 2019 г.

Выбор между .load и $.get

На странице есть меню с кнопками, при нажатии на которые должен подгружаться контент из php-файлов и вставляться в соответствующие дивы. Страница статичная, без CMS. Подскажите какой из 2х описаных в заголовке вариантов подойдет для этого лучше всего? Какая между ними разница?


Ответ

должен подгружаться контент из php-файлов и вставляться в соответствующие дивы
Это практически описание работы функции .load
Поэтому в данном случае стоит использовать ее.
$.get делает только GET запрос. В то же время, .load в зависимости от параметров может делать запрос любого типа и даже выбирать конкретный элемент из результата, который надо вставить на страницу.
Для это нужно указать в параметре url нужный селектор через пробел.
elements.load('site/url/ #element')
Приведенный код вставит только содержимое элемента с id=element а не всю загруженную разметку.
Разница так же заключается в возвращаемом значении: $.get возвращает Deferred (реализация Promise), в то время как .load возвращает текущую коллекцию, что позволяет продолжить с ней работать.

Что произойдет с переменной после return value++?

Пример:
int foo() { int value = 0; return value++; }
Метод вернет 0, но что произойдет с переменной value? Будет ли компилятор генерировать код для ++? Или оптимизирует? И как это проверить?


Ответ

Метод вернет 0, но что произойдет с переменной value? Будет ли компилятор генерировать код для ++? Или оптимизирует? И как это проверить?
Вот что я обнаружил когда открыл откомпилированный файл (с расширением .class) в Intellij IDEA. После декомпиляции код будет выглядеть так (в Java 8):
int foo() { int value = 0; byte var10000 = value; int var2 = value + 1; return var10000; }
Компилятор создаст новую переменную var10000 как вы видите в виде byte так как переменная value равна нулю и byte будет вполне достаточно и сэкономит нам памяти. Потом почему то создает новую переменную var2 типа int и добавляет единичку и возвращает результат.
Если хотите проверить в других компиляторах, то в IDE выберете нужную Вам версию JDK, или даже можно пользоваться jd декомпилятором без IDE.

Input с выпадающим списком выбора кода оператора

Как реализовать инпут, при нажатии на который появлялся бы выпадающий список с возможностью выбора оператора.
Нашел вот такое решение, с использованием database:

Но этот способ больше подходит для автозаполнения, а мне нужно чтобы просто можно было выбрать код оператора и он записался в инпут. Может есть у кого готовое решение или какая-то библиотека?


Ответ

$('select#cities').on('change', function() { $('input[name="city"]').val(this.value); });

Для чего ипользуют ветку gh-pages?

Для чего создают branch gh-pages? Вижу часто во многих репах на github


Ответ

Это GitHub Pages
Github позволяет выступать как хостинг для ваших проектов. Часто это статические сайты, данные (исходники) которых находятся прямо в репозитории, рядом с исходниками проекта (или нет).
Как создать сайт проекта с помощью gh-pages.

Странное поведение оператора — в PHP

$b=10; $f=$b + --$b; echo $f; //выведет 18, почему не 19 !?!?!?
//или еще вот так $b=10; $f=$b + $b + $b--; echo $f; //выведет 30, ожидаемо, но, смотрим дальше
$b=10; $f=$b + $b--; echo $f; //выведет...пауза...затаили дыхание...барабанный бой...19!!!!
Буду очень признателен, если кто-нибудь сможет объяснить, почему так происходит. Версия PHP 7.1.6 (xampp v3.2.2)


Ответ

Очень интересный приоритет операторов у этого вашего PHP. К тому же сложение выполняется по порядку, но слагаемые вычисляются наоборот справа налево в данном случае. Этот порядок не гарантируется, поэтому результат на других версиях PHP может отличаться. К примеру на версии 5.0.4 порядок тот же, но слагаемые вычисляются более привычно слева направо, см. UPD.
Уравнение первое $f=$b + $b + --$b;
Складываем $b + $b и запоминаем результат $temp = 20. Справа инкремент, выполняем его и складываем с левым значением $temp. 20 + 9 = 29.
Уравнение второе $f = $b + --$b;
Справа инкремент, выполняем его и получаем 9. Слева ссылка на $b, раскрываем ее и там у нас лежит после инкремента девятка. 9 + 9 = 18.
Уравнение третье $f=$b + $b + $b--;
Складываем $b + $b и запоминаем результат $temp = 20. Справа преинкремент, выполняем его возвращая 10 и складываем с левым значением $temp. 20 + 10 = 30
Уравнение четвертое $f=$b + $b--;
Слева инкремент, выполняем его возвращая 10 и складываем с левым значением. Слева ссылка, раскрываем ее и получаем 9. 9 + 10 = 19.
Но гораздо веселее складывать такое: $f=$b + $b + $b-- + $b + $b; и $f=$b + $b + --$b + $b + $b;
P.S. Лучше избегайте таких конструкций, результат которых может быть неопределенным и никак не гарантируется разработчиками.
UPD.. На версии 5.0.4 порядок тот же, но слагаемые вычисляются более привычно слева направо. Поэтому в примерах выше сначала раскроются ссылки слева и потом будет вычислен инкремент справа.
Уравнение первое $f=$b + $b + --$b;
Складываем $b + $b и запоминаем результат $temp = 20. Складываем с левым значением $temp полученный инкремент. 20 + 9 = 29.
Уравнение второе $f = $b + --$b;
Слева ссылка на $b, раскрываем ее и получаем 10. Справа инкремент, выполняем его и получаем 9. 10 + 9 = 19.
Уравнение третье $f=$b + $b + $b--;
Складываем $b + $b и запоминаем результат $temp = 20. Складываем с левым значением $temp преинкремент 10. 20 + 10 = 30
Уравнение четвертое $f=$b + $b--;
Слева ссылка, раскрываем ее и получаем 10. Слева инкремент, выполняем его возвращая 10 и складываем с левым значением. 10 + 10 = 20.

Троичная логика в SQL

В SQL фигурирует значение NULL, функциональная роль которого заключается в обозначении отсутствующих или неизвестных данных. Для этого значения установлены правила выполнения предикатов, которые при участии NULL возвращают не TRUE или FALSE, а UNKNOWN, что вполне соответствует бытовой логике: если одно из данных отсутствует, то результат операции нам неизвестен. Но есть ли у такого расширения до троичной логики функциональная роль, или на практике, если заменить все UNKNOW на FALSE, то мы не заметим разницы? Насколько я знаю, от предиката требуется вернуть TRUE, чтобы произошло некоторое действие. Быть может, в разных реализациях это устроенно по разному? Буду рад каким-нибудь рассказам или ссылкам на эту тему.


Ответ

Если заменить Unknown значения на False, то возникают неоднозначности:
Not False = True, но Not Unknown != True Из предыдущего следует False or Not False = True. Но Unknown or Not Unknown != True. Не всегда любое высказывание либо истинно, либо ложно
Например:
Выведем продукты, которые стоят больше не больше 100:
select * from products t1 where not t1.price>100
В этом вариант в результат не попадут строки, в которых стоимость(price) не известна. Если же Unknown заменить на False, то в результат попали бы строчки, для которых стоимость не известна, что не соответствует истине.
PS
Справедливости ради надо сказать, что в данном примере запрос можно переписать для правильной работы в случае FALSE = Unknown
select * from products t1 where t1.price<=100

Удаленность сторон куба от плоскости проекции

Дано:
Система координат Куб вращается вокруг своего центра по всем трем осям. Угол поворота заранее не известен. Параллельная проекция считается по формуле:
x`` = x; y`` = y + z / 4; В единицу времени отображаются только 3 ближних к плоскости проекции грани.
Вопрос:
Как определить, какие из граней ближние?
Делаю таким образом: беру попарно грани правую и левую, переднюю и заднюю, верхнюю и нижнюю, затем вычисляю дальнюю и ближнюю точки по оси (Z), составляю уравнение прямой, беру вторую по удаленности точку по оси (Z), и подставляю в это уравнение - так определяю наклон плоскости. Исходя из этого выбираю какая из 2х граней ближе к плоскости проекции.
Такой код не работает.
'use strict'; var figures; var figure_111 = { //╔═══╗ //║ ║ //╚═══╝ figureType: 5, figurePositionY: 1, figurePositionX: 1, figurePositionZ: 1, shape: [ [[5]] ], rotateZ: function () { this.shape = rotateZ(this.shape); this.figurePositionZ = changePosition(this.figurePositionZ); } }; function initAnimation() { figures = [ figure_111 ]; // Запускаем падение figureStartsFalling(); } // Текущая фигура var currentFigure; // Размер фигуры var size = 30; // Объект на основании фигуры var fallingObject; // Цвет фигуры var figureColor; // Центр фигуры var t0; var canvas = document.getElementById('animation'); var canvas_context = canvas.getContext('2d'); // Сместим центр оси (Х) в середину поля canvas_context.translate(canvas.width / 2, 0); var doTenTimes; function figureStartsFalling() { doTenTimes = 30; // Переопределяем текущую фигуру currentFigure = figures[Math.floor(Math.random() * figures.length)]; // Получим цвет текущей фигуры figureColor = 'rgb(0,200,0)'; // Высота фигуры var figureHeight = currentFigure.shape.length; // Строим 3Д объект на основании фигуры fallingObject = []; var cube = []; var side = []; for (var y = currentFigure.shape.length - 1; y > -1; y--) { for (var x = 0; x < currentFigure.shape[0].length; x++) { for (var z = 0; z < currentFigure.shape[0][0].length; z++) { // Для каждой клеточки создаем кубик с 5 гранями cube = []; if (currentFigure.shape[y][x][z] != 0) { // Левая стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'left'} ]; // Центральная координата, нужна для расчета удаленности по осям (X) и (Z) side[4] = getSidecentralCoordinate(side); // Если левее чтото есть, то не рисуем if (x > 0 && currentFigure.shape[y][x - 1][z] != 0) { side[5] = false; } cube.push(side); // Правая стенка side = [ {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'right'} ]; side[4] = getSidecentralCoordinate(side); // Если правее чтото есть, то не рисуем if (x + 1 < currentFigure.shape[0].length && currentFigure.shape[y][x + 1][z] != 0) { side[5] = false; } cube.push(side); // Задняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'rear'} ]; side[4] = getSidecentralCoordinate(side); // Если сзади чтото есть, то не рисуем if (z > 0 && currentFigure.shape[y][x][z - 1] != 0) { side[5] = false; } cube.push(side); // Передняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {m:'front'} ]; side[4] = getSidecentralCoordinate(side); // Если впереди чтото есть, то не рисуем if (z + 1 < currentFigure.shape[0][0].length && currentFigure.shape[y][x][z + 1] != 0) { side[5] = false; } cube.push(side); // Верхняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {m:'top'} ]; side[4] = getSidecentralCoordinate(side); // Если сверху чтото есть, то не рисуем if (y > 0 && currentFigure.shape[y - 1][x][z] != 0) { side[5] = false; } cube.push(side); // Нижняя стенка side = [ {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'bottom'} ]; side[4] = getSidecentralCoordinate(side); // Если снизу чтото есть, то не рисуем if (y + 1 < currentFigure.shape.length && currentFigure.shape[y + 1][x][z] != 0) { side[5] = false; } cube.push(side); fallingObject.push(cube); } } } } // Определим центр фигуры t0 = { y:currentFigure.shape.length / 2 * size - figureHeight*size, x:currentFigure.shape[0].length / 2 * size, z:currentFigure.shape[0][0].length / 2 * size }; // Запускаем падение setTimeout(figureFalls, 100); } // Угол поворота, не изменяется var deg = 1; // Текущая проекция var arrProj; var tempArr; function figureFalls() { // Сначала очистим экран - закрасим белым цветом clearScreen(canvas, canvas_context, 'rgb(255,255,255)'); // Изменяем положение текущей фигуры for (var i = 0; i < fallingObject.length; i++) { for (var j = 0; j < 6; j++) { for (var k = 0; k < 4; k++) { // Повернем фигуру на угол fallingObject[i][j][k] = rotateOnDegreeY(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeX(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeZ(t0, fallingObject[i][j][k]); // и опустим вниз на 1px fallingObject[i][j][k].y += 1; } // Пересчитаем центральную координату плоскости fallingObject[i][j][4] = getSidecentralCoordinate(fallingObject[i][j]); } doTenTimes -= 1; // Пересчитаем центральную координату кубика fallingObject[i][6] = getCubecentralCoordinate(fallingObject[i]); // Если кубик ушел слишком далеко - удаляем кубик if (fallingObject[i][6].y > canvas.height) { fallingObject.splice(i, 1); i -=1; } } // Центр фигуры тоже опускается t0.y += 1; // Сортируем кубики по удаленности от начала координат по (X) и (Z) var exit = false; while (!exit) { exit = true; for (var i = 0; i < fallingObject.length - 1; i++) { if (fallingObject[i][6].z > fallingObject[i + 1][6].z) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } else if (fallingObject[i][6].z == fallingObject[i + 1][6].z) { if (fallingObject[i][6].x > fallingObject[i + 1][6].x) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } } } } // Отрисовываем фигуру for (var i = 0; i < fallingObject.length; i++) { // Получим параллельную проекцию arrProj = getParallelProjection(fallingObject[i]); // Нарисуем параллельную проекцию fillFigure(canvas_context, arrProj, figureColor); } // Если в видимой области еще есть кубики - // переходим на следующий шаг if (fallingObject.length > 0) { setTimeout(figureFalls, 100); } // иначе начинаем заново с новой фигурой else { setTimeout(figureStartsFalling, 100); } } // Получим центральную координату плоскости // для расчета удаленности от задней стенки function getSidecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 4; i++) { centralCoordinate.x += side[i].x; centralCoordinate.y += side[i].y; centralCoordinate.z += side[i].z; } centralCoordinate.x = centralCoordinate.x / 4; centralCoordinate.y = centralCoordinate.y / 4; centralCoordinate.z = centralCoordinate.z / 4; centralCoordinate.m = side[4].m; return centralCoordinate; } // Получим центральную координату кубика // для расчета удаленности от начала координат function getCubecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 6; i++) { centralCoordinate.x += side[i][4].x; centralCoordinate.y += side[i][4].y; centralCoordinate.z += side[i][4].z; } centralCoordinate.x = centralCoordinate.x / 6; centralCoordinate.y = centralCoordinate.y / 6; centralCoordinate.z = centralCoordinate.z / 6; return centralCoordinate; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y) // относительно точки t0(x,y,z) function rotateOnDegreeY(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.y = t.y; t_new.z = t0.z + (t.x - t0.x) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (X) // относительно точки t0(x,y,z) function rotateOnDegreeX(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t.x; t_new.y = t0.y + (t.y - t0.y) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.z = t0.z + (t.y - t0.y) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Z) // относительно точки t0(x,y,z) function rotateOnDegreeZ(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.y - t0.y) * Math.sin(rad); t_new.y = t0.y + (t.x - t0.x) * Math.sin(rad) + (t.y - t0.y) * Math.cos(rad); t_new.z = t.z; // Возвращаем полученное значение return t_new; } // Закрашиваем весь экран определенным цветом function clearScreen(canvas, context, color) { context.fillStyle = color; context.beginPath(); context.fillRect(- canvas.width / 2, 0, canvas.width, canvas.height); context.closePath(); context.fill(); } // Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k; var arr_new = []; // Попарно работаем с гранями arr_new[0] = getSideParallelProjection(arr[0], arr[1], arr[6]); // Левая и правая arr_new[1] = getSideParallelProjection(arr[2], arr[3], arr[6]); // Задняя и передняя arr_new[2] = getSideParallelProjection(arr[4], arr[5], arr[6]); // Верхняя и нижняя return arr_new; } // Параллельная проекция грани function getSideParallelProjection(side1, side2, cubeCenter) { // Сначала выясним какая пара нам попалась var centralProjection1 = getPointParallelProjection(side1[4]); var centralProjection2 = getPointParallelProjection(side2[4]); var coordinate = { x:Math.abs(side1[4].x - side2[4].x), y:Math.abs(side1[4].y - side2[4].y), z:Math.abs(side1[4].z - side2[4].z)}; var i; // Найдем дальнюю (1) и ближнюю (2) от экрана точки // по оси (Z) и посчитаем их проекции var point1 = {z:10000}, point2 = {z:-10000}, point3 = {z:10000}; for (i = 0; i < 4; i++) { if (point1.z > side1[i].z) { point1 = side1[i]; } if (point1.z == side1[i].z && point1.x > side1[i].x) { point1 = side1[i]; } if (point1.z == side1[i].z && point1.x == side1[i].x && point1.y < side1[i].y) { point1 = side1[i]; } if (point2.z < side1[i].z) { point2 = side1[i]; } if (point2.z == side1[i].z && point2.x < side1[i].x) { point2 = side1[i]; } if (point2.z == side1[i].z && point2.x == side1[i].x && point2.y > side1[i].y) { point2 = side1[i]; } } for (i = 0; i < 4; i++) { if (point3.z > side1[i].z && point1 != side1[i]) { point3 = side1[i]; } if (point3.z == side1[i].z && point3.x > side1[i].x && point1 != side1[i]) { point3 = side1[i]; } if (point3.z == side1[i].z && point3.x == side1[i].x && point3.y < side1[i].y && point1 != side1[i]) { point3 = side1[i]; } } var direction = (point3.x - point1.x) / (point2.x - point1.x) - (point3.y - point1.y) / (point2.y - point1.y); var projection = []; // (X) Правая и левая грани if (coordinate.x > coordinate.y && coordinate.x > coordinate.z) { point1 = getPointParallelProjection(point1); point2 = getPointParallelProjection(point2); point3 = getPointParallelProjection(point3); var direction = (point3.x - point1.x) / (point2.x - point1.x) - (point3.y - point1.y) / (point2.y - point1.y); var selected; if (point1.x >= point2.x) { if (centralProjection1.x >= centralProjection2.x) { selected = side1; } else { selected = side2; } } else { if (centralProjection1.x <= centralProjection2.x) { selected = side1; } else { selected = side2; } } if (selected.length < 6) { for (i = 0; i < 4; i++) { projection[i] = getPointParallelProjection(selected[i]); } projection[4] = 'rgb(200,200,0)'; } } // (Z) Передняя и задняя грани else if (coordinate.z > coordinate.x && coordinate.z > coordinate.y) { var selected; if (point1.x >= point2.x && direction < 0) { if (centralProjection1.x >= centralProjection2.x) { selected = side1; } else { selected = side2; } } else { if (centralProjection1.x <= centralProjection2.x) { selected = side1; } else { selected = side2; } } if (selected.length < 6) { for (i = 0; i < 4; i++) { projection[i] = getPointParallelProjection(selected[i]); } projection[4] = 'rgb(200,0,0)'; } } // (Y) Верхняя и нижняя грани else { var selected; if (point1.y <= point2.y) { if (centralProjection1.y <= centralProjection2.y) { selected = side1; } else { selected = side2; } } else { if (centralProjection1.y >= centralProjection2.y) { selected = side1; } else { selected = side2; } } if (selected.length < 6) { for (i = 0; i < 4; i++) { projection[i] = getPointParallelProjection(selected[i]); } projection[4] = 'rgb(0,0,0)'; } } return projection; } // Параллельная проекция точки function getPointParallelProjection(point) { return { x:point.x, y:point.y + point.z / 4}; // return { // x:point.x + point.z * Math.cos(Math.PI * 3 / 4) / 2, // y:point.y + point.z * Math.sin(Math.PI * 3 / 4) / 2}; } // Рисуем фигуру по точкам из массива function fillFigure(context, arr, color) { context.lineWidth = 2; context.strokeStyle = 'rgb(255,255,255)'; context.fillStyle = color; for (var i = 0; i < arr.length; i++) { context.beginPath(); for (var j = 0; j < arr[i].length; j++) { if (arr[i][4] != undefined) { context.fillStyle = arr[i][4]; } else { context.fillStyle = color; } if (j == 0) { context.moveTo(arr[i][j].x, arr[i][j].y); } else { context.lineTo(arr[i][j].x, arr[i][j].y); } } context.closePath(); context.fill(); context.stroke(); } } // Запускаем наш скрипт window.onload = function() { setTimeout(initAnimation, 200); };

Ваш браузер не поддерживает Canvas



Ответ

Слова ничего не значат. Покажите мне код. (c) Линус Торвальдс
Challenge accepted. Фиддл
'use strict'; var figures; var figure_111 = { //╔═══╗ //║ ║ //╚═══╝ figureType: 5, figurePositionY: 1, figurePositionX: 1, figurePositionZ: 1, shape: [ [[5]] ], rotateZ: function () { this.shape = rotateZ(this.shape); this.figurePositionZ = changePosition(this.figurePositionZ); } }; function initAnimation() { figures = [ figure_111 ]; // Запускаем падение figureStartsFalling(); } // Текущая фигура var currentFigure; // Размер фигуры var size = 70; // Объект на основании фигуры var fallingObject; // Цвет фигуры var figureColor; // Центр фигуры var t0; var canvas = document.getElementById('animation'); var canvas_context = canvas.getContext('2d'); // Сместим центр оси (Х) в середину поля canvas_context.translate(canvas.width / 2, 0); var doTenTimes; function figureStartsFalling() { doTenTimes = 30; // Переопределяем текущую фигуру currentFigure = figures[Math.floor(Math.random() * figures.length)]; // Получим цвет текущей фигуры figureColor = 'rgb(0,200,0)'; // Высота фигуры var figureHeight = currentFigure.shape.length; // Строим 3Д объект на основании фигуры fallingObject = []; var cube = []; var side = []; for (var y = currentFigure.shape.length - 1; y > -1; y--) { for (var x = 0; x < currentFigure.shape[0].length; x++) { for (var z = 0; z < currentFigure.shape[0][0].length; z++) { // Для каждой клеточки создаем кубик с 5 гранями cube = []; if (currentFigure.shape[y][x][z] != 0) { // Левая стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {m:'left'} ]; // Центральная координата, нужна для расчета удаленности по осям (X) и (Z) side[4] = getSidecentralCoordinate(side); // Если левее чтото есть, то не рисуем if (x > 0 && currentFigure.shape[y][x - 1][z] != 0) { side[5] = false; } cube.push(side); // Правая стенка side = [ {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {m:'right'} ]; side[4] = getSidecentralCoordinate(side); // Если правее чтото есть, то не рисуем if (x + 1 < currentFigure.shape[0].length && currentFigure.shape[y][x + 1][z] != 0) { side[5] = false; } cube.push(side); // Задняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {m:'rear'} ]; side[4] = getSidecentralCoordinate(side); // Если сзади чтото есть, то не рисуем if (z > 0 && currentFigure.shape[y][x][z - 1] != 0) { side[5] = false; } cube.push(side); // Передняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {m:'front'} ]; side[4] = getSidecentralCoordinate(side); // Если впереди чтото есть, то не рисуем if (z + 1 < currentFigure.shape[0][0].length && currentFigure.shape[y][x][z + 1] != 0) { side[5] = false; } cube.push(side); // Верхняя стенка side = [ {x:x*size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size}, {x:x*size + size, y:y*size - figureHeight*size, z:z*size + size}, {x:x*size, y:y*size - figureHeight*size, z:z*size + size}, {m:'top'} ]; side[4] = getSidecentralCoordinate(side); // Если сверху чтото есть, то не рисуем if (y > 0 && currentFigure.shape[y - 1][x][z] != 0) { side[5] = false; } cube.push(side); // Нижняя стенка side = [ {x:x*size, y:y*size + size - figureHeight*size, z:z*size}, {x:x*size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size + size}, {x:x*size + size, y:y*size + size - figureHeight*size, z:z*size}, {m:'bottom'} ]; side[4] = getSidecentralCoordinate(side); // Если снизу чтото есть, то не рисуем if (y + 1 < currentFigure.shape.length && currentFigure.shape[y + 1][x][z] != 0) { side[5] = false; } cube.push(side); fallingObject.push(cube); } } } } // Определим центр фигуры t0 = { y:currentFigure.shape.length / 2 * size - figureHeight*size, x:currentFigure.shape[0].length / 2 * size, z:currentFigure.shape[0][0].length / 2 * size }; // Запускаем падение setTimeout(figureFalls, 100); } // Угол поворота, не изменяется var deg = 1; // Текущая проекция var arrProj; var tempArr; function figureFalls() { // Сначала очистим экран - закрасим белым цветом clearScreen(canvas, canvas_context, 'rgb(255,255,255)'); // Изменяем положение текущей фигуры for (var i = 0; i < fallingObject.length; i++) { for (var j = 0; j < 6; j++) { for (var k = 0; k < 4; k++) { // Повернем фигуру на угол fallingObject[i][j][k] = rotateOnDegreeY(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeX(t0, fallingObject[i][j][k]); fallingObject[i][j][k] = rotateOnDegreeZ(t0, fallingObject[i][j][k]); // и опустим вниз на 1px fallingObject[i][j][k].y += 1; } // Пересчитаем центральную координату плоскости fallingObject[i][j][4] = getSidecentralCoordinate(fallingObject[i][j]); } doTenTimes -= 1; // Пересчитаем центральную координату кубика fallingObject[i][6] = getCubecentralCoordinate(fallingObject[i]); // Если кубик ушел слишком далеко - удаляем кубик if (fallingObject[i][6].y > canvas.height) { fallingObject.splice(i, 1); i -=1; } } // Центр фигуры тоже опускается t0.y += 1; // Сортируем кубики по удаленности от начала координат по (X) и (Z) var exit = false; while (!exit) { exit = true; for (var i = 0; i < fallingObject.length - 1; i++) { if (fallingObject[i][6].z > fallingObject[i + 1][6].z) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } else if (fallingObject[i][6].z == fallingObject[i + 1][6].z) { if (fallingObject[i][6].x > fallingObject[i + 1][6].x) { tempArr = fallingObject[i]; fallingObject[i] = fallingObject[i + 1]; fallingObject[i + 1] = tempArr; exit = false; } } } } // Отрисовываем фигуру for (var i = 0; i < fallingObject.length; i++) { // Получим параллельную проекцию arrProj = getParallelProjection(fallingObject[i]); // Нарисуем параллельную проекцию fillFigure(canvas_context, arrProj, figureColor); } // Если в видимой области еще есть кубики - // переходим на следующий шаг if (fallingObject.length > 0) { setTimeout(figureFalls, 100); } // иначе начинаем заново с новой фигурой else { setTimeout(figureStartsFalling, 100); } } // Получим центральную координату плоскости // для расчета удаленности от задней стенки function getSidecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 4; i++) { centralCoordinate.x += side[i].x; centralCoordinate.y += side[i].y; centralCoordinate.z += side[i].z; } centralCoordinate.x = centralCoordinate.x / 4; centralCoordinate.y = centralCoordinate.y / 4; centralCoordinate.z = centralCoordinate.z / 4; centralCoordinate.m = side[4].m; return centralCoordinate; } // Получим центральную координату кубика // для расчета удаленности от начала координат function getCubecentralCoordinate(side) { var centralCoordinate = {x:0, y:0, z:0}; for (var i = 0; i < 6; i++) { centralCoordinate.x += side[i][4].x; centralCoordinate.y += side[i][4].y; centralCoordinate.z += side[i][4].z; } centralCoordinate.x = centralCoordinate.x / 6; centralCoordinate.y = centralCoordinate.y / 6; centralCoordinate.z = centralCoordinate.z / 6; return centralCoordinate; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Y) // относительно точки t0(x,y,z) function rotateOnDegreeY(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.y = t.y; t_new.z = t0.z + (t.x - t0.x) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (X) // относительно точки t0(x,y,z) function rotateOnDegreeX(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t.x; t_new.y = t0.y + (t.y - t0.y) * Math.cos(rad) - (t.z - t0.z) * Math.sin(rad); t_new.z = t0.z + (t.y - t0.y) * Math.sin(rad) + (t.z - t0.z) * Math.cos(rad); // Возвращаем полученное значение return t_new; } // Поворачиваем точку t(x,y,z) на угол (deg) по оси (Z) // относительно точки t0(x,y,z) function rotateOnDegreeZ(t0, t) { var t_new = {}; // Переводим угол поворота из градусов в радианы var rad = (Math.PI / 180) * deg; // Рассчитываем координаты новой точки по формуле t_new.x = t0.x + (t.x - t0.x) * Math.cos(rad) - (t.y - t0.y) * Math.sin(rad); t_new.y = t0.y + (t.x - t0.x) * Math.sin(rad) + (t.y - t0.y) * Math.cos(rad); t_new.z = t.z; // Возвращаем полученное значение return t_new; } // Закрашиваем весь экран определенным цветом function clearScreen(canvas, context, color) { context.fillStyle = color; context.beginPath(); context.fillRect(- canvas.width / 2, 0, canvas.width, canvas.height); context.closePath(); context.fill(); } // Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k; var projected = []; var projection; for (i = 0; i < 6; i++) { projection = []; if (arr[i].length < 6) { for (j = 0; j < 4; j++) { projection[j] = getPointParallelProjection(arr[i][j]); } } projected[i] = projection; } return projected.filter(isClockwise); } function isClockwise(point_array) { if (point_array.length < 3) return false; var p0 = point_array[0]; var p1 = point_array[1]; var p2 = point_array[2]; return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y) > 0; } // Параллельная проекция точки function getPointParallelProjection(point) { return { x:point.x, y:point.y + point.z / 4}; } // Рисуем фигуру по точкам из массива function fillFigure(context, arr, color) { context.lineWidth = 2; context.strokeStyle = 'rgb(255,255,255)'; context.fillStyle = color; for (var i = 0; i < arr.length; i++) { context.beginPath(); for (var j = 0; j < arr[i].length; j++) { if (arr[i][4] != undefined) { context.fillStyle = arr[i][4]; } else { context.fillStyle = color; } if (j == 0) { context.moveTo(arr[i][j].x, arr[i][j].y); } else { context.lineTo(arr[i][j].x, arr[i][j].y); } } context.closePath(); context.fill(); context.stroke(); } context.font = '12px'; context.fillText('Рисуется сторон: ' + arr.length, -120, 20); } // Запускаем наш скрипт window.onload = function() { setTimeout(initAnimation, 200); };

Ваш браузер не поддерживает Canvas


Релевантный кусок:
// Получаем параллельную проекцию кубика на плоскость экрана function getParallelProjection(arr) { var i, j, k;
var projected = []; var projection;
for (i = 0; i < 6; i++) { projection = []; if (arr[i].length < 6) { for (j = 0; j < 4; j++) { projection[j] = getPointParallelProjection(arr[i][j]); } } projected[i] = projection; }
return projected.filter(isClockwise); }
function isClockwise(point_array) { if (point_array.length < 3) return false; var p0 = point_array[0]; var p1 = point_array[1]; var p2 = point_array[2]; return (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y) > 0; }
Вся магия в том, что мы проверяем в том, как рисуются грани. Для этого, правда, пришлось поправить порядок задания вершин в самом начале, дабы каждая грань рисовалась по часовой стрелке. После проекции "векторным" произведением проверяется порядок спроецированных вершин, и если он оказался против часовой стрелки - этот набор вершин отбрасывается.
Бонусом на канвас выводится число граней, переданных в функцию отрисовки - чисто так, для верности расчётов.
Стоит ли говорить, что решена несколько иная задача, чем поставленная в вопросе, но код есть код :)

Как правильно организовать работу с данными (C# Desktop App, Database)?

Упрощенная схема БД
Нужна помощь в организации работы с данными. Сабжект: десктопное приложение для гостиницы. В таблице Reservations данные по каждому размещению. Каждое размещение может состоять из одного или нескольких дней (ResDays). На каждый день размещения может быть добавлена доп. услуга (AddServices).
В программе для этого я использую коллекции. Очень упрощенно:
class AddServiceItem { string Name; Decimal Cost; } class ResDayItem { DateTime Date; Decimal Cost; Collection AddServices; } class Reservation { DateTime StartDate; DateTime StartDate; int Status; Collection ResDays; }
С созданием размещения проблем нет - пользователь выбирает количество дней, создается экземпляр Reservation, а коллекции из дней и доп. услуг заполняются юзером в интерфейсе программы. После нажатия кнопки "Добавить размещение" все записывается в БД.
Конечно, нужно иметь возможность изменять все данные. При открытии размещения для редактирования, я опять создаю экземпляр Reservation, а коллекции заполняю значениями из БД примерно так
MyReservation = new Reservation(); //создаю коллекцию дней MyReservation.ResDays = new Collection(); //и для каждого дня размещения с Id == resId foreach (var Day in db.Table().Where(x => x.ResId == resId)) { //создаю экземпляр ResDayItem ResDayItem DayItem = new ReservationDayItem(); //и в нем коллекцию доп. услуг DayItem.AddServices = new Collection(); foreach (var AddService in db.Table().Where(x => x.ResDayId == Day.Id)) DayItem.AddServices.Add(AddService); MyReservation.ResDays.Add(DayItem); }
Вопрос в следующем: как (в каком порядке) делать апдейт БД при изменении коллекций? Хотелось бы, чтобы в БД все писалось при нажатии пользователем кнопки "Сохранить". Если количество дней/доп.услуг осталось прежним, то это просто update по всем записям, а если юзер удалил что-то? Как узнать, какие именно элементы коллекций были удалены, чтобы удалить соответствующие записи из БД? Может быть я вообще применил неверный подход с такой вложенностью коллекций? Как правильно организовать работу с данными в такой задаче?


Ответ

В более-менее сложных программах реализуют примерно такую структуру

Как пример реализации паттернов Repository и Unit of Work могу предложить вам следующий вариант. Для Repository мы будем опираться на следующую иллюстрацию

Для Unit of Work мы будем опираться на следующую иллюстрацию

1) Создайте проект Class Library LibDomain, создайте в нем две папки: Repositories и Models. 2) Cоздайте след. классы в папке Models
public class Reservation { //ctor public Reservation() => Days = new HashSet();
public long Id { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } public int Status { get; set; }
public virtual ICollection Days { get; set; } }
``
public class ReservedDay { //ctor public ReservedDay() => Services = new HashSet();
public long Id { get; set; } public DateTime Date { get; set; } public decimal Cost { get; set; }
//внешний ключ public long ReservationId { get; set; } //навигационное св-во public virtual Reservation Reservation { get; set; }
public ICollection Services { get; set; } }
``
public class AdditionService { public int Id { get; set; } public string Name { get; set; } public decimal Cost { get; set; }
//внешний ключ public long ReservedDayId { get; set; } //навигационное св-во public virtual ReservedDay Day { get; set; } }
`` 3) в папке репозиториев вам нужно будет создать главный интерфейс репозитория
public interface IRepository where TEntity : class { //Queries Task> Get(); Task Get(long id); Task> Find(Expression> predicate);
//Commands Task Add(TEntity entity); Task AddRange(IEnumerable entities); void Remove(TEntity entity); void RemoveRange(IEnumerable entities); }
и на каждую сущность свой интерфейса репозитория (даю пример только двух)
public interface IReservationRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных для данной сущности
Task GetByIdWithDays(long id);
Task GetByIdWithDaysAndServices(long id); }
``
public interface IReservedDayRepository : IRepository { //здесь можно добавить объявление необходимых методов //актуальных только для данной сущности }
создайте в корне проекта интерфейс для UnitOfWork
public interface IUnitOfWork : IDisposable { IReservationRepository Reservations { get; } IReservedDayRepository ReservedDays { get; } IAdditionServiceRepository AdditionServices { get; }
Task Commit(); }
4) Создайте в решении еще один проект библиотеки LibData, в котором мы непосредственно реализуем работу с SQLite через Entity Framework Core. Добавьте ссылку на LibDomain и установите через NuGet Package Manager пакет Microsoft.EntityFrameworkCore.Sqlite (важно! устанавливайте версию 1.1.2, а не самую последнюю 2.0, которая вышла несколько дней назад, иначе будут проблемы).
5) в проекте создайте класс SqliteDbContext
public class SqliteDbContext : DbContext { //ctor public SqliteDbContext(DbContextOptions options) : base(options) { //создание БД, если она не была ранее создана Database.EnsureCreated(); }
public virtual DbSet Reservations { get; set; } public virtual DbSet ReservedDays { get; set; } public virtual DbSet AdditionServices { get; set; } }
и создайте папку Repositories и в ней класс репозитория
public abstract class Repository : IRepository where TEntity : class { protected readonly DbContext _context;
//ctor public Repository(DbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context)); }
//Queries public virtual async Task> Get() { return await _context.Set().ToArrayAsync(); }
public virtual async Task Get(long id) { return await _context.Set().FindAsync(id); }
public virtual async Task> Find(Expression> predicate) { return await _context.Set().Where(predicate).ToArrayAsync(); }
//Commands public virtual Task Add(TEntity entity) { return _context.AddAsync(entity); }
public virtual Task AddRange(IEnumerable entities) { return _context.AddRangeAsync(entities); }
public virtual void Remove(TEntity entity) { _context.Remove(entity); }
public virtual void RemoveRange(IEnumerable entities) { _context.RemoveRange(entities); } }
и классы для каждой сущности: ReservationRepository, ReservedDayRepository, AdditionServiceRepository (пример только для первого класса)
public class ReservationRepository : Repository, IReservationRepository { private readonly SqliteDbContext _context;
//ctor public ReservationRepository(SqliteDbContext context) : base(context) { _context = context; }
//далее реализация методов из IReservationRepository //или переопределение методов из Repository под Reservation
public Task GetByIdWithDays(long id) { return _context.Reservations .Include(r => r.Days) .FirstAsync(r => r.Id == id); }
public Task GetByIdWithDaysAndServices(long id) { return _context.Reservations .Include(r => r.Days) .ThenInclude(d => d.Services) .FirstAsync(r => r.Id == id); } }
6) создайте класс, кот. реализутет интерфейс IUnitOfWork
public class SqliteDataService : IUnitOfWork { private readonly SqliteDbContext _context;
//ctor public SqliteDataService(SqliteDbContext context) { _context = context ?? throw new ArgumentNullException(nameof(context));
Reservations = new ReservationRepository(_context); ReservedDays = new ReservedDayRepository(_context); AdditionServices = new AdditionServiceRepository(_context); }
public IReservationRepository Reservations { get; private set; } public IReservedDayRepository ReservedDays { get; private set; } public IAdditionServiceRepository AdditionServices { get; private set; }
public Task Commit() => _context.SaveChangesAsync(); public void Dispose() => _context.Dispose();
///

/// Соединение с БД и передача экземпляра этого сервиса /// /// строка соединения с БД /// экземпляр этого сервиса public static SqliteDataService GetService(string connectionString) { if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString));
var dbBuilder = new DbContextOptionsBuilder(); dbBuilder.UseSqlite(connectionString);
return new SqliteDataService(new SqliteDbContext(dbBuilder.Options)); } }
7) Пересмотрите еще раз иллюстрации, перечитайте код примеров. Надеюсь вам станет яснее схема взаимодействия с БД. В качестве примера использования привожу пример простого консольного приложения
class Program { static void Main(string[] args) {
string dbPath = @"Filename=D:
epExample.sqlite"; var dbService = SqliteDataService.GetService(dbPath);
//AddReservation(dbService); //AddServicesToReservation(dbService);
ShowReservation(dbService);
Console.ReadKey(); }
private static async void ShowReservation(SqliteDataService dbService) { //находим размещение c его днями и услугами по его id var res = await dbService.Reservations.GetByIdWithDaysAndServices(1);
Console.WriteLine(); Console.WriteLine($"Размещение с {res.StartDate.ToShortDateString()} по {res.EndDate.ToShortDateString()}"); Console.WriteLine(new string('=', 70));
int dayNumber = 1; int servNumber = 1; foreach (var day in res.Days) { Console.WriteLine(); Console.WriteLine($"День {dayNumber} - {day.Date.ToShortDateString()}");
foreach (var service in day.Services) { Console.WriteLine(); Console.WriteLine($"Услуга {servNumber}:{service.Name} за {service.Cost}руб."); servNumber++; }
Console.WriteLine(new string('-', 30)); dayNumber++; servNumber = 1; }
}
private static async void AddReservation(SqliteDataService dbService) { //создаем дни размещения ReservedDay day1 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 20), Cost = 200M }; ReservedDay day2 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 21), Cost = 200M }; ReservedDay day3 = new ReservedDay() { Date = new DateTime(year: 2017, month: 8, day: 22), Cost = 200M };
//размещение и его дни Reservation res = new Reservation() { StartDate = day1.Date, EndDate = day3.Date, Status = 1 }; res.Days.Add(day1); res.Days.Add(day2); res.Days.Add(day3);
//добавляем и сохраняем в БД await dbService.Reservations.Add(res); await dbService.Commit(); }
private static async void AddServicesToReservation(SqliteDataService dbService) { var serv1 = new AdditionService { Name = "Бассейн", Cost = 2M }; var serv2 = new AdditionService { Name = "Парилка", Cost = 1M }; var serv3 = new AdditionService { Name = "Завтрак в номер", Cost = 100M }; var serv4 = new AdditionService { Name = "Чай в номер", Cost = 10M };
//находим размещение по его id var res = await dbService.Reservations.GetByIdWithDays(1);
//выбираем дни по дате var dateDay1 = new DateTime(year: 2017, month: 8, day: 20); var dateDay2 = new DateTime(year: 2017, month: 8, day: 21); var dateDay3 = new DateTime(year: 2017, month: 8, day: 22); var day1 = res.Days.Single(d => d.Date == dateDay1); var day2 = res.Days.Single(d => d.Date == dateDay2); var day3 = res.Days.Single(d => d.Date == dateDay3);
//добавляем услуги в эти дни day1.Services.Add(serv1); day1.Services.Add(serv4);
day2.Services.Add(serv3); day2.Services.Add(serv1); day2.Services.Add(serv2);
day3.Services.Add(serv4);
//запоминаем await dbService.Commit(); } }
Целиком пример можно скачать здесь

Как вывести текст в любую часть экрана?

Необходимо вывести текст в координаты x, y на дисплей,. Не в само окошко программы, а на desktop, любое другое окно других программ по заданным координатам x, y. Использую Qt, пробовал так, но ничего не вышло:
QPainter painter(); painter.setFont(QFont("Times", 25, QFont::Normal)); painter.drawText(0,0, "Draw text ");


Ответ

Я бы вывел с помощью QLabel с прозрачным фоном, навскидку:
QLabel *label = new QLabel(); label->setWindowFlag(Qt::SplashScreen); //убираем рамки label->setAttribute(Qt::WA_TranslucentBackground); //setStyleSheet c помощью него задаем размеры, цвет текста label->setStyleSheet("QLabel { background-color : red; color : blue; font-size:36px}"); QString text = "I am textaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; label->setText(text); //А тут мой минихак с заданием width и height под размер текста label->adjustSize(); int w = label->width(); int h = label->height(); //0, 0 - координаты куда будем текст помещать label->setGeometry(0, 0, w, h); label->adjustSize(); label->show();
Положение задаем с помощью setGeometry, размеры я задал от фонаря, а потом с помощью adjustSize подогнал их под контент

Как из некоторых снимков сделать отдельную ветку?


Есть существующая старая ветка old в которой вся история из результирующих и промежуточных снимков. Необходимо выбрать из неё только результирующие снимки и создать на их основе отдельную «чистую» ветку, например, master Решение не обязательно должно соответствовать рисункам, возможно, есть более грамотный вариант, не знаю. Рис. 1 — старая ветка. Рис. 2 — вариант со слиянием. Рис. 3 — вариант с копированием. Чёрные — результирующие снимки. Белые — промежуточные снимки.


Ответ

Как работать, чтобы такая проблема не возникала?
«Промежуточные» коммиты нужны и полезны. Очень удобно бывает сохраниться где-то посреди разработки: чтобы попробовать другое решение, чтобы перенести работу на другую машину, просто для спокойствия.
Но в итоговой истории они не очень-то интересны. Более того, я лично не хотел бы, чтобы кто-то через месяцы и годы смотрел, как я изобретал велосипеды, писал по строчке в час и делал дурацкие ошибки. Нет, я хочу один финальный коммит, как будто я сел, возложил руки на клавиатуру и создал шедевр. Возможно, вам тоже так будет удобнее.
Что делать-то?
1. Используйте фичеветки (feature branches)
Для работы над каждой задачей делайте отдельную ветку от master. Делайте в ней сколько душе угодно коммитов. Когда результат будет готов — вы сможете собрать все эти коммиты в один или несколько осмысленных (инструкция ниже) и замержить в master
Мержить рекомендую с --no-ff. Так получается более информативная история: видно, где закончились коммиты одной фичи и начались коммиты другой.
Подробнее о фичеветках Про разные модели работы, в том числе с фичеветками.
2. Пользуйтесь commit --amend для промежуточных коммитов
Исправили одну строчку в фичеветке и снова нужно сделать коммит? Проще переписать старый.
git add path/filename git commit --amend --no-edit
Здесь --no-edit означает, что мы не хотим менять сообщение коммита — оставим старое.
Важно: Нужен хотя бы один новый коммит в ветке, который вы будете переписывать. Если вы только что сделали новую ветку от master, пока что нельзя делать --amend. Иначе вы перепишете коммит из master Важно: commit --amend, rebase -i и другие операции по переписыванию истории нельзя выполнять в общих (стабильных) ветках вроде master
Как объединить коммиты по группам в новой ветке?
Предположим, у нас есть такая история коммитов. «Промежуточные» коммиты обозначены как bullshit, а «результативные» — result x. Самый новый коммит — сверху.
git log --oneline --graph --decorate
* 71e8ed5 (HEAD -> oldbranch) result 2 * 0955502 bullshit * cf30c6f bullshit * d590b3d result 1 * 8266a62 bullshit * ce5b35c bullshit
Пересобрать их в более красивую (что очень субъективно) историю нам поможет команда rebase -i
Сначала сделаем новую ветку, в которой будет результат операции.
git checkout -b newbranch
Поскольку здесь мы хотим заребейзить всю ветку целиком, добавляем --root
git rebase -i --root
Если нужно переписать историю только до какого-то коммита (не включая его), указываем хеш этого коммита.
git rebase -i ce5b35c
Открывается редактор с вот таким содержимым. Здесь порядок коммитов обратный: самый новый коммит — снизу
pick ce5b35c bullshit pick 8266a62 bullshit pick d590b3d result 1 pick cf30c6f bullshit pick 0955502 bullshit pick 71e8ed5 result 2
# Rebase 71e8ed5 onto f7569aa (6 commands) ...
Что в этой ситуации можно сделать с коммитами? Нам интересны две команды:
# p, pick = use commit # оставить коммит как есть
# s, squash = use commit, but meld into previous commit # слить содержимое коммита с ПРЕДЫДУЩИМ коммитом
Объединяем (squash) коммиты группами: первый «промежуточный» после каждого результата станет основой нового результата.
pick ce5b35c bullshit s 8266a62 bullshit s d590b3d result 1 pick cf30c6f bullshit s 0955502 bullshit s 71e8ed5 result 2
Когда вы сохраните этот текст и выйдете из редактора, откроется новый, с текстом сообщения для нового коммита result 1, потом для result 2 и так далее — столько раз, сколько коммитов вы хотите в итоге оставить. Первым будет самый старый, последним — самый новый. По умолчанию вам предлагается вот такой шаблон:
# This is a combination of 3 commits. # This is the 1st commit message: bullshit
# This is the commit message #2:
bullshit
# This is the commit message #3:
result 1
# Please enter the commit message for your changes. Lines starting
Когда вы отредактируете и сохраните все новые сообщения, rebase -i завершится. Получится примерно такая история:
* 588a7ac (HEAD -> newbranch) result 2 * 2713fda result 1

SVG анимация графика

Добрый день, подскажите как сделать так что бы "ползунок" не "дрифтовал" по графику, а плавно перетекал, как капля, и повторял изгибы графика?
Process Created using Figma


Ответ

если использовать rect, то как вариант решения, можно так
Process Created using Figma

RTTI. Как обратить к статическому полю класса, имея объект Class для него?

Имеем класс Allocator, который имеет метод allocate(Class). Как без знания того, объект Class какого класса пришёл, вытащить из него поле с возможностью его изменить?
Вариант с использованием objOFclassClass.newInstance() не подойдёт. Охота без лишних затрат памяти на создание объекта обойтись.
class X { public static boolean f = false; }
class Y { public static boolean f = false; }
class Z { public static boolean f = false; }
class Allocator { public static void allocate (Class c) throws Exception{ // Предполагая, что (c.name == "X" или "Y" или "Z"), как сделать что то в духе: // c.setStaticField("f",true); ??? } }


Ответ

С помощью метода getDeclaredField получаем объект класса Field Изменяем поле с помощью вызова метода set у этого объекта

Field field = c.getDeclaredField("имя_поля"); field.set(null, true /* новое значение */);
Код на Ideone

Как изменять line-height в зависимости от того, вмещается ли контент в блок

Ребята ситуация такова:
.mytext { width: 120px; padding: 0px 10px; text-align: center; line-height: 30px; height: 30px; border: 1px #c6c6c6 solid; } .mytext2 { margin-top:10px; width: 120px; padding: 0px 10px; text-align: center; line-height: 30px; height: 30px; border: 1px #c6c6c6 solid; }

Тест
Тестовая информация

Мне необходимо, что бы в зависимости от текста который вставляется в блок изменялся параметр line-height. Более чем 2 строки текста никогда не бывает, поэтому, если текст не поместился в одну строку нужно применить line-height:18px. Как это можно сделать с помощью css? Или тут прибегать нужно к js?


Ответ

Чтобы выровнить положение текста по вертикали, необязательно менять line-height. Достаточно обернуть текст внутри блоков в другой элемент, например, p, и этому p задать display:inline-block; (и vertical-align:middle; при необходимости) и задать ему line-height сразу с учетом того, что текста будет на 2 строки:
.mytext { width: 120px; padding: 0px 10px; text-align: center; line-height: 30px; height: 30px; border: 1px #c6c6c6 solid; } .mytext2 { margin-top: 10px; } .mytext>p { display: inline-block; line-height: 15px; margin: 0px; padding: 0px; }

Тест

Тестовая информация


Math.random, Javascript. Что-то пошло не так [дубликат]

На данный вопрос уже ответили: Как вернуть значение из события или из функции обратного вызова? Или хотя бы дождаться их окончания 3 ответа
var GoodObj = {};
$(document).ready(function() { var a = Math.floor(Math.random() * 50); var b = Math.floor(Math.random() * 50); var trololo = parseInt(a + b); GoodObj.lala = trololo; document.getElementById('number').innerHTML += a; document.getElementById('number').innerHTML += '+'; document.getElementById('number').innerHTML += b; });
function myFunction (){ var myTrololo = GoodObj.lala; var stop = false; if (document.getElementById('timer').innerHTML == myTrololo) { alert('right'); return true; } else { alert('false'); return stop; } }
function randomInt(min,max){ return Math.floor(Math.random() * (max - min) + min); } var param = randomInt(14, GoodObj.lala); alert(randomInt(14, GoodObj.lala));
Здесь GoodObj.lala - сума двух рандомных чисел. Не думаю, что тот кусок кода нужен. В общем, выводит Nan. В других случаях перемещения переменной или еще чего - что код слишком рекурсивен. Уже совсем запуталась


Ответ

var GoodObj = {}; $(document).ready(function() { var a = Math.floor(Math.random() * 50); var b = Math.floor(Math.random() * 50); var trololo = parseInt(a + b); GoodObj.lala = trololo; document.getElementById('number').innerHTML += a; document.getElementById('number').innerHTML += '+'; document.getElementById('number').innerHTML += b; var param = randomInt(14, GoodObj.lala); console.log(randomInt(14, GoodObj.lala)); }); function randomInt(min, max) { return Math.floor(Math.random() * (max - min) + min); } // этот кусок кода выполняется сразу, тогда, когда инициализация // var trololo = parseInt(a + b); // GoodObj.lala = trololo; // только после загрузки всего ДОМ дерева // в итоге GoodObj.lala попросту не существует и ровняется undefined что и дает нам NaN // просто перенесите в $(document).ready //var param = randomInt(14, GoodObj.lala); //console.log(randomInt(14, GoodObj.lala));


Разница между датами в днях

Вообщем, ввожу данные 1-й даты и 2-й даты и пытаюсь найти разницу между ними в днях, решил для начала найти разницу в днях в месяцах и годах, месяцы и годы перевожу в дни и все суммирую, но кол-во месяцев не подсчитывает
#include #include using namespace std; int main() { int year_user, mon_user, day_user; int current_year, current_mon, current_day; int num_days, num_mon=0, num_years; int leap_year = 0, leap_like = 0; int m, m1, i, j; int M[12] = {31,28,31,30,31,30,31,31,30,31,30,31}; cout<<"Введите год месяц и день вашего дня рождения(в формате ГГГГ М Д):"<>year_user>>mon_user>>day_user; cout<<"Ваша дата рождения:"<>current_year>>current_mon>>current_day; cout<<"Желаемая дата:"< if (mon_user >= current_mon) { int m = mon_user; int m1 = current_mon; } else { int m = current_mon; int m1 = mon_user; }
//рассчитываем количество лишних месяцев for(i=m; icout<<"Количество дней между месяцами:"<//рассчитываем количество лет между датами и проверяем высокосный ли он
for(j=year_user; j } else { num_years += 365; } } cout<<"Количество дней между годами:"<

Ответ

Я бы не мучил эти дни, месяцы и годы... Есть такое хорошее понятие как юлианская дата с хорошо разработанным алгоритмом расчета, и ваша задача - просто вычисление разности двух таких дат.
Если вы не планируете работать с датами до введения григорианского календаря и учитывать введение в России григорианского в 1918 году :), то вам достаточно запрограммировать это:
Как видите, даже ни одного условного перехода :)

SSMS V17.2 отключение комментария при генерации скриптов

Прошу помощи сообщества в следующем вопросе: как отключить вставку комментария вида:
/* ==Scripting Parameters==
Source Server Version : SQL Server 2014 (12.0.5511) Source Database Engine Edition : Microsoft SQL Server Enterprise Edition Source Database Engine Type : Standalone SQL Server
Target Server Version : SQL Server 2014 Target Database Engine Edition : Microsoft SQL Server Enterprise Edition Target Database Engine Type : Standalone SQL Server */
при генерации скриптов в SQL Server Management Studio V17.2? Например при генерации Alter для View очень напрягает удалять этот блок постоянно.

UPD: 09 октября 2017 года вышла новая версия SSMS - 17.3 Заголовок ==Scripting Parameters== теперь не добавляется. За добавление заголовка в новой версии отвечает опция Include scripting parameters header по умолчанию опция установлена в False


Ответ

На данный момент нельзя убрать, но в следующем релизе SMO/SSMS, по умолчанию это будет отключено (https://connect.microsoft.com/SQLServer/feedback/details/3139328/).

Массив без выхода за пределы C++

Мне нужно массив или вектор без выхода за пределы. Что это такое? допустим Создаю вектор или массив на 10 символов типа int Все 10 элементов равны 10 когда беру a[0],a[4],a[9] == 10; но как сделать так что бы
a[10 (это 0) ] == 10 a[12 (это 2) ] == 10
желательно ещё для отрицательных чисел


Ответ

Если вопрос в том, как получить индекс, не выходящий за пределы массива при отрицательных индексах, то можно индекс массива вычислять так:
i < 0 ? -i%10 : i % 10
Например:
#include using namespace std;

int main() { int a[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i=-12; i<12; ++i){ cout << "a[" << i << "] = " << a[i < 0 ? -i%10 : i % 10] << endl; } return 0; }
На выходе будет:
a[-12] = 3 a[-11] = 2 a[-10] = 1 a[-9] = 10 a[-8] = 9 a[-7] = 8 a[-6] = 7 a[-5] = 6 a[-4] = 5 a[-3] = 4 a[-2] = 3 a[-1] = 2 a[0] = 1 a[1] = 2 a[2] = 3 a[3] = 4 a[4] = 5 a[5] = 6 a[6] = 7 a[7] = 8 a[8] = 9 a[9] = 10 a[10] = 1 a[11] = 2

Как в gradle скрипте Java проекта настроить создание константы, имеющей разные значения для разных ОС

Пишу сервлет на винде, собираю проект через Gradle. Запускать надо будет на Debian. В проекте используется путь до определённого файла в ОС (драйвер Chrome для Selenium). На винде и Debian он разный. Как я могу настроить варианты сборки для разных ОС так, чтобы в них была в Java коде доступна константа, заданная в скрипте сборки и имеющая разные значения для вариантов сборки?
В андроид это сделать легко, а вот тут сходу не сообразить. Рассмотрю вариант однозначного программного определения ОС, на коей идёт исполнение и подставление пути до файла в рантайме.


Ответ

Рассмотрю вариант однозначного программного определения ОС, на коей идёт исполнение и подставление пути до файла в рантайме.
Насколько я понимаю для Ваших целей достаточного грубого определения ОС: Windows и не-Windows. Тогда подойдет такой метод:
boolean isWindows() { return System.getProperty("os.name").contains("Windows"); }
Метод можно использовать при инициализации пути, например:
//константы private static final String WINDOWS_PATH = "C:\\driver\\chrome.exe"; private static final String UNIX_PATH = "/usr/bin/chromedriver"; ... //использование pathToExecutable = isWindows() ? WINDOWS_PATH : UNIX_PATH;
С помощью Gradle можно записывать путь, например, в файл properties, после чего код будет считывать его в рантайме.

Возможно ли такое расположение в панели?

Есть форма, под ней кнопки (ОК, Отмена). Хочу добиться следующего поведения: если форма маленькая и умещается в родительской панели, кнопки должны быть сразу после формы. Если форма большая и не умещается в родительской панели, появляется ScrollBar только на форму, кнопки видны вне формы внизу. Привожу иллюстрацию:

Если делать Grid со строками размера * и Auto, кнопки будут всегда внизу, а хочется чтобы они были сразу под содержимым. Если делать StackPanel, ScrollBar вообще не появляется. Если делать DockPanel, а элементам задать DockPanel.Top, поведение аналогично StackPanel
Есть ли вообще варианты сделать такое?

Тестовый пример:


Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

Автоматически перезагружать файлы при изменении их извне. Visual studio 2017

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


Ответ

Зайдите в меню Tools > Options, в разделе Environment > Documents поставьте галку возле пункта Reload modified files unless there are unsaved changes. При первом изменении студия все равно отобразит диалог, но там будет дополнительная опция.

Сокет клиент - сервер

Здравствуйте. Вопрос возможно глупый. Начал вникать в создание сокет клиента под андроид. Нашел статьи где описывается процесс отправки сообщения с телефона на сервер. Можно ли сделать что бы в качестве сервера выступало другое андроид устройство? Подскажите где можно почитать по этому поводу.


Ответ

Плохая идея. Во-первых, в общем случае мобилка имеет или приватный IP-адрес за NAT'ом или динамический адрес. В первом случае шанса подключиться к серверу нет вообще, а во втором нужен dynamic dns, чтобы клиент знал куда подключаться. Во-вторых, постоянно запущенное приложение ожидающее соединений будет высаживать батарею с неприятно большой скоростью. Наконец, это не безопасно.

Нейронные сети - почему веса синапсов случайны при инициализации системы?

Пытаюсь хотя-бы приблизительно понять нейронные сети, раз они стали настолько модными, что даже на фрилансе их запрашивают. Читаю эту серию: https://habrahabr.ru/post/312450/
"Важно помнить, что во время инициализации нейронной сети, веса расставляются в случайном порядке."
Почему? Для автора статьи это, может, очевидно. Для меня нет. Разве не может все обучение и работа пойти крахом, если программа изначально расставит неправильный вес?


Ответ

Цитата из Нейронные сети для обработки информации
3.9 методы инициализации весов ... На результаты обучения огромное влияние оказывает подбор начальных весов сети. Идеальными считаются начальные значения, достаточно близкие к оптимальным. При этом удается не только устранить задержки в точках локальных минимумов, но и значительно ускорить процесс обучения. К сожалению, не существует универсального метода подбора весов, который бы гарантировал нахождение наилучшей начальной точки для любой решаемой задачи. По этой причине в большинстве практических реализаций чаще всего применяется случайный подбор весов с равномерным распределением значений в заданном интервале. ...

Изменение картинки при наведении WPF

Приветствую!
Есть WPF приложение, на одной из форм которого расположена картинка:

Вопрос - как наиболее простым способом реализовать поведение, при котором если курсор наведен на картинку то Source картинки изменится на "pack://application:,,,/Resources/reminder2.png"а при отведении обратно ?
Можно написать тригеры, добавить в вьюмодели свойство string Patch= "pack://..."; затем вызвать NotifyPropertyChanged... Но наверняка это можно реализовать гораздо проще. Если можно, то как? Ограничиваясь только средствами разметки (+ расширения XAML) такое воплотить ?
UPD Код :

Пути до файлов прописаны верно, однако ничего не происходит. В чем ошибка ?


Ответ

Разобрался. Если кому понадобится менять картинку при наведении в WPF реализовать это можно так :

Как получить отображение вставленных данных(insert) на исходную таблицу?

Есть 2 таблицы (гипотетические)
TableSource(id int IDENTITY(1,1), value int, order int)
TableTarget(id int IDENTITY(1,1), value)
И операция вставки данных из первой во вторую
INSERT TableTarget(value) SELECT s.value FROM TableSource s WHERE order < 10
Мне нужно как-то получить отображение id записей из TableSource на новые вставленные строки в TableTarget.
IdTableSource | idTableTarget ----------------------------- 2 | 1 4 | 2 5 | 3 7 | 4 9 | 5
Возможно есть какой то способ добавить эти данные в OUTPUT, но не нашёл как.


Ответ

способ добавить эти данные в OUTPUT
INSERT не позволяет направлять в OUTPUT любые столбцы из источника (лишь те, которые доступны в inserted). Это возможно только помощью MERGE
MERGE INTO TableTarget T USING ( SELECT id, value FROM TableSource WHERE [order] < 10 ) S ON 1 = 0 WHEN NOT MATCHED THEN INSERT (value) VALUES (S.value) OUTPUT S.id AS idSource, inserted.id AS idTarget;

Использование неопределенного типа

Пишу абстрактный класс символьного дифференцирования и возникла проблема с объявлением классов sin и cos, если сначала объявляю cos, то не могу использовать sin в методе diff, если сначала объявляю sin, то не могу использовать cos. Ошибка: использование неопределенного типа "Sin" Как решить эту проблему?
class Cos : public Expression { private: Expression *d1; public: friend class Sin; Cos(Expression *L1) : d1(L1) {} Expression *diff() { return new Mul(new Sin(d1), d1->diff()); } void print(){} }; class Sin : public Expression { private: Expression *d1; public: friend class Cos; Sin(Expression *L1): d1(L1) {} Expression *diff() { return new Mul(new Cos(d1), d1->diff()); } void print(){} };


Ответ

В определении функций diff требуется завершенный класс, раз уж вы используете его конструктор. Так что вы должны в объявлениях классов только объявить функции-члены diff, а вот определять их (писать тело) - вне классов.
Типа
class Cos : public Expression { private: Expression *d1; public: friend class Sin; Cos(Expression *L1) : d1(L1) {} Expression *diff(); void print(){} };
Потом - такой же Sin, а уж только потом, вне классов, писать
Expression *Cos::diff() { return new Mul(new Sin(d1), d1->diff()); }

Преобразование JSON в POJO начиная с определенного уровня вложенности

Есть система, отдающая по REST API здоровенный JSON. На нее я воздействовать не могу ни коем образом.
Из этого огромного JSON-а мне нужна только часть по определенному ключу.
Допустим из этого:
{ "a":{ "b":[.....] }, "c":{.....}, "r":{ "d":{ "e":{ "f":[....], "g":[....] } }, "m":{......} ..... "z":{....} }
Мне нужно сделать POJO структуру начиная с r.d.e
Для того же GSON'а придется создавать как минимум всю ветку, что совершенно излишне (или я заблуждаюсь?)
Есть ли какие библиотеки, которые можно использовать совместно с okhttp3 и которые позволяют организовать такое?


Ответ

Можно вручную разбирать весь JSON пока не дойдёте до нужного узла и только его парсить в объект. Типа как-то так
JsonParser parser = new JsonParser(); JsonObject obj = parser.parse(HUGE_JSON_STRING).getAsJsonObject(); String stringToParseNoramlly = obj.get("r").get("d").get("e").getAsString(); EModel eModel = new Gson().fromJson(stringToParseNoramlly, EModel.class);

Преобразовать json в формат

Имеется json
{ "Car": { "Pos": ["id", "mark"], "Bat": [ [1, "bmw"] ] }, "Temp": { "Pos": ["min", "hig", "mid"], "Bat": [[1, 2, 3] } }
Как его преобразовать в формат
{ "Car":{ "id": 1, "mark": bmw }, "Temp": { "min": 1, "hig": 2, "mid": 3 } }


Ответ

Вот такой вариант
const src = { "Car": { "Pos": ["id", "mark"], "Bat": [ [1, "bmw"], [2, "audi"], [3, "mercedes"] ] }, "Temp": { "Pos": ["min", "hig", "mid"], "Bat": [[1, 2, 3], [4, 5, 6], [7, 8, 9]] } }; const res = {}; for(const k in src){ const cnt = src[k].Pos.length; for(let i=0; i

Когда нужно вызывать GetExitCodeProcess

Функцию GetExitCodeProcess нужно вызывать до, или после CloseHandle?
if (CreateProcess(L"C:\\Windows\\System32\\schtasks.exe", ptr, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) { WaitForSingleObject(pi.hProcess, INFINITE); DWORD lp; if (GetExitCodeProcess(pi.hProcess, (&lp))) qDebug() << parseError(&lp); else qDebug() << "ошибка EXITA"; CloseHandle(pi.hProcess); CloseHandle(&si); } else qDebug() << GetLastError();


Ответ

После CloseHandle хэндл становится невалидным и с ним уже никакую функцию вызывать не надо.

Прохождение бинарного дерева (post-order)

Здравствуйте, как на с/с++ написать нерекурсивный обход бинарного дерева через стек методом post-order (сначала листья, потом корень)?


Ответ

Т.к. рекурсию использовать нельзя, мы вместо неё будем использовать два стэка. Вот весь алгоритм, в результате его будут распечатаны все вершины бинарного дерева в post-order порядке (можно вместо распечатки делать какое-то полезное действие):
Добавить корень в первый стэк. Пока первый стэк не пуст выполнять следующие два подпункта:
2.1. Извлечь узел из вершины первого стэка, добавить его во второй стэк.
2.2. Добавить левого затем правого ребёнка узла из п. 2.1 в первый стэк. Распечатать содержимое второго стэка, т.е. в цикле извлекать вершину второго стэка и печатать.
Замечание: в конце порядок распечатки для поддеревьев будет тот же что и при помещении в 1й стэк, т.е. мы помещали в 1й стэк левого а затем правого ребёнка, при распечатке в итоге будет раньше распечатано левое поддерево, затем правое, а затем родитель. Если нужно в распечатке иметь вначале правое поддерево а затем левое тогда нужно в 1й стэк помещать вначале правого ребёнка затем левого.

Алгоритм выделения блока кода из позиции курсора?

Допустим, есть код следующий | - каретка
function a () { let b = 20; | }
В таком случае легко написать алгоритм выделения блока { let b = 20 } просто пройти сначала направо или налево, найти положение открывающей/закрывающей скобки, сделать counter равный 1 и потом пойти в обратную сторону прибавляя или уменьшая счетчик в зависимости от того, какая скобка попадается в очередной раз и когда он будет 0 означает что блок закрылся.
Это так же сработает и для { | {} {} {} }
Но это не будет работать для { {} | {} }, как быть в этом случае? Есть ли вообще решение без разбиения всего кода на токены, выделение блоков, ast и прочее? Ну и как вообще лучше данную задачу решить?


Ответ

Ну почему же не сработает?
Открывающая скобка - +1, закрывающая - -1. Идем в вашем случае влево, получается последовательность (пишу значение после обработки каждой скобки по пути) -1, 0, +1. Все, стоп! +1 - найдена искомая открывающая скобка.
Идем вправо, имея +1. Далее - +2, +1, +2, +1, 0. Стоп! 0 - найдена закрывающая скобка.
Не сработает при наличии скобок в комментариях или строковых литералах, например. Тут нужно дополнительно разбираться...