سفارشی‌سازی اشکال ارائه در اندروید

مرور کلی

این مقاله توضیح می‌دهد چگونه می‌توان اشکال ارائه در Aspose.Slides را با ویرایش هندسه شکل از طریق نقاط ویرایش و مسیرهای هندسی سفارشی کرد. نشان می‌دهد چگونه با GeometryPath و IGeometryPath کار کرد تا اشکال موجود را تغییر داد، عملیات پایه ویرایش مسیر را انجام داد، نقاط را اضافه یا حذف کرد و هندسه به‌روزرسانی‌شده را به شکل اعمال کرد.

همچنین نمایش می‌دهد چگونه اشکال سفارشی و مرکب ایجاد کرد، اشکالی با گوشه‌های منحنی ساخت، تعیین کرد آیا هندسه یک شکل بسته است یا خیر، و عناصر شکل را با استفاده از آرایه‌های نقطه‌ای Android بررسی کرد.

تغییر یک شکل با استفاده از نقاط ویرایش

در PowerPoint می‌توانید از نقاط ویرایش برای تعديل یک مربع استفاده کنید:

  • یک گوشه را به سمت داخل یا خارج حرکت دهید.
  • انحنای یک نقطه را تنظیم کنید.
  • نقاط جدید اضافه کنید.

ویرایش نقاط به شما امکان می‌دهد outlines یک شکل را تغییر دهید یا از یک شکل موجود به‌عنوان پایه‌ای برای شکل جدید استفاده کنید.

نکات ویرایش شکل

overview_image

هنگام ویرایش اشکال PowerPoint، این مفاهیم را در نظر داشته باشید:

  • یک شکل (یا مسیر آن) می‌تواند بسته یا باز باشد.
  • مسیر یک شکل از نقاط لنگر متصل‌شده توسط بخش‌ها تشکیل شده است.
  • یک بخش می‌تواند مستقیم یا منحنی باشد. نقاط لنگر و دستگیره‌های کنترل شکل آن را تعیین می‌کنند.
  • نقاط لنگر به صورت نقاط گوشه‌ای، نقاط مستقیم یا نقاط صاف وجود دارند:
    • در یک نقطه گوشه‌ای، بخش‌های مجاور می‌توانند زیر یک زاویه ملاقات کنند.
    • در یک نقطه صاف، دو دستگیره کنترل هم‌راستا هستند و فاصله مساوی از نقطه لنگر دارند.
    • در یک نقطه مستقیم، دو دستگیره کنترل هم‌راستا هستند اما می‌توانند فاصله‌های متفاوتی از نقطه لنگر داشته باشند.
  • جابجایی نقاط لنگر یا تنظیم دستگیره‌های آن‌ها شکل outlines را تغییر می‌دهد.

برای ویرایش اشکال PowerPoint از طریق نقاط ویرایش، Aspose.Slides کلاس GeometryPath و اینترفیس IGeometryPath را فراهم می‌کند.

عملیات ساده ویرایش

از متدهای زیر برای ویرایش یک مسیر هندسی استفاده کنید. نسخه‌های overload که یک ایندکس می‌پذیرند، یک بخش را در آن موقعیت صفر‑محور در آرایهٔ بخش‌های مسیر وارد می‌کند.

عملیات متد
افزودن یا درج یک خط مستقیم. GeometryPath.lineTo
افزودن یا درج یک منحنی Bézier مکعبی با دو نقطه کنترل و یک نقطه انتهایی. GeometryPath.cubicBezierTo
افزودن یا درج یک منحنی Bézier درجه دو با یک نقطه کنترل و یک نقطه انتهایی. GeometryPath.quadraticBezierTo
افزودن یک قوس بیضوی با ابعاد و زاویه‌های مشخص به درجه. GeometryPath.arcTo
بستن شکل فعلی با اتصال نقطهٔ انتهایی آن به نقطهٔ شروع. GeometryPath.closeFigure
تنظیم نقطهٔ شروع یک شکل. GeometryPath.moveTo
حذف یک بخش در یک ایندکس صفر‑محور. GeometryPath.removeAt

افزودن نقاط سفارشی به یک شکل

این مثال دو نقطه را به یک شکل مستطیلی با استفاده از GeometryPath.lineTo وارد می‌کند و مسیر ویرایش‌شده را با IGeometryShape.setGeometryPath اعمال می‌نماید. مختصات‌ها به‌صورت محلی نسبت به شکل هستند: هر دو نقطهٔ جدید در مرکز آن، ۱۰۰ نقطه از سمت چپ و ۵۰ نقطه از سمت بالا قرار دارند. ایندکس‌های بخش ۱ و ۴ آن‌ها را بر روی لبه‌های بالایی و پایینی قرار می‌دهد و این لبه‌ها را به سمت داخل می‌کشند.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);

    IGeometryPath geometryPath = shape.getGeometryPaths()[0];
    geometryPath.lineTo(100, 50, 1);
    geometryPath.lineTo(100, 50, 4);

    shape.setGeometryPath(geometryPath);
} finally {
    presentation.dispose();
}

example1_image

حذف نقاط از یک شکل

این مثال خطوط حاشیهٔ شکل قلبی را با حذف بخش در ایندکس صفر‑محور ۲ با استفاده از GeometryPath.removeAt تغییر می‌دهد. اعمال مسیر اصلاح‌شده با IGeometryShape.setGeometryPath مرزبندی شکل را به‌روز می‌کند.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Heart, 100, 100, 300, 300);

    IGeometryPath path = shape.getGeometryPaths()[0];
    path.removeAt(2);

    shape.setGeometryPath(path);
} finally {
    presentation.dispose();
}

example2_image

ایجاد یک شکل سفارشی

این مثال یک ستارهٔ پنج‌پر با استفاده از GeometryPath می‌سازد. شعاع‌های بیرونی و داخلی به ترتیب ۱۰۰ و ۵۰ نقطه، نوک‌ها و تو رفتگی‌ها را تعریف می‌کنند؛ یک گام ۷۲ درجه، پنج نوک را به‌صورت مساوی پراکنده می‌کند. مسیر با GeometryPath.closeFigure بسته می‌شود و با استفاده از IGeometryShape.setGeometryPath به یک شکل مستطیلی اعمال می‌شود و حاشیهٔ آن را جایگزین می‌کند.

import com.aspose.slides.*;
import android.graphics.PointF;
import java.util.ArrayList;
import java.util.List;

List<PointF> points = new ArrayList<>();

float outerRadius = 100f;
float innerRadius = 50f;
int angleStep = 72;

for (int angle = -90; angle < 270; angle += angleStep)
{
    double radians = angle * (Math.PI / 180f);
    double x = outerRadius * Math.cos(radians);
    double y = outerRadius * Math.sin(radians);
    PointF point = new PointF((float)x + outerRadius, (float)y + outerRadius);
    points.add(point);

    radians = Math.PI * (angle + angleStep / 2) / 180.0;
    x = innerRadius * Math.cos(radians);
    y = innerRadius * Math.sin(radians);
    point = new PointF((float)x + outerRadius, (float)y + outerRadius);
    points.add(point);
}

GeometryPath starPath = new GeometryPath();
starPath.moveTo(points.get(0));

for (int i = 1; i < points.size(); i++)
{
    starPath.lineTo(points.get(i));
}

starPath.closeFigure();

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, outerRadius * 2, outerRadius * 2);
    shape.setGeometryPath(starPath);
} finally {
    presentation.dispose();
}

example3_image

ایجاد یک شکل سفارشی ترکیبی

این مثال دو شیء GeometryPath بسته برای یک‑سوم بالایی و یک‑سوم پایینی یک شکل تعریف می‌کند. اعمال هر دو مسیر با IGeometryShape.setGeometryPaths یک شکل ترکیبی واحد با یک فاصله در یک‑سوم میانی ایجاد می‌کند.

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);

    GeometryPath topPath = new GeometryPath();
    topPath.moveTo(0, 0);
    topPath.lineTo(shape.getWidth(), 0);
    topPath.lineTo(shape.getWidth(), shape.getHeight() / 3);
    topPath.lineTo(0, shape.getHeight() / 3);
    topPath.closeFigure();

    GeometryPath bottomPath = new GeometryPath();
    bottomPath.moveTo(0, shape.getHeight() / 3 * 2);
    bottomPath.lineTo(shape.getWidth(), shape.getHeight() / 3 * 2);
    bottomPath.lineTo(shape.getWidth(), shape.getHeight());
    bottomPath.lineTo(0, shape.getHeight());
    bottomPath.closeFigure();

    shape.setGeometryPaths(new GeometryPath[] { topPath, bottomPath });
} finally {
    presentation.dispose();
}

example4_image

ایجاد یک شکل سفارشی با گوشه‌های منحنی

این مثال بخش‌های مستقیم را با GeometryPath.arcTo ترکیب می‌کند تا شکلی با گوشه‌های منحنی به سمت داخل به‌دست آورد. هر اندازهٔ گوشه عرض و ارتفاع قوس را به نقاط تعیین می‌کند و زاویهٔ -90 درجه یک ربع بیضی مخالف جهت عقربه‌های ساعت می‌کشد. مسیر نهایی با IGeometryShape.setGeometryPath اعمال می‌شود و ارائه در قالب PPTX ذخیره می‌شود.

import com.aspose.slides.*;
import android.graphics.PointF;

float shapeX = 20f;
float shapeY = 20f;
float shapeWidth = 300f;
float shapeHeight = 200f;

float leftTopSize = 50f;
float rightTopSize = 20f;
float rightBottomSize = 40f;
float leftBottomSize = 10f;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Custom, shapeX, shapeY, shapeWidth, shapeHeight);

    PointF topLeft = new PointF(leftTopSize, 0);
    PointF topRight = new PointF(shapeWidth - rightTopSize, 0);
    PointF rightBottom = new PointF(shapeWidth, shapeHeight - rightBottomSize);
    PointF bottomLeft = new PointF(leftBottomSize, shapeHeight);
    PointF leftTop = new PointF(0, leftTopSize);

    GeometryPath geometryPath = new GeometryPath();
    geometryPath.moveTo(topLeft);
    geometryPath.lineTo(topRight);
    geometryPath.arcTo(rightTopSize, rightTopSize, 180, -90);
    geometryPath.lineTo(rightBottom);
    geometryPath.arcTo(rightBottomSize, rightBottomSize, -90, -90);
    geometryPath.lineTo(bottomLeft);
    geometryPath.arcTo(leftBottomSize, leftBottomSize, 0, -90);
    geometryPath.lineTo(leftTop);
    geometryPath.arcTo(leftTopSize, leftTopSize, 90, -90);
    geometryPath.closeFigure();

    shape.setGeometryPath(geometryPath);

    presentation.save("output.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

تشخیص اینکه آیا هندسهٔ یک شکل بسته است

کمک‌کنندهٔ زیر بررسی می‌کند که آیا هر مسیر هندسی غیر خالی با یک بخش بسته پایان می‌یابد یا نه. این متد IGeometryPath.getPathData را می‌خواند و فرمان نهایی بخش را با IPathSegment.getPathCommand مقایسه می‌کند و با PathCommandType.Close مقایسه می‌کند. وقتی مسیرهای غیر خالی وجود ندارند، مقدار false برمی‌گرداند. این متد فرمان نهایی هر مسیر را بررسی می‌کند؛ نه اینکه هر زیرشکل داخل مسیر را بررسی کند. مثال برای یک مستطیل مقدار true چاپ می‌کند.

import com.aspose.slides.*;

class GeometryInspector {
    boolean isGeometryClosed(IGeometryShape geometryShape) {
        boolean hasNonemptyPath = false;

        for (IGeometryPath geometryPath : geometryShape.getGeometryPaths()) {
            IPathSegment[] segments = geometryPath.getPathData();
            if (segments.length == 0) {
                continue;
            }

            hasNonemptyPath = true;
            IPathSegment lastSegment = segments[segments.length - 1];
            if (lastSegment.getPathCommand() != PathCommandType.Close) {
                return false;
            }
        }

        return hasNonemptyPath;
    }
}

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 100, 100, 200, 100);

    GeometryInspector inspector = new GeometryInspector();
    boolean closed = inspector.isGeometryClosed(shape);
    System.out.println(closed);
} finally {
    presentation.dispose();
}

استخراج و بررسی عناصر شکل

از متد IGeometryShape.createShapeElements برای بررسی یک شکل موجود به‌عنوان عناصر شامل نقاط مسیر و انواع نقاط استفاده کنید. هر شیء برگشتی از نوع IShapeElement را به ShapeElement تبدیل کنید تا آرایه‌های نقاط و انواع را از طریق متدهای getPathPoints و getPathTypes دریافت کنید. این نمایش برای تحلیل مرزبندی یا انتقال هندسه به ابزارهای رسم سفارشی مفید است.

مثال فایلی به نام input.pptx باز می‌کند، اولین شکل هندسی را مستقیماً در اولین اسلاید پیدا می‌کند و بخش‌های مسیر آن را چاپ می‌کند. برای مشاهدهٔ خروجی‌های هر دو نوع خط مستقیم و منحنی، از ارائه‌ای استفاده کنید که شامل شکل با بخش‌های مستقیم و منحنی باشد. این متد درون گروه‌ها را جستجو نمی‌کند.

import com.aspose.slides.*;
import android.graphics.PointF;

Presentation presentation = new Presentation("input.pptx");
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IGeometryShape shape = null;
    for (IShape candidate : slide.getShapes()) {
        if (candidate instanceof IGeometryShape) {
            shape = (IGeometryShape) candidate;
            break;
        }
    }

    if (shape == null) {
        System.out.println("No geometry shape found on the first slide.");
    } else {
        for (IShapeElement element : shape.createShapeElements()) {
            ShapeElement shapeElement = (ShapeElement) element;
            PointF[] points = shapeElement.getPathPoints();
            byte[] types = shapeElement.getPathTypes();
            System.out.println("Shape element:");

            for (int i = 0; i < points.length; i++) {
                int pointType = types[i] & 0x07; // نوع بخش را نگه می‌دارد، پرچم‌ها را مستثنی می‌کند.
                switch (pointType) {
                    case 0:
                        System.out.println("Start figure at " + points[i]);
                        break;
                    case 1:
                        System.out.println("Line to " + points[i]);
                        break;
                    case 3:
                        System.out.println("Cubic Bezier: controls " + points[i] + ", " + points[i + 1] + "; end " + points[i + 2]);
                        i += 2; // هر دو نقطه کنترل و نقطه انتهایی را مصرف می‌کند.
                        break;
                }

                if ((types[i] & 0x80) != 0) {
                    System.out.println("Close subpath back to its starting point.");
                }
            }
        }
    }
} finally {
    presentation.dispose();
}

آرایه‌ها براساس ایندکس مطابقت دارند: points[i] یک نقطه است و types[i] نقش آن را توصیف می‌کند. نوع 0 یک شکل را شروع می‌کند و نوع 1 خطی را از نقطهٔ قبلی به نقطهٔ جاری می‌کشد. یک بخش Bézier مکعبی از نقطهٔ انتهای قبلی شروع می‌شود و از سه ورودی متوالی نوع‑3 استفاده می‌کند: دو نقطهٔ کنترل و سپس نقطهٔ انتهایی جدید. حلقه این سه ورودی را به‌صورت یکجا مصرف می‌کند و نقطهٔ انتهایی را برای بستن بررسی می‌کند.

انواع نقطه همچنین شامل پرچم‌ها هستند. با استفاده از ماسک 0x07 می‌توانید نوع بخش را شناسایی کنید و پرچم 0x80 را به‌صورت جداگانه برای تشخیص یک زیرمسیر بسته تست کنید. به‌عنوان مثال، 129 (0x81) به معنای نقطهٔ انتهایی خطی است که یک زیرمسیر را می‌بندد و 131 (0x83) به معنای نقطهٔ انتهایی Bézier است که یک زیرمسیر را می‌بندد. جاوا این پرچم‌ها را در بایت‌های علامت‌دار ذخیره می‌کند؛ ماسک کردن مقادیر بایت‌های علامت‌دار مقدارهای بیت اصلی را حفظ می‌کند. مقایسهٔ مقدار خام فقط با 1 یا 3 این موارد را از دست می‌دهد. پرچم‌های دیگر مانند dash (0x10) و marker (0x20) نوع پایهٔ بخش را تغییر نمی‌دهند.

مختصات و تبدیل‌ها. نقاط بر حسب نقطه (1/72 اینچ) اندازه‌گیری می‌شوند، با X به سمت راست افزایشی و Y به سمت پایین افزایشی. برای یک شکل بدون گروه، مختصات استخراج‌شده هم اکنون شامل موقعیت بازگشتی توسط getX و getY و عرض و ارتفاع فعلی آن است. به‌عنوان مثال، یک مستطیل بدون چرخش در (100, 200) با اندازهٔ 120 × 80 دارای مرزهای مرزبندی (100, 200)–(220, 280) است. موقعیت شکل را دوبار اضافه نکنید. برای دریافت مختصات نسبی به چارچوب چرخش‌نشده، X و Y آن را کم کنید.

نقاط استخراج‌شده چرخش شکل، که توسط getRotation بازگردانده می‌شود، یا وارونگی افقی و عمودی را اعمال نمی‌کنند. برای بازتولید مرزبندی نمایش داده‌شده، این تبدیل‌ها را نسبت به مرکز شکل اعمال کنید. برای اشکال گروهی، همچنین باید نگاشت مختصات گروه و تبدیل‌ها را در نظر بگیرید؛ فرض نکنید که نقاط استخراج‌شده نهایی هستند یا ترجمه یا مقیاس را دو بار اعمال کنید اگر قبلاً در چارچوب فرزند منعکس شده است.

بازرسی در مقابل ویرایش. آرایه‌های استخراج‌شده مرزبندی را توصیف می‌کنند؛ تغییر آن‌ها شکل اصلی را به‌روزرسانی نمی‌کند. این روش را زمانی انتخاب کنید که کد تحلیل یا رسم شما نیاز به آرایه‌های نقطه/نوع داشته باشد. از getGeometryPaths زمانی استفاده کنید که به دستورات هندسی در سیستم مختصات محلی شکل نیاز دارید یا می‌خواهید هندسه‌اش را ویرایش کنید. مسیرهای ویرایش‌شده را از طریق setGeometryPath یا setGeometryPaths اعمال کنید.

سوالات متداول

پس از جایگزین کردن هندسه، پر کردن و حاشیهٔ شکل چه اتفاقی می‌افتد؟

سبک همراه با شکل باقی می‌ماند؛ فقط مرزبندی تغییر می‌کند. پر کردن و حاشیه به‌صورت خودکار بر روی هندسهٔ جدید اعمال می‌شوند.

چگونه می‌توان یک شکل سفارشی را به‌درستی همراه با هندسه‌اش چرخاند؟

از متد setRotation شکل استفاده کنید؛ هندسه با شکل می‌چرخد زیرا به سیستم مختصات خود شکل متصل است.

آیا می‌توانم یک شکل سفارشی را به تصویر تبدیل کنم تا «قفل» شود؟

بله. ناحیهٔ مورد نیاز را به‌عنوان اسلاید یا خود شکل به فرمت رستر صادر کنید؛ این کار کار با هندسه‌های پیچیده را ساده‌تر می‌کند.