Pull to refresh

Comments 23

Удивительно, что вы не начали с варианта хранения сразу всех заготовок мышц покрашенных во все возможные цвета.
Тоже сразу об этом подумал
Удивительно, но это не рационально :)
Во-первых, сейчас заготовок: 31 мышца Х 2 пола = 62 файла. Если сохранять все перекрашенные варианты — это х10 цветов, или 620 файлов. Текущий объем файлов занимает 766КБ (уже кропнутых). По вашему варианту объем отдельных файлов составит более 7МБ – это роскошь для мобайла.
Во-вторых, декодирование ресурсов требует в 3-6 раз больше времени, чем перекрашивание. Специально для вас замерил:
decodeResource> 83 ms
Paint> 18 ms
decodeResource> 85 ms
Paint> 16 ms
decodeResource> 137 ms
Paint> 26 ms

В-третьих, как следствие второго, вы не сможете переиспользовать ресурсы, уже декодированные.
А что если использовать для этих целей SVG? Можно ли в андроиде изменить цвет отдельной ноды в SVG? Чтобы не нужно было хранить кучу больших PNG и отрисовать сразу в один проход. Если нет, то можно разбить на кучу маленьких SVG, менять им tint color в рантайме и отрисовывать — даже так должно быть эффективнее.
Ох, как же я забыл об этом написать, это был первый вариант. Все мышцы хранились в VectorDrawable (тот же SVG, используется в Android). К сожалению, программно заменить цвет заливки нельзя (либо я не нашел таких методов). Но самый большой минус – векторные изображения сначала конвертируются в растр нужного размера, а затем уже их можно полноценно использовать, а это дополнительная память и время.
Поэтому от использования вектора очень быстро отказался, но можно было бы озаглавить пост «Ускорил в 100 раз!», наверное :)
В дополнение, существует LayerDrawable, который позволяет накладывать массив Drawable друг на друга. Удобно, но ресурсоёмко.
Скорее всего Вы не нашли как.
Даже на уровне кривого хака, от человека который ничего не смыслит в андроиде, там наверняка есть файловые и строковый операции. Которые позволяют сделать следующее:

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

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

Профит.

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

Еще можно попробовать сделать Custom View перенеся все фигуры в кривые через Path.
Раньше писал свой плагин для Sketch, но он работает теперь только со старыми версиями. Сейчас PaintCode позволяет это делать за тебя. Можно реализовать определения кликов через класс Region и сделать вполне себе интерактивную карту тела, меняя цвета в рантайме через перерисовку.


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

Если даже с SVG возникнут какие-то сложности, в данном случае будет достаточно отрисовки через Canvas.drawPath.
Я разработкой под Android не занимался, но неужели не существует простого 2D движка с рендером на GPU с поддержкой SVG без предварительной растеризации в текстуру? Батарейку сильно жрать не должно, так как перерисовку не нужно будет выполнять постоянно как в играх.

С ходу напрашивается


  1. Сделать один нормальный, грейскейленый атлас со всеми группами мышц (не нужно ни каких основ)
  2. Один раз построить карту с положением группы на теле человека (x, y)
  3. Чтобы отобразить нужное изображение (тело человека спереди или сзади) просто последовательно отрисовываем нужные элементы из атласа в позиции соответствующей карте из пункта 2. Для интенсивности вешаем на отрисовку нужного элемента простой шейдер, который раскрасит его в заданный цвет.
  4. Это смело можно делать в рантайме, нужно загрузить только один (или два в зависимости от ограничений на текстуры) атлас. Таким образом отображаем любую картинку, которая будет соответствовать данным — карта с название группы мышц и степенью нагрузки. А хранить исключительно данные тренировки
1. Так и есть. В оптимизированном варианте изображение основы включает все кости и мышцы. Накладываются только участвовавшие мышцы.
2. Так и есть. В каждой группе мышц захардкодены ее координаты на основе (общей карте).
3. Так и есть. Отрисовываются только нужные элементы, но методом
resultCanvas.drawBitmap(bitmapDst, x, y, paint); 

О шейдерах ничего не могу сказать, не знаком, посмотрю обязательно, спасибо!
Причем можно хранить только левые (или правые мышцы), а недостающие зеркалить на лету.
Почему нельзя просто хранить карту мышц, а пользователю генерировать новую картинку расскрашивая нужные мышцы?

Генерирование изображений действительно было необходимо?
Почему бы не использовать изображение с индексированным цветом (PNG8), на изображении фигура целиком, для каждой группы мышц — отдельный цвет и отрисовывать с нужной на данный момент палитрой?


На основе данных завершенных тренировок строится HashMap<String, Float>, String – мышца, Float – степень нагрузки от 0 до 10.

Зачем String, ведь есть же enum?

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

UFO just landed and posted this here

Я не про то, никакого наложения вообще.
Рисуем целую фигуру человека, со всеми группами мышц, как на заглавной картинке в статье. Каждую группу мышц рисуем своим цветом. Полученное изображение сохраняем как PNG с палитрой и кладём в ресурсы приложения. 256 цветов должно хватить за глаза.


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


String также необходим для поиска, хранения в бд, статистики и тд.

Элементам enum-a можно добавить дополнительные аттрибуты:


public enum SomeEnum {

    FOO(123, "Alpha"),
    BAR(345, "Beta"),
    BAZ(567, "Gamma");

    private int id;

    private String description;

    SomeEnum(int id, String description) {
        this.id = id;
        this.description = description;
    }

    public int getId() {
        return this.id;
    }

    public String getDescription() {
        return this.description;
    }

}



public class Test {

    public static void main(String... args) {
        for(SomeEnum value : SomeEnum.class.getEnumConstants()) {
            System.out.println();
            System.out.println("Enum Name:        " +  value.name());
            System.out.println("Enum Id:          " +  value.getId());
            System.out.println("Enum Description: " +  value.getDescription());
        }
    }

}
— Ты чего такой грустный?
— Да вот начальство заставляет поднять цену на товары для постоянных клиентов на 20%. Неудобно как-то…
— Ой, фигня вопрос! Объявляешь поднятие цены на 100%, а для постоянных клиентов — скидка 80%

Как-то так.
(как я накосячил и тормознул свое приложение в 50 раз — а потом обнаружил косяки и частично исправил)

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

Обсуждаемый вверху вариант с использованием VectorDrawable имеет место быть. Есть несколько опен соурс решений по смене цвета «path» или «group» в конечном файле xml (vector drawable). Думаю стоит присмотреться, если минимальная версия sdk позволяет (>21).
Пример нескольких библиотек:
github.com/harjot-oberai/VectorMaster
github.com/devendroid/VectorChildFinder
Есть либа AndroidSVG, которая напрямую отрисовывает вектор на канве без конвертации в битмап.
Sign up to leave a comment.

Articles