Cosmetic Bend Sample

Description

Demonstrates creating sheet metal cosmetic bend features in a flat pattern: a press brake bend with an explicit radius override and custom angle, and a crease bend with manufacturing notes.

Code Samples

"""Cosmetic Bend API sample — demonstrates creating sheet metal cosmetic bend features in a
flat pattern: a press brake bend with an explicit radius override and custom angle, and a
crease bend with manufacturing notes.

Builds its own flat, sheet-metal plate and flat pattern in a new document, so it can be run
as-is without any external data file.
"""

import math
import traceback
import adsk.core
import adsk.fusion

app = adsk.core.Application.get()

PLATE_SIZE_CM = 10.0
PRESS_BRAKE_ANGLE_RADIANS = math.pi / 3  # 60 degrees
PRESS_BRAKE_RADIUS_CM = 0.2  # explicit radius override
PRESS_BRAKE_NOTES = 'Press brake bend: explicit 0.2 cm radius override'
CREASE_NOTES = 'Crease: sharp fold, no radius'


def createFlatPatternComponent(design):
    """Extrudes a flat plate, converts it to sheet metal, and creates its flat pattern. Returns
    (FlatPatternComponent, topFace), or (None, None) on failure. The flat pattern's own
    Sketches/Features live on a FlatPatternComponent, reached through the FlatPatternProduct in
    the document that matches the new flat pattern's entity token."""
    rootComp = design.rootComponent

    # Use the first library sheet metal rule's own thickness for the plate, so the extruded body
    # is guaranteed to match the rule convertToSheetMetal() is given below -- no need to probe the
    # body's thickness back out after the fact.
    libraryRules = design.librarySheetMetalRules
    if libraryRules.count == 0:
        return None, None
    rule = libraryRules.item(0)
    thickness = rule.thickness.value

    sketch = rootComp.sketches.add(rootComp.xYConstructionPlane)
    sketch.sketchCurves.sketchLines.addTwoPointRectangle(
        adsk.core.Point3D.create(0, 0, 0),
        adsk.core.Point3D.create(PLATE_SIZE_CM, PLATE_SIZE_CM, 0))
    prof = sketch.profiles.item(0)

    extrudes = rootComp.features.extrudeFeatures
    extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
    extInput.setDistanceExtent(False, adsk.core.ValueInput.createByReal(thickness))
    extInput.isSolid = True
    ext = extrudes.add(extInput)
    body = ext.bodies.item(0)

    # Find the largest planar face -- this becomes the sheet metal body's stationary face.
    largestFace = None
    maxArea = 0.0
    for face in body.faces:
        if face.geometry.surfaceType == adsk.core.SurfaceTypes.PlaneSurfaceType and face.area > maxArea:
            maxArea = face.area
            largestFace = face
    if largestFace is None:
        return None, None

    if not body.convertToSheetMetal(largestFace, rule):
        return None, None

    flatPattern = rootComp.createFlatPattern(largestFace)
    if flatPattern is None:
        return None, None

    products = design.parentDocument.products
    for i in range(products.count):
        candidate = adsk.fusion.FlatPatternProduct.cast(products.item(i))
        if candidate and candidate.flatPattern and candidate.flatPattern.entityToken == flatPattern.entityToken:
            return adsk.fusion.FlatPatternComponent.cast(candidate.rootComponent), flatPattern.topFace

    return None, None


def run(_context: str):
    """Documentation sample: Create sheet metal cosmetic bend features in a flat pattern."""
    try:
        app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
        design = adsk.fusion.Design.cast(app.activeProduct)

        fpComponent, topFace = createFlatPatternComponent(design)
        if fpComponent is None:
            app.log('Failed: could not create the flat pattern component')
            return

         Measure the face's true border in ITS OWN plane, not world X/Y -- createFlatPattern()
         can lay the flat pattern body out at an arbitrary orientation, so a world-axis-aligned
         bounding box can cut corners off the actual (rotated) rectangle. Project every vertex
         onto the face's own uDirection/vDirection basis instead; that basis is exactly aligned
         with the face's real edges, so a bounding box in that space is a tight fit regardless
         of the face's orientation.
        plane = adsk.core.Plane.cast(topFace.geometry)
        if plane is None:
            app.log('Failed: top face geometry is not a Plane')
            return
        origin = plane.origin
        uDir = plane.uDirection
        vDir = plane.vDirection

        minU = maxU = minV = maxV = 0.0
        haveBounds = False
        for vertex in topFace.vertices:
            pt = vertex.geometry
            relX, relY, relZ = pt.x - origin.x, pt.y - origin.y, pt.z - origin.z
            u = relX * uDir.x + relY * uDir.y + relZ * uDir.z
            v = relX * vDir.x + relY * vDir.y + relZ * vDir.z
            if not haveBounds:
                minU = maxU = u
                minV = maxV = v
                haveBounds = True
            else:
                minU, maxU = min(minU, u), max(maxU, u)
                minV, maxV = min(minV, v), max(maxV, v)
        if not haveBounds:
            app.log('Failed: top face has no vertices')
            return

        fpSketch = fpComponent.sketches.add(topFace)

         addByTwoPoints expects points in the SKETCH's own local coordinate space, not model
         space -- build each point in model space from the (u, v) face-plane basis, then
         explicitly convert via modelToSketchSpace() before handing it to the sketch curve
         creator.
        def pointAtUV(u, v):
            modelPoint = adsk.core.Point3D.create(
                origin.x + u * uDir.x + v * vDir.x,
                origin.y + u * uDir.y + v * vDir.y,
                origin.z + u * uDir.z + v * vDir.z)
            return fpSketch.modelToSketchSpace(modelPoint)

        lines = fpSketch.sketchCurves.sketchLines
         Two bend lines placed at 30% and 70% across the face, each spanning border to border.
        line1U = minU + (maxU - minU) * 0.3
        line2U = minU + (maxU - minU) * 0.7
        line1 = lines.addByTwoPoints(pointAtUV(line1U, minV), pointAtUV(line1U, maxV))
        line2 = lines.addByTwoPoints(pointAtUV(line2U, minV), pointAtUV(line2U, maxV))

        features = fpComponent.features

         Example 1: Create a press brake cosmetic bend with an explicit radius override
        cbFeatures = features.cosmeticBendFeatures
        angleInput = adsk.core.ValueInput.createByReal(PRESS_BRAKE_ANGLE_RADIANS)
        inp1 = cbFeatures.createInput(line1, adsk.fusion.CosmeticBendTypes.PressBrakeCosmeticBendType, angleInput)
        if inp1 is None:
            app.log('Failed: createInput() for the first line returned None')
            return
        radiusInput = adsk.core.ValueInput.createByReal(PRESS_BRAKE_RADIUS_CM)
        if not inp1.setToPressBrake(angleInput):
            app.log('Failed: setToPressBrake() returned False')
            return
        inp1RadiusOverride = inp1.bendRadiusOverride
        if inp1RadiusOverride is None:
            app.log('Failed: bendRadiusOverride() returned None')
            return
        if not inp1RadiusOverride.setOverride(radiusInput):
            app.log('Failed: setOverride() returned False')
            return
        inp1.notes = PRESS_BRAKE_NOTES

        pressBrakeFeature = cbFeatures.add(inp1)
        if pressBrakeFeature is None:
            app.log('Failed: add() for the press brake bend returned None')
            return
        pressBrakeRadiusOverride = pressBrakeFeature.bendRadiusOverride
        if pressBrakeRadiusOverride is None:
            app.log('Failed: bendRadiusOverride() on feature returned None')
            return
        app.log('Created press brake cosmetic bend: {}, angle={} rad, radius={} cm'.format(
            pressBrakeFeature.name, pressBrakeFeature.angle.value, pressBrakeRadiusOverride.bendRadius.value))

         Example 2: Create a crease cosmetic bend (sharp fold, no radius/angle)
        cbFeatures = features.cosmeticBendFeatures
        inp2 = cbFeatures.createInput(line2, adsk.fusion.CosmeticBendTypes.CreaseCosmeticBendType)
        if inp2 is None:
            app.log('Failed: createInput() for the second line returned None')
            return
        inp2.notes = CREASE_NOTES

        creaseFeature = cbFeatures.add(inp2)
        if creaseFeature is None:
            app.log('Failed: add() for the crease bend returned None')
            return
        app.log('Created crease cosmetic bend: {}'.format(creaseFeature.name))

        app.log('CosmeticBendSample completed successfully.')

    except Exception:
        app.log(f'Failed:\n{traceback.format_exc()}')
// Cosmetic Bend API sample — demonstrates creating sheet metal cosmetic bend features in a flat
// pattern: a press brake bend with an explicit radius override and custom angle, and a crease bend
// with manufacturing notes.
//
// Builds its own flat, sheet-metal plate and flat pattern in a new document, so it can be run as-is
// without any external data file.

#include <Core/CoreAll.h>
#include <Fusion/FusionAll.h>
#include <string>

using namespace adsk::core;
using namespace adsk::fusion;

static const double PI = 3.14159265358979323846;

static const double PLATE_SIZE_CM = 10.0;
static const double PRESS_BRAKE_ANGLE_RADIANS = PI / 3.0; // 60 degrees
static const double PRESS_BRAKE_RADIUS_CM = 0.2;          // explicit radius override
static const char* PRESS_BRAKE_NOTES = "Press brake bend: explicit 0.2 cm radius override";
static const char* CREASE_NOTES = "Crease: sharp fold, no radius";

Ptr<Application> app;

#define CHECK(expr)                                                                                                    \
    if (!(expr))                                                                                                       \
    {                                                                                                                  \
        app->log("FAIL: " #expr);                                                                                      \
        return false;                                                                                                  \
    }
#define CHECK_PTR(ptr) CHECK((ptr) != nullptr)

// Extrudes a flat plate, converts it to sheet metal, and creates its flat pattern. The flat
// pattern's own Sketches/Features live on a FlatPatternComponent, reached through the
// FlatPatternProduct in the document that matches the new flat pattern's entity token.
static Ptr<FlatPatternComponent> createFlatPatternComponent(const Ptr<Design>& design, Ptr<BRepFace>& outTopFace)
{
    auto rootComp = design->rootComponent();
    if (!rootComp)
        return nullptr;

    // Use the first library sheet metal rule's own thickness for the plate, so the extruded body
    // is guaranteed to match the rule convertToSheetMetal() is given below -- no need to probe the
    // body's thickness back out after the fact.
    auto libraryRules = design->librarySheetMetalRules();
    if (!libraryRules || libraryRules->count() == 0)
        return nullptr;
    auto rule = libraryRules->item(0);
    if (!rule || !rule->thickness())
        return nullptr;
    double thickness = rule->thickness()->value();

    auto sketch = rootComp->sketches()->add(rootComp->xYConstructionPlane());
    if (!sketch)
        return nullptr;
    sketch->sketchCurves()->sketchLines()->addTwoPointRectangle(
        Point3D::create(0, 0, 0), Point3D::create(PLATE_SIZE_CM, PLATE_SIZE_CM, 0));

    auto prof = sketch->profiles()->item(0);
    if (!prof)
        return nullptr;

    auto extrudes = rootComp->features()->extrudeFeatures();
    if (!extrudes)
        return nullptr;
    auto extInput = extrudes->createInput(prof, FeatureOperations::NewBodyFeatureOperation);
    if (!extInput)
        return nullptr;
    extInput->setDistanceExtent(false, ValueInput::createByReal(thickness));
    extInput->isSolid(true);
    auto ext = extrudes->add(extInput);
    if (!ext)
        return nullptr;

    auto body = ext->bodies()->item(0);
    if (!body)
        return nullptr;

    // Find the largest planar face -- this becomes the sheet metal body's stationary face.
    Ptr<BRepFace> largestFace;
    double maxArea = 0.0;
    auto faces = body->faces();
    if (!faces)
        return nullptr;
    for (size_t i = 0; i < faces->count(); ++i)
    {
        auto face = faces->item(i);
        if (face && face->geometry()->surfaceType() == SurfaceTypes::PlaneSurfaceType && face->area() > maxArea)
        {
            maxArea = face->area();
            largestFace = face;
        }
    }
    if (!largestFace)
        return nullptr;

    if (!body->convertToSheetMetal(largestFace, rule))
        return nullptr;

    auto flatPattern = rootComp->createFlatPattern(largestFace);
    if (!flatPattern)
        return nullptr;
    outTopFace = flatPattern->topFace();

    auto document = design->parentDocument();
    if (!document)
        return nullptr;
    auto products = document->products();
    if (!products)
        return nullptr;

    for (size_t i = 0; i < products->count(); ++i)
    {
        auto candidate = products->item(i)->cast<FlatPatternProduct>();
        if (candidate && candidate->flatPattern() &&
            candidate->flatPattern()->entityToken() == flatPattern->entityToken())
            return candidate->rootComponent()->cast<FlatPatternComponent>();
    }

    return nullptr;
}

extern "C" XI_EXPORT bool run(const char* context)
{
    app = Application::get();
    if (!app)
        return false;

    auto doc = app->documents()->add(DocumentTypes::FusionDesignDocumentType);
    CHECK_PTR(doc);

    auto design = app->activeProduct()->cast<Design>();
    CHECK_PTR(design);

    Ptr<BRepFace> topFace;
    auto fpComponent = createFlatPatternComponent(design, topFace);
    CHECK_PTR(fpComponent);
    CHECK_PTR(topFace);

     Measure the face's true border in ITS OWN plane, not world X/Y -- createFlatPattern() can
     lay the flat pattern body out at an arbitrary orientation, so a world-axis-aligned bounding
     box can cut corners off the actual (rotated) rectangle. Project every vertex onto the face's
     own uDirection/vDirection basis instead; that basis is exactly aligned with the face's real
     edges, so a bounding box in that space is a tight fit regardless of the face's orientation.
    auto plane = topFace->geometry()->cast<Plane>();
    CHECK_PTR(plane);
    auto origin = plane->origin();
    auto uDir = plane->uDirection();
    auto vDir = plane->vDirection();
    CHECK_PTR(origin);
    CHECK_PTR(uDir);
    CHECK_PTR(vDir);

    auto vertices = topFace->vertices();
    CHECK_PTR(vertices);
    CHECK(vertices->count() > 0);

    double minU = 0.0, maxU = 0.0, minV = 0.0, maxV = 0.0;
    bool haveBounds = false;
    for (size_t i = 0; i < vertices->count(); ++i)
    {
        auto vertex = vertices->item(i);
        if (!vertex)
            continue;
        auto pt = vertex->geometry();
        if (!pt)
            continue;
        double relX = pt->x() - origin->x();
        double relY = pt->y() - origin->y();
        double relZ = pt->z() - origin->z();
        double u = relX * uDir->x() + relY * uDir->y() + relZ * uDir->z();
        double v = relX * vDir->x() + relY * vDir->y() + relZ * vDir->z();
        if (!haveBounds)
        {
            minU = maxU = u;
            minV = maxV = v;
            haveBounds = true;
            continue;
        }
        if (u < minU)
            minU = u;
        if (u > maxU)
            maxU = u;
        if (v < minV)
            minV = v;
        if (v > maxV)
            maxV = v;
    }
    CHECK(haveBounds);

    auto fpSketch = fpComponent->sketches()->add(topFace);
    CHECK_PTR(fpSketch);

     addByTwoPoints expects points in the SKETCH's own local coordinate space, not model space --
     build each point in model space from the (u, v) face-plane basis, then explicitly convert
     via modelToSketchSpace() before handing it to the sketch curve creator.
    auto pointAtUV = [&](double u, double v) {
        Ptr<Point3D> modelPoint = Point3D::create(
            origin->x() + u * uDir->x() + v * vDir->x(),
            origin->y() + u * uDir->y() + v * vDir->y(),
            origin->z() + u * uDir->z() + v * vDir->z());
        return fpSketch->modelToSketchSpace(modelPoint);
    };

    auto lines = fpSketch->sketchCurves()->sketchLines();
    CHECK_PTR(lines);
     Two bend lines placed at 30% and 70% across the face, each spanning border to border.
    double line1U = minU + (maxU - minU) * 0.3;
    double line2U = minU + (maxU - minU) * 0.7;
    auto line1 = lines->addByTwoPoints(pointAtUV(line1U, minV), pointAtUV(line1U, maxV));
    CHECK_PTR(line1);
    auto line2 = lines->addByTwoPoints(pointAtUV(line2U, minV), pointAtUV(line2U, maxV));
    CHECK_PTR(line2);

    auto features = fpComponent->features();
    CHECK_PTR(features);

     Example 1: Create a press brake cosmetic bend with an explicit radius override
    auto cbFeatures = features->cosmeticBendFeatures();
    CHECK_PTR(cbFeatures);

    auto angleInput = ValueInput::createByReal(PRESS_BRAKE_ANGLE_RADIANS);
    auto inp1 = cbFeatures->createInput(line1, CosmeticBendTypes::PressBrakeCosmeticBendType, angleInput);
    CHECK_PTR(inp1);

    auto radiusInput = ValueInput::createByReal(PRESS_BRAKE_RADIUS_CM);
    CHECK(inp1->setToPressBrake(angleInput));
    auto inp1RadiusOverride = inp1->bendRadiusOverride();
    CHECK_PTR(inp1RadiusOverride);
    CHECK(inp1RadiusOverride->setOverride(radiusInput));
    CHECK(inp1->notes(PRESS_BRAKE_NOTES));

    auto pressBrakeFeature = cbFeatures->add(inp1);
    CHECK_PTR(pressBrakeFeature);
    auto pressBrakeRadiusOverride = pressBrakeFeature->bendRadiusOverride();
    CHECK_PTR(pressBrakeRadiusOverride);
    app->log(
        "Created press brake cosmetic bend: " + pressBrakeFeature->name() +
        ", angle=" + std::to_string(pressBrakeFeature->angle()->value()) +
        " rad, radius=" + std::to_string(pressBrakeRadiusOverride->bendRadius()->value()) + " cm");

     Example 2: Create a crease cosmetic bend (sharp fold, no radius/angle)
    cbFeatures = features->cosmeticBendFeatures();
    CHECK_PTR(cbFeatures);

    auto inp2 = cbFeatures->createInput(line2, CosmeticBendTypes::CreaseCosmeticBendType);
    CHECK_PTR(inp2);
    CHECK(inp2->notes(CREASE_NOTES));

    auto creaseFeature = cbFeatures->add(inp2);
    CHECK_PTR(creaseFeature);
    app->log("Created crease cosmetic bend: " + creaseFeature->name());

    app->log("CosmeticBendSample completed successfully.");

    return true;
}

#undef CHECK_PTR
#undef CHECK
/**
 * Cosmetic Bend API sample — demonstrates creating sheet metal cosmetic bend features in a flat
 * pattern: a press brake bend with an explicit radius override and custom angle, and a crease
 * bend with manufacturing notes.
 *
 * Builds its own flat, sheet-metal plate and flat pattern in a new document, so it can be run
 * as-is without any external data file.
 */

import { adsk } from '@adsk/fusion';

const PLATE_SIZE_CM = 10.0;
const PRESS_BRAKE_ANGLE_RADIANS = Math.PI / 3; // 60 degrees
const PRESS_BRAKE_RADIUS_CM = 0.2; // explicit radius override
const PRESS_BRAKE_NOTES = 'Press brake bend: explicit 0.2 cm radius override';
const CREASE_NOTES = 'Crease: sharp fold, no radius';

// Extrudes a flat plate, converts it to sheet metal, and creates its flat pattern. The flat
// pattern's own Sketches/Features live on a FlatPatternComponent, reached through the
// FlatPatternProduct in the document that matches the new flat pattern's entity token.
function createFlatPatternComponent(
  design: adsk.fusion.Design): { fpComponent: adsk.fusion.FlatPatternComponent; topFace: adsk.fusion.BRepFace } {
  const rootComp = design.rootComponent;

  // Use the first library sheet metal rule's own thickness for the plate, so the extruded body
  // is guaranteed to match the rule convertToSheetMetal() is given below -- no need to probe the
  // body's thickness back out after the fact.
  const libraryRules = design.librarySheetMetalRules;
  if (libraryRules.count === 0) throw Error('No sheet metal rules available in the design.');
  const rule = libraryRules.item(0);
  const thickness = rule.thickness.value;

  const sketch = rootComp.sketches.add(rootComp.xYConstructionPlane);
  sketch.sketchCurves.sketchLines.addTwoPointRectangle(
    adsk.core.Point3D.create(0, 0, 0), adsk.core.Point3D.create(PLATE_SIZE_CM, PLATE_SIZE_CM, 0));
  const prof = sketch.profiles.item(0);

  const extrudes = rootComp.features.extrudeFeatures;
  const extInput = extrudes.createInput(prof, adsk.fusion.FeatureOperations.NewBodyFeatureOperation);
  extInput.setDistanceExtent(false, adsk.core.ValueInput.createByReal(thickness));
  extInput.isSolid = true;
  const ext = extrudes.add(extInput);
  const body = ext.bodies.item(0);

  // Find the largest planar face -- this becomes the sheet metal body's stationary face.
  let largestFace: adsk.fusion.BRepFace | null = null;
  let maxArea = 0.0;
  for (let i = 0; i < body.faces.count; i++) {
    const face = body.faces.item(i);
    if (face && face.geometry.surfaceType === adsk.core.SurfaceTypes.PlaneSurfaceType && face.area > maxArea) {
      maxArea = face.area;
      largestFace = face;
    }
  }
  if (!largestFace) throw Error('No planar face found on the extruded body.');

  if (!body.convertToSheetMetal(largestFace, rule)) throw Error('convertToSheetMetal() failed.');

  const flatPattern = rootComp.createFlatPattern(largestFace);
  if (!flatPattern) throw Error('createFlatPattern() returned null.');

  const products = design.parentDocument.products;
  for (let i = 0; i < products.count; i++) {
    const candidate = products.item(i) as adsk.fusion.FlatPatternProduct;
    if (candidate && candidate.flatPattern && candidate.flatPattern.entityToken === flatPattern.entityToken) {
      const fpComponent = candidate.rootComponent as adsk.fusion.FlatPatternComponent;
      if (!fpComponent) throw Error('FlatPatternProduct.rootComponent is not a FlatPatternComponent.');
      return { fpComponent, topFace: flatPattern.topFace };
    }
  }

  throw Error('Could not find the FlatPatternComponent for the new flat pattern in document.products.');
}

function run() {

  // Get the Fusion API's application object
  const app = adsk.core.Application.get();
  if (!app) throw Error('No adsk.core.Application.');

  app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType);
  const design = app.activeProduct as adsk.fusion.Design;
  if (!design) throw Error('No active Design.');

  const { fpComponent, topFace } = createFlatPatternComponent(design);

  // Measure the face's true border in ITS OWN plane, not world X/Y -- createFlatPattern() can lay
  // the flat pattern body out at an arbitrary orientation, so a world-axis-aligned bounding box
  // can cut corners off the actual (rotated) rectangle. Project every vertex onto the face's own
  // uDirection/vDirection basis instead; that basis is exactly aligned with the face's real edges,
  // so a bounding box in that space is a tight fit regardless of the face's orientation.
  const plane = topFace.geometry as adsk.core.Plane;
  if (!plane) throw Error('Top face geometry is not a Plane.');
  const origin = plane.origin;
  const uDir = plane.uDirection;
  const vDir = plane.vDirection;

  let minU = 0, maxU = 0, minV = 0, maxV = 0;
  let haveBounds = false;
  for (let i = 0; i < topFace.vertices.count; i++) {
    const pt = topFace.vertices.item(i).geometry;
    const relX = pt.x - origin.x, relY = pt.y - origin.y, relZ = pt.z - origin.z;
    const u = relX * uDir.x + relY * uDir.y + relZ * uDir.z;
    const v = relX * vDir.x + relY * vDir.y + relZ * vDir.z;
    if (!haveBounds) {
      minU = maxU = u;
      minV = maxV = v;
      haveBounds = true;
    } else {
      minU = Math.min(minU, u); maxU = Math.max(maxU, u);
      minV = Math.min(minV, v); maxV = Math.max(maxV, v);
    }
  }
  if (!haveBounds) throw Error('Top face has no vertices.');

  const fpSketch = fpComponent.sketches.add(topFace);

  // addByTwoPoints expects points in the SKETCH's own local coordinate space, not model space --
  // build each point in model space from the (u, v) face-plane basis, then explicitly convert via
  // modelToSketchSpace() before handing it to the sketch curve creator.
  const pointAtUV = (u: number, v: number): adsk.core.Point3D => {
    const modelPoint = adsk.core.Point3D.create(
      origin.x + u * uDir.x + v * vDir.x,
      origin.y + u * uDir.y + v * vDir.y,
      origin.z + u * uDir.z + v * vDir.z);
    return fpSketch.modelToSketchSpace(modelPoint);
  };

  const lines = fpSketch.sketchCurves.sketchLines;
  // Two bend lines placed at 30% and 70% across the face, each spanning border to border.
  const line1U = minU + (maxU - minU) * 0.3;
  const line2U = minU + (maxU - minU) * 0.7;
  const line1 = lines.addByTwoPoints(pointAtUV(line1U, minV), pointAtUV(line1U, maxV));
  const line2 = lines.addByTwoPoints(pointAtUV(line2U, minV), pointAtUV(line2U, maxV));

  const features = fpComponent.features;

  // Example 1: Create a press brake cosmetic bend with an explicit radius override
  let cbFeatures = features.cosmeticBendFeatures;
  const angleInput = adsk.core.ValueInput.createByReal(PRESS_BRAKE_ANGLE_RADIANS);
  const inp1 = cbFeatures.createInput(line1, adsk.fusion.CosmeticBendTypes.PressBrakeCosmeticBendType, angleInput);
  if (!inp1) throw Error('createInput() for the first line returned null.');

  const radiusInput = adsk.core.ValueInput.createByReal(PRESS_BRAKE_RADIUS_CM);
  if (!inp1.setToPressBrake(angleInput)) throw Error('setToPressBrake() failed.');
  const inp1RadiusOverride = inp1.bendRadiusOverride;
  if (!inp1RadiusOverride) throw Error('bendRadiusOverride() returned null.');
  if (!inp1RadiusOverride.setOverride(radiusInput)) throw Error('setOverride() failed.');
  inp1.notes = PRESS_BRAKE_NOTES;

  const pressBrakeFeature = cbFeatures.add(inp1);
  if (!pressBrakeFeature) throw Error('add() for the press brake bend returned null.');
  const pressBrakeRadiusOverride = pressBrakeFeature.bendRadiusOverride;
  if (!pressBrakeRadiusOverride) throw Error('bendRadiusOverride() on feature returned null.');
  adsk.log(`Created press brake cosmetic bend: ${pressBrakeFeature.name}` +
    `, angle=${pressBrakeFeature.angle.value} rad, radius=${pressBrakeRadiusOverride.bendRadius.value} cm`);

  // Example 2: Create a crease cosmetic bend (sharp fold, no radius/angle)
  cbFeatures = features.cosmeticBendFeatures;
  const inp2 = cbFeatures.createInput(line2, adsk.fusion.CosmeticBendTypes.CreaseCosmeticBendType);
  if (!inp2) throw Error('createInput() for the second line returned null.');
  inp2.notes = CREASE_NOTES;

  const creaseFeature = cbFeatures.add(inp2);
  if (!creaseFeature) throw Error('add() for the crease bend returned null.');
  adsk.log(`Created crease cosmetic bend: ${creaseFeature.name}`);

  adsk.log('CosmeticBendSample completed successfully.');
}

run();