Рисование огромных объемов данных в NSView или что-то еще? - PullRequest
4 голосов
/ 29 января 2009

У меня есть сотни тысяч точек данных временных рядов, которые я хочу представить пользователю. Мое текущее решение состоит в том, чтобы отобразить указанные данные в PNG с помощью сторонней библиотеки, а затем загрузить этот PNG в NSImage и отобразить его в виде прокрутки. Это прекрасно работает, за исключением того, что:

  1. NSImages шириной более 32k пикселей не отображаются должным образом
  2. Я хочу иметь возможность быстро и легко увеличить данные
  3. Чтение и запись с диска глупо

Моя текущая попытка - нарисовать NSBezierPath s до NSView. Представление выглядит красиво, но очень, очень медленно, даже если я рисую только ограниченное подмножество точек за раз. И каждый раз, когда я прокручиваю, мне приходится перерисовывать, что тоже медленно.

Я уверен, как относительный новичок в Какао, что мне не хватает некоторых лучших способов сделать это. Какой «правильный» способ сделать это?

Ответы [ 6 ]

6 голосов
/ 30 января 2009

Моя текущая попытка состоит в том, чтобы напрямую нарисовать NSBezierPaths в NSView. Представление выглядит красиво, но очень, очень медленно, даже если я рисую только ограниченное подмножество точек за раз. И каждый раз, когда я прокручиваю, мне приходится перерисовывать, что тоже медленно.

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

  1. Clip. Используйте NSRectClip, передавая прямоугольник, который вы берете в качестве аргумента, drawRect:. Это однострочник.
  2. Выполните простое тестирование прямоугольника перед заполнением путей. Для каждого пути получите его границы и используйте NSIntersectsRect, чтобы проверить, находится ли он внутри прямоугольника.
  3. Выполните чуть-чуть менее простое тестирование прямоугольника перед штриховкой. Аналогично предыдущему шагу, за исключением того, что вам нужно увеличить прямоугольник (тот, который вы получили в качестве аргумента) по ширине линии, поскольку половина хода выпадет за пределы дорожки. Для каждого пути получите ширину линии и отрицайте ее (delta = -[path lineWidth] - и да, вы должны включить этот знак минуса), а затем передать результат в качестве обоих аргументов NSInsetRect. Обязательно сохраняйте исходный прямоугольник, так как разные пути могут иметь разную ширину линии.

Идея в том, чтобы просто рисовать меньше. Установка обтравочного контура (используя NSRectClip) уменьшит количество бликов, возникающих в результате операций рисования. Исключение путей, которые полностью выходят за пределы прямоугольника рисования, сэкономит вам, вероятно, более дорогое ограничение для этих путей.

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

Еще одна вещь: получение границ для каждого пути может быть дорогим. (Опять же, профиль.) Если это так, вы можете кэшировать границы для каждого пути, возможно, используя параллельные NSArray из NSValue s или объединяя путь и границы в объекте вашего собственного изобретения. Затем вы вычисляете границы только один раз и просто извлекаете их при будущих запусках чертежей.

2 голосов
/ 31 января 2009

Насколько динамичен набор данных?

Если он достаточно статичен и вы хотите «увеличить», то вам следует рассмотреть возможность объединения данных в масштабированное подмножество.

Придуманный пример: если у вас есть 100 000 точек (скажем, продвижение по оси X), то, очевидно, на изображении с 1000 пикселями вы неизбежно будете чертить только 1000 пунктов из этих 100 000.

Таким образом, вы можете сделать это в расширенном режиме (1000 пунктов, 10000 пунктов, необработанные данные 100000 пунктов) и выбрать из соответствующего набора на основе отображаемого уровня «увеличения».

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

2 голосов
/ 31 января 2009

Я бы посмотрел на CATiledLayer (только для леопардов) для этого. Вы можете нарисовать огромное количество материала в плиточном слое и отображать области по мере необходимости. В вашем случае вы можете установить CATiledLayer в качестве основы для вашего NSView, нарисовать все ваши пути Безье в этом слое, а затем прокручивать и даже увеличивать и уменьшать его. Слои Core Animation обрабатываются как текстуры OpenGL, поэтому вы должны получить очень хорошую производительность. Векторный рисунок кэшируется в слое и не перерисовывается при прокрутке, как в стандартном NSView.

Например, Билл Дадни опубликовал пример кода о том, как использовать CATiledLayer для отображения массивного файла PDF.

2 голосов
/ 30 января 2009

Если вы рисуете так много более простых путей, вероятно, OpenGL - это то, что вам нужно. Документация Apple будет хорошим началом. У них есть пример кода для реализации базового контекста рендеринга OpenGL в окне какао.

Это перенесет бремя сложных задач рисования на графический процессор и оставит ваше приложение свободным для прокрутки по желанию.

Эта страница содержит пример кода OpenGL для рисования кривых Безье.

2 голосов
/ 30 января 2009

В документах есть раздел о производительности NSBezierPath .

0 голосов
/ 01 октября 2012

Рассмотрите возможность использования инфраструктуры, которая эффективно обрабатывает такие вещи, как CorePlot

...