Разделение диаграмм Ганта на порции

Иногда может возникнуть необходимость разделения диаграммы Ганта на порции. Например, это может оказаться полезным для подготовки диаграммы для печати. В диаграмме содержится слишком много данных и она отображается с вертикальной и/или горизонтальной прокруткой. Если такую диаграмму вывести на печать напрямую, будет отображена только первая "порция" данных.

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

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

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

Ниже приведен пример подобного алгоритма. Следует обратить внимание на несколько моментов.
Определение количества строк, которые можно вывести в одной порции. В данном примере подразумевается, что мы знаем используемый шрифт диаграммы, знаем высоту порции, поскольку она берется из макета, и можем заранее определить (подобрать) количество строк как константу. При необходимости можно написать более "интеллектуальный" код, который будет динамически расчитывать это число.

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

Данный пример оформлен как модуль обработки. Нужно заполнить экспортируемую переменную ИсходнаяДГ и вызвать функцию ПодготовитьПечатьДиаграммыГанта().

Копировать в буфер обмена
Перем ИсходнаяДГ Экспорт;     // Исходная диаграмма Ганта.
Перем Макет;                  // Макет, используемый для формирования.
Перем РазмерПорцииСтрок;      // Количество отображаемых строк.
Перем ВыводимыйДокумент;      // Формируемый табличный документ.
Перем ТекущееКоличествоСтрок; // Количество выведенных строк в текущую диаграмму.
Перем ТекущаяДиаграмма;       // Текущая заполняемая диаграмма.
Перем ТекущаяСерия;           // Серия заполняемой диаграммы.
Перем ТекущаяКолекцияТочек;   // Текущая колекция точек заполняемой диаграммы.
Перем ПерваяСтраница;         // Флаг первой выводимой страницы.

Процедура ПодготовитьПечатьДиаграммыГанта() Экспорт
	Макет = ПолучитьМакет("Макет");
	ВыводимыйДокумент = Новый ТабличныйДокумент;
	РазмерПорцииСтрок = ВычислитьКоличествоОтображаемыхСтрок();
	ПерваяСтраница = Истина;
 
	Если ИсходнаяДГ.Точки.Количество() > 0 Тогда
		ПерваяТочка = ИсходнаяДГ.Точки[0];
	КонецЕсли;  
 
	НоваяДиаграмма(ПерваяТочка);
	ВывестиТочки(ИсходнаяДГ.Точки);
 
	Для Каждого Рисунок Из ВыводимыйДокумент.Рисунки Цикл
		Рисунок.Объект.ПоказатьУровеньТочек(0);
	КонецЦикла;  
 
	ВыводимыйДокумент.Показать();
КонецПроцедуры 

Функция ВычислитьКоличествоОтображаемыхСтрок()
	Возврат 21; // Заранее подобранная константа.
КонецФункции   

Процедура НоваяДиаграмма(СледующаяТочка)
	Если ПерваяСтраница Тогда
		ПерваяСтраница = Ложь;
	Иначе 
		ВыводимыйДокумент.ВывестиГоризонтальныйРазделительСтраниц();
	КонецЕсли;  
 
	Область = Макет.ПолучитьОбласть("ОбластьДиаграммы");
	ВыводимыйДокумент.Вывести(Область);
	ТекущаяДиаграмма = ВыводимыйДокумент.Рисунки[ВыводимыйДокумент.Рисунки.Количество() - 1].Объект;
 
	ТекущаяДиаграмма.АвтоОпределениеПолногоИнтервала = Ложь;
	ТекущаяДиаграмма.УстановитьПолныйИнтервал(ИсходнаяДГ.НачалоПолногоИнтервала, 
                                                  ИсходнаяДГ.КонецПолногоИнтервала);
             
	ИсходнаяШкала = ИсходнаяДГ.ОбластьПостроения.ШкалаВремени;             
	ТекущаяШкала = ТекущаяДиаграмма.ОбластьПостроения.ШкалаВремени;
	ТекущаяШкала.Положение = ИсходнаяШкала.Положение;
	ТекущаяШкала.ПрозрачныйФон = ИсходнаяШкала.ПрозрачныйФон;
	ТекущаяШкала.ЦветТекста = ИсходнаяШкала.ЦветТекста;
	ТекущаяШкала.ЦветФона = ИсходнаяШкала.ЦветФона;
 
	Для Индекс = 0 По ИсходнаяШкала.Элементы.Количество() - 1 Цикл
		ИсходныйЭлемент = ИсходнаяШкала.Элементы[Индекс];
  
		Если Индекс = 0 Тогда
			ТекущийЭлемент = ТекущаяШкала.Элементы[0];
		Иначе
			ТекущийЭлемент = ТекущаяШкала.Элементы.Добавить();
		КонецЕсли;   
  
		ТекущийЭлемент.Видимость = ИсходныйЭлемент.Видимость;
		ТекущийЭлемент.Единица = ИсходныйЭлемент.Единица;
		ТекущийЭлемент.Кратность = ИсходныйЭлемент.Кратность;
		ТекущийЭлемент.ЛинииДелений = ИсходныйЭлемент.ЛинииДелений;
		ТекущийЭлемент.ОтображатьПериодическиеМетки = ИсходныйЭлемент.ОтображатьПериодическиеМетки;
		ТекущийЭлемент.Формат = ИсходныйЭлемент.Формат;
		ТекущийЭлемент.ФорматДня = ИсходныйЭлемент.ФорматДня;
		ТекущийЭлемент.ЦветЛиний = ИсходныйЭлемент.ЦветЛиний;
		ТекущийЭлемент.ЦветТекста = ИсходныйЭлемент.ЦветТекста;
		ТекущийЭлемент.ЦветФона = ИсходныйЭлемент.ЦветФона;
	КонецЦикла;  
             
	ТекущееКоличествоСтрок = 0;
 
	ТекущаяСерия = ТекущаяДиаграмма.Серии.Добавить();
	ИсходнаяСерия = ИсходнаяДГ.Серии[0];
	ТекущаяСерия.ДополнительныйЦвет = ИсходнаяСерия.ДополнительныйЦвет;
	ТекущаяСерия.ПриоритетЦвета = ИсходнаяСерия.ПриоритетЦвета;
	ТекущаяСерия.Расшифровка = ИсходнаяСерия.Расшифровка;
	ТекущаяСерия.Текст = ИсходнаяСерия.Текст;
	ТекущаяСерия.Значение  = ИсходнаяСерия.Значение;
	ТекущаяСерия.Цвет = ИсходнаяСерия.Цвет;
	ТекущаяСерия.ЦветШтриховкиМеждуИнтервалами = ИсходнаяСерия.ЦветШтриховкиМеждуИнтервалами;
	ТекущаяСерия.ШтриховкаМеждуИнтервалами = ИсходнаяСерия.ШтриховкаМеждуИнтервалами;
	ТекущаяСерия.ШтриховкаПерекрывающихсяИнтервалов = ИсходнаяСерия.ШтриховкаПерекрывающихсяИнтервалов;
 
	ТекущаяКолекцияТочек = ТекущаяДиаграмма.Точки;
	Если СледующаяТочка <> Неопределено Тогда
		Родители = Новый Массив;
		Родитель = СледующаяТочка;
		Пока Родитель.Родитель <> Неопределено Цикл
			Родитель = Родитель.Родитель;
			Родители.Добавить(Родитель);
		КонецЦикла;  
  
		Для Индекс = 1 По Родители.Количество() Цикл
			ИсходныйРодитель = Родители[Родители.Количество() - Индекс];
			ТекущийРодитель = ТекущаяКолекцияТочек.Добавить();
			ТочкаВТочку(ИсходныйРодитель, ТекущийРодитель); 
   
			ТекущаяКолекцияТочек = ТекущийРодитель.Точки;
			ТекущееКоличествоСтрок = ТекущееКоличествоСтрок + 1;
		КонецЦикла;     
	КонецЕсли;  
КонецПроцедуры  

Процедура ТочкаВТочку(ИсходнаяТочка, КонечнаяТочка)
	КонечнаяТочка.ДополнительныйЦвет = ИсходнаяТочка.ДополнительныйЦвет;
	КонечнаяТочка.Значение = ИсходнаяТочка.Значение;
	КонечнаяТочка.Картинка = ИсходнаяТочка.Картинка;
	КонечнаяТочка.ПриоритетЦвета = ИсходнаяТочка.ПриоритетЦвета;
	КонечнаяТочка.Расшифровка = ИсходнаяТочка.Расшифровка;
	КонечнаяТочка.Текст = ИсходнаяТочка.Текст;
	КонечнаяТочка.Цвет = ИсходнаяТочка.Цвет;
	КонечнаяТочка.ЦветТекста = ИсходнаяТочка.ЦветТекста;
	КонечнаяТочка.ЦветФона = ИсходнаяТочка.ЦветФона;
	КонечнаяТочка.Шрифт = ИсходнаяТочка.Шрифт;
 
	ИсходноеЗначение = ИсходнаяДГ.ПолучитьЗначение(ИсходнаяТочка, ИсходнаяДГ.Серии[0]);
	КонечноеЗначение = ТекущаяДиаграмма.ПолучитьЗначение(КонечнаяТочка, ТекущаяСерия);
 
	КонечноеЗначение.ДополнительныйЦвет = ИсходноеЗначение.ДополнительныйЦвет;
	КонечноеЗначение.Расшифровка = ИсходноеЗначение.Расшифровка;
	КонечноеЗначение.Текст = ИсходноеЗначение.Текст;
	КонечноеЗначение.Цвет = ИсходноеЗначение.Цвет;
	КонечноеЗначение.ЦветТекста = ИсходноеЗначение.ЦветТекста;
	КонечноеЗначение.ЦветФона = ИсходноеЗначение.ЦветФона;
 
	Для Каждого ИсходныйИнтервал Из ИсходноеЗначение Цикл
		КонечныйИнтервал = КонечноеЗначение.Добавить();
		КонечныйИнтервал.Конец = ИсходныйИнтервал.Конец;
		КонечныйИнтервал.Начало = ИсходныйИнтервал.Начало;
		КонечныйИнтервал.Расшифровка = ИсходныйИнтервал.Расшифровка;
		КонечныйИнтервал.Текст = ИсходныйИнтервал.Текст;
		КонечныйИнтервал.Цвет = ИсходныйИнтервал.Цвет;
	КонецЦикла;  
КонецПроцедуры  

Процедура ВывестиТочки(ИсходныеТочки)
	Для Каждого Точка Из ИсходныеТочки Цикл 
		Если ТекущееКоличествоСтрок >= РазмерПорцииСтрок Тогда
			НоваяДиаграмма(Точка);
		КонецЕсли;   
  
		НоваяТочка = ТекущаяКолекцияТочек.Добавить();
		ТекущееКоличествоСтрок = ТекущееКоличествоСтрок + 1;
		ТочкаВТочку(Точка, НоваяТочка); 
  
		Если Точка.Точки.Количество() > 0 Тогда
			ТекущаяКолекцияТочек = НоваяТочка.Точки;
			ВывестиТочки(Точка.Точки);
   
			Если ТекущаяКолекцияТочек.Родитель.Родитель <> Неопределено Тогда
				ТекущаяКолекцияТочек = ТекущаяКолекцияТочек.Родитель.Родитель.Точки;
			Иначе    
				ТекущаяКолекцияТочек = ТекущаяДиаграмма.Точки;
			КонецЕсли;       
		КонецЕсли;   
	КонецЦикла;  
КонецПроцедуры