ปรับแต่งรูปร่างการนำเสนอบน Android

ภาพรวม

บทความนี้อธิบายวิธีปรับแต่งรูปร่างการนำเสนอใน Aspose.Slides โดยการแก้ไขเรขาคณิตของรูปร่างผ่านจุดแก้ไขและเส้นทางเรขาคณิต มันแสดงวิธีใช้ GeometryPath และ IGeometryPath เพื่อแก้ไขรูปร่างที่มีอยู่, ทำการดำเนินการแก้ไขเส้นทางพื้นฐาน, เพิ่มหรือเอาจุดออก, และนำเรขาคณิตที่อัปเดตกลับไปยังรูปร่าง

นอกจากนี้ยังสาธิตวิธีสร้างรูปร่างแบบกำหนดเองและแบบผสม, สร้างรูปร่างที่มีมุมโค้ง, ตรวจสอบว่ารูปร่างเรขาคณิตปิดหรือไม่, และตรวจสอบส่วนประกอบของรูปร่างโดยใช้แอร์เรย์จุดของ Android

เปลี่ยนรูปร่างโดยใช้จุดแก้ไข

ใน PowerPoint, คุณสามารถใช้ Edit Points เพื่อแก้ไขสี่เหลี่ยมจัตุรัส:

  • ย้ายมุมเข้าไปหรือออกไป
  • ปรับความโค้งที่จุด
  • เพิ่มจุดใหม่

การแก้ไขจุดทำให้คุณสามารถเปลี่ยนโครงร่างของรูปร่างหรือใช้รูปร่างที่มีอยู่เป็นพื้นฐานสำหรับรูปร่างใหม่ได้

เคล็ดลับการแก้ไขรูปร่าง

overview_image

เมื่อแก้ไขรูปร่างใน PowerPoint ให้คำนึงถึงแนวคิดต่อไปนี้:

  • รูปร่าง (หรือเส้นทางของมัน) สามารถเป็นแบบปิดหรือเปิดได้
  • เส้นทางของรูปร่างประกอบด้วยจุดเยื้องเชื่อมต่อกันด้วยส่วนเส้น
  • ส่วนเส้นสามารถเป็นแบบตรงหรือโค้งได้ จุดเยื้อนและแฮนด์จับควบคุมกำหนดรูปทรงของมัน
  • จุดเยื้อนมีอยู่ในรูปแบบจุดมุม, จุดตรง, หรือจุดเรียบ:
    • ที่จุดมุม, ส่วนเส้นที่ต่อกันอาจเจอที่มุม
    • ที่จุดเรียบ, แฮนด์จับสองตัวจะจัดแนวและห่างจากจุดเยื้อนเท่ากัน
    • ที่จุดตรง, แฮนด์จับสองตัวจะจัดแนวแต่ระยะห่างจากจุดเยื้อนอาจต่างกัน
  • การย้ายจุดเยื้อนหรือปรับแฮนด์จับของมันจะเปลี่ยนโครงร่างของรูปร่าง

เพื่อแก้ไขรูปร่าง PowerPoint ผ่านจุดแก้ไข, Aspose.Slides ให้คลาส GeometryPath และอินเทอร์เฟซ IGeometryPath

  • ตัวอย่าง GeometryPath แทนเส้นทางเรขาคณิตของวัตถุ IGeometryShape
  • เพื่อดึง GeometryPath จากอินสแตนซ์ของ IGeometryShape, คุณสามารถใช้เมธอด IGeometryShape.getGeometryPaths
  • เพื่อกำหนด GeometryPath ให้กับรูปร่าง, คุณสามารถใช้เมธอดเหล่านี้: IGeometryShape.setGeometryPath สำหรับรูปร่างที่มีเส้นทางเดียวและ IGeometryShape.setGeometryPaths สำหรับรูปร่างที่มีหลายเส้นทาง
  • เพื่อเพิ่มส่วนเส้น, คุณสามารถใช้เมธอดภายใต้ IGeometryPath
  • ด้วยเมธอด IGeometryPath.setStroke และ IGeometryPath.setFillMode, คุณสามารถกำหนดลักษณะการแสดงของเส้นทางเรขาคณิตได้
  • ด้วยเมธอด IGeometryPath.getPathData คุณสามารถดึงเส้นทางเรขาคณิตของ GeometryShape เป็นแอร์เรย์ของส่วนเส้นได้

การดำเนินการแก้ไขอย่างง่าย

ใช้เมธอดต่อไปนี้เพื่อแก้ไขเส้นทางเรขาคณิต การโอเวอร์โหลดที่รับดัชนีจะใส่ส่วนเส้นที่ตำแหน่งศูนย์ฐานในแอร์เรย์ส่วนเส้นของเส้นทาง

การดำเนินการ วิธีการ
เพิ่มหรือแทรกเส้นตรง GeometryPath.lineTo
เพิ่มหรือแทรกเส้นโค้งเบซิเยร์แบบคิวบิกที่มีจุดควบคุมสองจุดและจุดสิ้นสุด GeometryPath.cubicBezierTo
เพิ่มหรือแทรกเส้นโค้งเบซิเยร์แบบควอดราติกที่มีจุดควบคุมหนึ่งจุดและจุดสิ้นสุด GeometryPath.quadraticBezierTo
เพิ่มส่วนโค้งวงรีโดยกำหนดมิติและมุมเป็นองศา GeometryPath.arcTo
ปิดรูปภาพปัจจุบันโดยเชื่อมจุดสิ้นสุดกับจุดเริ่มต้น GeometryPath.closeFigure
กำหนดจุดเริ่มต้นของรูปภาพ GeometryPath.moveTo
เอาส่วนเส้นออกที่ดัชนีศูนย์ฐาน GeometryPath.removeAt

เพิ่มจุดกำหนดเองให้กับรูปร่าง

ตัวอย่างนี้ใส่จุดสองจุดลงในรูปร่างสี่เหลี่ยมโดยใช้ GeometryPath.lineTo และนำเส้นทางที่แก้ไขไปใช้กับ IGeometryShape.setGeometryPath พิกัดเป็นค่าท้องถิ่นของรูปร่าง: ทั้งสองจุดใหม่อยู่ที่กึ่งกลาง, ห่างจากด้านซ้าย 100 จุดและจากด้านบน 50 จุด ดัชนีส่วนเส้น 1 และ 4 จะวางจุดเหล่านี้บนขอบบนและขอบล่าง, ดึงขอบเหล่านั้นเข้าไป

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

เอาจุดออกจากรูปร่าง

ตัวอย่างนี้เปลี่ยนโครงร่างรูปหัวใจโดยเอาส่วนเส้นที่ดัชนีศูนย์ฐาน 2 ออกด้วย 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 รัศมีภายนอกและภายในสลับกันที่ 100 และ 50 จุดกำหนดจุดและช่องว่าง; การขั้น 72 องศาจะเว้นระยะห่างให้ดาวห้าแฉกเท่าๆ กัน เส้นทางถูกปิดด้วย 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; // Consume both control points and the endpoint.
                        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 วาดเส้นจากจุดก่อนหน้าไปยังจุดปัจจุบัน ส่วนโค้งเบซิเยร์แบบคิวบิกเริ่มจากจุดสิ้นสุดก่อนหน้าและใช้รายการประเภท 3 สามรายการต่อเนื่อง: จุดควบคุมสองจุดตามด้วยจุดสิ้นสุดใหม่ ลูปจะรับสามรายการนี้พร้อมกันและตรวจสอบจุดสิ้นสุดเพื่อปิด

ประเภทจุดยังมีแฟล็กด้วย การทำมาสก์ด้วย 0x07 เพื่อระบุประเภทส่วนเส้น, และทดสอบ 0x80 แยกกันเพื่อค้นหาส่วนย่อยที่ปิด ตัวอย่างเช่น 129 (0x81) หมายถึงจุดสิ้นสุดของเส้นที่ปิดส่วนย่อย, และ 131 (0x83) หมายถึงจุดสิ้นสุดของเบซิเยร์ที่ปิดหนึ่งส่วน แฟล็กเหล่านี้ถูกเก็บเป็นไบต์ที่เป็นเครื่องหมายใน Java; การทำมาสก์จะรักษาค่าบิตตามที่ตั้งใจ การเปรียบเทียบค่าดิบเฉพาะ 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 ของรูปร่าง; เรขาคณิตจะหมุนตามรูปร่างเนื่องจากผูกกับระบบพิกัดของรูปร่างเอง

ฉันสามารถแปลงรูปร่างกำหนดเองเป็นภาพเพื่อ “ล็อก” ผลลัพธ์ได้หรือไม่?

ได้. ส่งออก สไลด์ ที่ต้องการหรือ รูปร่าง เองเป็นรูปแบบเรสเตอร์; วิธีนี้ทำให้การทำงานต่อกับเรขาคณิตซับซ้อนง่ายขึ้น