Настройка форм презентаций в C++

Обзор

Эта статья объясняет, как настраивать формы презентаций в Aspose.Slides, редактируя геометрию формы с помощью точек редактирования и геометрических путей. Она показывает, как работать с GeometryPath и IGeometryPath для изменения существующих форм, выполнения базовых операций редактирования пути, добавления или удаления точек и применения обновлённой геометрии обратно к форме.

Она также демонстрирует, как создавать пользовательские и составные формы, строить формы со скруглёнными углами и определять, является ли геометрия формы замкнутой.

Изменение формы с помощью точек редактирования

В PowerPoint вы можете использовать Edit Points для изменения квадрата:

  • Переместить угол внутрь или наружу.
  • Отрегулировать кривизну в точке.
  • Добавить новые точки.

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

Советы по редактированию форм

overview_image

При редактировании форм PowerPoint учитывайте следующие концепции:

  • Форма (или её путь) может быть закрытой или открытой.
  • Путь формы состоит из опорных точек, соединённых отрезками.
  • Отрезок может быть прямым или изогнутым. Опорные точки и контрольные рычаги определяют его форму.
  • Опорные точки могут быть угловыми, прямыми или сглаженными:
    • В угловой точке соседние отрезки могут соединяться под углом.
    • В сглаженной точке два контрольных рычага выровнены и находятся на одинаковом расстоянии от опорной точки.
    • В прямой точке два контрольных рычага выровнены, но могут находиться на разном расстоянии от опорной точки.
  • Перемещение опорных точек или корректировка их рычагов изменяет контур формы.

Для редактирования форм PowerPoint с помощью точек редактирования Aspose.Slides предоставляет класс GeometryPath и интерфейс IGeometryPath.

Простые операции редактирования

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

Operation Method
Добавить или вставить прямую линию. GeometryPath::LineTo
Добавить или вставить кубическую кривую Безье с двумя контрольными точками и конечной точкой. GeometryPath::CubicBezierTo
Добавить или вставить квадратичную кривую Безье с одной контрольной точкой и конечной точкой. GeometryPath::QuadraticBezierTo
Добавить эллиптическую дугу с указанными размерами и углами в градусах. GeometryPath::ArcTo
Замкнуть текущую фигуру, соединив её конечную точку с начальной. GeometryPath::CloseFigure
Задать начальную точку фигуры. GeometryPath::MoveTo
Удалить отрезок по индексу, начинающемуся с нуля. GeometryPath::RemoveAt

Добавление пользовательских точек в форму

В этом примере вставляются две точки в прямоугольную форму с помощью GeometryPath::LineTo и применяется отредактированный путь через IGeometryShape::SetGeometryPath. Координаты локальны для формы: обе новые точки находятся в её центре, на 100 пунктов от левого края и 50 пунктов от верхнего. Индексы отрезков 1 и 4 размещают их на верхнем и нижнем краях, притягивая эти края внутрь.

#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>

using namespace Aspose::Slides;
using namespace System;

auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, 200.0f, 100.0f);

auto geometryPath = shape->GetGeometryPaths()->idx_get(0);
geometryPath->LineTo(100.0f, 50.0f, 1);
geometryPath->LineTo(100.0f, 50.0f, 4);

shape->SetGeometryPath(geometryPath);

example1_image

Удаление точек из формы

В этом примере меняется контур в виде сердца, удаляя отрезок по индексу 2 (нумерация с нуля) с помощью GeometryPath::RemoveAt. Применение изменённого пути через IGeometryShape::SetGeometryPath обновляет контур формы.

#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>

using namespace Aspose::Slides;
using namespace System;

auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Heart, 100.0f, 100.0f, 300.0f, 300.0f);

auto path = shape->GetGeometryPaths()->idx_get(0);
path->RemoveAt(2);

shape->SetGeometryPath(path);

example2_image

Создание пользовательской формы

В этом примере создаётся пятиконечная звезда с помощью GeometryPath. Чередующиеся внешние и внутренние радиусы 100 и 50 пунктов определяют вершины и впадины; шаг в 72 градуса равномерно распределяет пять вершин. Путь замыкается с помощью GeometryPath::CloseFigure и применяется к прямоугольной форме через IGeometryShape::SetGeometryPath, заменяя её контур.

#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <drawing/point_f.h>
#include <system/collections/list.h>
#include <system/math.h>
#include <system/smart_ptr.h>

using namespace Aspose::Slides;
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Drawing;

auto points = System::MakeObject<List<PointF>>();

auto outerRadius = 100.0f;
auto innerRadius = 50.0f;
auto angleStep = 72;

for (auto angle = -90; angle < 270; angle += angleStep)
{
    auto radians = angle * (Math::PI / 180.f);
    auto x = outerRadius * Math::Cos(radians);
    auto y = outerRadius * Math::Sin(radians);
    points->Add(PointF((float)x + outerRadius, (float)y + outerRadius));

    radians = Math::PI * (angle + angleStep / 2) / 180.0;
    x = innerRadius * Math::Cos(radians);
    y = innerRadius * Math::Sin(radians);
    points->Add(PointF((float)x + outerRadius, (float)y + outerRadius));
}

auto starPath = System::MakeObject<GeometryPath>();
starPath->MoveTo(points->idx_get(0));

for (auto i = 1; i < points->get_Count(); i++)
{
    starPath->LineTo(points->idx_get(i));
}

starPath->CloseFigure();

auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, outerRadius * 2, outerRadius * 2);
shape->SetGeometryPath(starPath);

example3_image

Создание составной пользовательской формы

В этом примере определяются два замкнутых объекта GeometryPath для верхней и нижней трети формы. Применение обоих путей через IGeometryShape::SetGeometryPaths создаёт одну составную форму с разрывом в её средней трети.

#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <system/smart_ptr.h>

using namespace Aspose::Slides;
using namespace System;

auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100.0f, 100.0f, 200.0f, 100.0f);

auto topPath = System::MakeObject<GeometryPath>();
topPath->MoveTo(0.0f, 0.0f);
topPath->LineTo(shape->get_Width(), 0.0f);
topPath->LineTo(shape->get_Width(), shape->get_Height() / 3);
topPath->LineTo(0.0f, shape->get_Height() / 3);
topPath->CloseFigure();

auto bottomPath = System::MakeObject<GeometryPath>();
bottomPath->MoveTo(0.0f, shape->get_Height() / 3 * 2);
bottomPath->LineTo(shape->get_Width(), shape->get_Height() / 3 * 2);
bottomPath->LineTo(shape->get_Width(), shape->get_Height());
bottomPath->LineTo(0.0f, shape->get_Height());
bottomPath->CloseFigure();

auto paths = System::MakeArray<SharedPtr<IGeometryPath>>({ topPath, bottomPath });

shape->SetGeometryPaths(paths);

example4_image

Создание пользовательской формы со скруглёнными углами

В этом примере комбинируются прямые отрезки с GeometryPath::ArcTo для создания формы со скруглёнными внутрь углами. Размер каждого угла задаёт ширину и высоту дуги в пунктах, а sweep -90 градусов рисует эллипс четвертью против часовой стрелки. Завершённый путь применяется через IGeometryShape::SetGeometryPath, и презентация сохраняется в формате PPTX.

#include <DOM/GeometryPath.h>
#include <DOM/IAutoShape.h>
#include <DOM/IShapeCollection.h>
#include <DOM/ISlide.h>
#include <DOM/ISlideCollection.h>
#include <DOM/Presentation.h>
#include <DOM/ShapeType.h>
#include <Export/SaveFormat.h>
#include <drawing/point_f.h>

using namespace Aspose::Slides;
using namespace Aspose::Slides::Export;
using namespace System::Drawing;

auto shapeX = 20.f;
auto shapeY = 20.f;
auto shapeWidth = 300.f;
auto shapeHeight = 200.f;

auto leftTopSize = 50.f;
auto rightTopSize = 20.f;
auto rightBottomSize = 40.f;
auto leftBottomSize = 10.f;

auto presentation = System::MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Custom, shapeX, shapeY, shapeWidth, shapeHeight);

auto topLeft = PointF(leftTopSize, 0.0f);
auto topRight = PointF(shapeWidth - rightTopSize, 0.0f);
auto rightBottom = PointF(shapeWidth, shapeHeight - rightBottomSize);
auto bottomLeft = PointF(leftBottomSize, shapeHeight);
auto leftTop = PointF(0.0f, leftTopSize);

auto geometryPath = System::MakeObject<GeometryPath>();
geometryPath->MoveTo(topLeft);
geometryPath->LineTo(topRight);
geometryPath->ArcTo(rightTopSize, rightTopSize, 180.0f, -90.0f);
geometryPath->LineTo(rightBottom);
geometryPath->ArcTo(rightBottomSize, rightBottomSize, -90.0f, -90.0f);
geometryPath->LineTo(bottomLeft);
geometryPath->ArcTo(leftBottomSize, leftBottomSize, 0.0f, -90.0f);
geometryPath->LineTo(leftTop);
geometryPath->ArcTo(leftTopSize, leftTopSize, 90.0f, -90.0f);
geometryPath->CloseFigure();

shape->SetGeometryPath(geometryPath);

presentation->Save(u"output.pptx", SaveFormat::Pptx);

Определение, является ли геометрия формы замкнутой

Следующий вспомогательный метод проверяет, заканчивается ли каждый непустой геометрический путь замыкающим отрезком. Он читает IGeometryPath::get_PathData и сравнивает команду последнего отрезка IPathSegment::get_PathCommand с PathCommandType::Close. При отсутствии непустых путей возвращает false. Проверяется только конечная команда каждого пути; отдельные подфигуры внутри пути не анализируются. В примере выводится True для прямоугольника.

#include <DOM/Presentation.h>
#include <DOM/ISlide.h>
#include <DOM/IShapeCollection.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/ShapeType.h>
#include <system/array.h>
#include <system/smart_ptr.h>
#include <system/console.h>
#include <DOM/IPathSegment.h>
#include <DOM/PathCommandType.h>

using namespace Aspose::Slides;
using namespace System;

auto isGeometryClosed = [](SharedPtr<IGeometryShape> geometryShape)
{
    auto hasNonemptyPath = false;
    for (auto&& geometryPath : geometryShape->GetGeometryPaths())
    {
        auto segments = geometryPath->get_PathData();
        if (segments->get_Length() == 0)
        {
            continue;
        }

        hasNonemptyPath = true;
        auto lastSegment = segments->idx_get(segments->get_Length() - 1);
        if (lastSegment->get_PathCommand() != PathCommandType::Close)
        {
            return false;
        }
    }
    return hasNonemptyPath;
};

auto presentation = MakeObject<Presentation>();
auto slide = presentation->get_Slide(0);

auto shape = slide->get_Shapes()->AddAutoShape(ShapeType::Rectangle, 100, 100, 200, 100);
Console::WriteLine(isGeometryClosed(shape));

Извлечение и проверка элементов формы

Используйте IGeometryShape::CreateShapeElements для изучения существующей формы как элементов, содержащих точки пути и типы точек. Преобразуйте каждый возвращенный IShapeElement к ShapeElement, чтобы прочитать массивы, возвращаемые методами get_PathPoints и get_PathTypes. Такое представление полезно для анализа контура или передачи геометрии в пользовательские инструменты рисования.

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

#include <DOM/Presentation.h>
#include <DOM/ISlide.h>
#include <DOM/IShapeCollection.h>
#include <DOM/IAutoShape.h>
#include <DOM/IGeometryShape.h>
#include <DOM/IGeometryPath.h>
#include <DOM/ShapeType.h>
#include <system/array.h>
#include <system/smart_ptr.h>
#include <system/console.h>
#include <DOM/IShapeElement.h>
#include <DOM/ShapeElement.h>

using namespace Aspose::Slides;
using namespace System;

auto presentation = MakeObject<Presentation>(u"input.pptx");
auto slide = presentation->get_Slide(0);

SharedPtr<IGeometryShape> shape;
for (auto&& candidate : slide->get_Shapes())
{
    shape = AsCast<IGeometryShape>(candidate);
    if (shape != nullptr)
    {
        break;
    }
}
if (shape == nullptr)
{
    Console::WriteLine(u"No geometry shape found on the first slide.");
    return;
}

for (auto&& element : shape->CreateShapeElements())
{
    auto shapeElement = ExplicitCast<ShapeElement>(element);
    auto points = shapeElement->get_PathPoints();
    auto types = shapeElement->get_PathTypes();
    Console::WriteLine(u"Shape element:");

    for (auto i = 0; i < points->get_Length(); i++)
    {
        auto pointType = types[i] & 0x07; // Сохранить тип сегмента, исключая флаги.
        switch (pointType)
        {
            case 0:
                Console::WriteLine(u"Start figure at {0}", points[i]);
                break;
            case 1:
                Console::WriteLine(u"Line to {0}", points[i]);
                break;
            case 3:
                Console::WriteLine(u"Cubic Bezier: controls {0}, {1}; end {2}", points[i], points[i + 1], points[i + 2]);
                i += 2; // Использовать обе контрольные точки и конечную точку.
                break;
        }
        if ((types[i] & 0x80) != 0)
        {
            Console::WriteLine(u"Close subpath back to its starting point.");
        }
    }
}

Массивы соответствуют друг другу по индексу: points[i] — точка, а types[i] описывает её роль. Тип 0 начинается фигуру, тип 1 рисует линию от предыдущей точки к текущей. Кубический сегмент Безье начинается от предыдущей конечной точки и использует три последовательных элемента типа 3: две контрольные точки и новую конечную точку. Цикл обрабатывает эти три элемента вместе и проверяет конечную точку на замкнутость.

Типы точек также содержат флаги. Применяйте маску 0x07, чтобы определить тип сегмента, и проверяйте 0x80 отдельно, чтобы обнаружить закрытый подпуть. Например, 129 (0x81) означает конечную точку линии, закрывающую подпуть, а 131 (0x83) — конечную точку Безье, закрывающую его. Сравнение сырого значения только с 1 или 3 упустит такие случаи. Другие флаги, например тире (0x10) и маркер (0x20), не меняют базовый тип сегмента.

Координаты и преобразования. Точки измеряются в пунктах (1/72 дюйма), при этом X увеличивается вправо, а Y — вниз. Для не сгруппированной формы извлечённые координаты уже включают позицию, возвращаемую методами get_X и get_Y, и отражают её текущие ширину и высоту. Например, незавёрнутый прямоугольник в (100, 200) размером 120 × 80 имеет границы контура (100, 200)–(220, 280). Не добавляйте позицию формы повторно. Вычтите её X и Y, чтобы получить координаты относительно её необращённого кадра.

Извлечённые точки не учитывают вращение, установленное через set_Rotation , или горизонтальное и вертикальное отражения. Чтобы восстановить отображаемый контур, примените эти преобразования вокруг центра формы. Для сгруппированных форм также учитывайте отображение координат группы и её преобразования; не следует полагать, что извлечённые точки являются конечными координатами слайда, и не применяйте трансляцию или масштабирование дважды, если они уже учтены в кадре дочернего объекта.

Проверка против редактирования. Извлечённые массивы описывают контур; их изменение не обновляет исходную форму. Выбирайте этот подход, когда ваш код анализа или рисования нуждается в массивах точек/типов. Используйте GetGeometryPaths , если нужны команды геометрии в локальной системе координат формы или требуется редактировать её геометрию. Применяйте отредактированные пути через SetGeometryPath или SetGeometryPaths .

Преобразование GeometryPath в GraphicsPath

Текущий публичный API Aspose.Slides для C++ не предоставляет преобразований между геометрическими путями и графическими путями. Для проверки контура используйте элементы формы, как описано выше. Для редактирования формы используйте геометрические пути и применяйте обновлённые пути к форме.

FAQ

Что произойдёт с заливкой и контуром после замены геометрии?

Стиль остаётся привязанным к форме; изменяется только контур. Заливка и контур автоматически применяются к новой геометрии.

Как правильно вращать пользовательскую форму вместе с её геометрией?

Используйте метод формы set_Rotation ; геометрия вращается вместе с формой, поскольку она привязана к её собственной системе координат.

Можно ли преобразовать пользовательскую форму в изображение, чтобы “зафиксировать” результат?

Да. Экспортируйте нужную область слайд или саму форму в растровый формат; это упрощает дальнейшую работу со сложными геометриями.