Files
test/source/blender/blenkernel/intern/curve_bevel.cc
Campbell Barton e955c94ed3 License Headers: Set copyright to "Blender Authors", add AUTHORS
Listing the "Blender Foundation" as copyright holder implied the Blender
Foundation holds copyright to files which may include work from many
developers.

While keeping copyright on headers makes sense for isolated libraries,
Blender's own code may be refactored or moved between files in a way
that makes the per file copyright holders less meaningful.

Copyright references to the "Blender Foundation" have been replaced with
"Blender Authors", with the exception of `./extern/` since these this
contains libraries which are more isolated, any changed to license
headers there can be handled on a case-by-case basis.

Some directories in `./intern/` have also been excluded:

- `./intern/cycles/` it's own `AUTHORS` file is planned.
- `./intern/opensubdiv/`.

An "AUTHORS" file has been added, using the chromium projects authors
file as a template.

Design task: #110784

Ref !110783.
2023-08-16 00:20:26 +10:00

312 lines
9.1 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Handle curve object data bevel options,
* both extruding
*/
#include <cstring>
#include "BLI_alloca.h"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "MEM_guardedalloc.h"
#include "DNA_curve_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_object_types.h"
#include "BKE_curve.h"
#include "BKE_curveprofile.h"
#include "BKE_displist.h"
enum CurveBevelFillType {
BACK = 0,
FRONT,
HALF,
FULL,
};
static CurveBevelFillType curve_bevel_get_fill_type(const Curve *curve)
{
if (!(curve->flag & (CU_FRONT | CU_BACK))) {
return FULL;
}
if ((curve->flag & CU_FRONT) && (curve->flag & CU_BACK)) {
return HALF;
}
return (curve->flag & CU_FRONT) ? FRONT : BACK;
}
static void bevel_quarter_fill(const Curve *curve,
float *quarter_coords_x,
float *quarter_coords_y)
{
if (curve->bevel_mode == CU_BEV_MODE_ROUND) {
float angle = 0.0f;
const float dangle = float(M_PI_2) / (curve->bevresol + 1);
for (int i = 0; i < curve->bevresol + 1; i++) {
quarter_coords_x[i] = float(cosf(angle) * (curve->bevel_radius));
quarter_coords_y[i] = float(sinf(angle) * (curve->bevel_radius));
angle += dangle;
}
}
else {
/* The curve profile evaluation should be done when the resolution is set. */
BLI_assert(curve->bevel_profile->segments != nullptr);
BLI_assert(curve->bevel_profile->segments_len == curve->bevresol + 1);
/* If there aren't enough samples, the curveprofile won't
* sample the start vertex, so set it manually instead. */
quarter_coords_x[0] = curve->bevel_radius;
quarter_coords_y[0] = 0.0f;
for (int i = 1; i < curve->bevresol + 1; i++) {
quarter_coords_x[i] = float(curve->bevel_profile->segments[i].x * (curve->bevel_radius));
quarter_coords_y[i] = float(curve->bevel_profile->segments[i].y * (curve->bevel_radius));
}
}
}
static void curve_bevel_make_extrude_and_fill(const Curve *cu,
ListBase *disp,
const bool use_extrude,
const CurveBevelFillType fill_type)
{
DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
/* Calculate the profile of the bevel once to reuse it for each quarter. We will need
* to flip around the indices for every other section in order to build around the circle
* in a consistent direction.
*
* These should be small enough for stack allocations because the current limit
* for #Curve.bevresol is 32. */
float *quarter_coords_x = static_cast<float *>(alloca(sizeof(float) * (cu->bevresol + 1)));
float *quarter_coords_y = static_cast<float *>(alloca(sizeof(float) * (cu->bevresol + 1)));
bevel_quarter_fill(cu, quarter_coords_x, quarter_coords_y);
int nr;
if (fill_type == FULL) {
/* The full loop. */
nr = 4 * cu->bevresol + (use_extrude ? 6 : 4);
dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
}
else if (fill_type == HALF) {
/* Half the loop. */
nr = 2 * (cu->bevresol + 1) + (use_extrude ? 2 : 1);
dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
}
else {
/* One quarter of the loop (just front or back). */
nr = use_extrude ? cu->bevresol + 3 : cu->bevresol + 2;
dl->flag = (fill_type == FRONT) ? DL_FRONT_CURVE : DL_BACK_CURVE;
}
dl->verts = static_cast<float *>(MEM_malloc_arrayN(nr, sizeof(float[3]), __func__));
BLI_addtail(disp, dl);
/* Use a different type depending on whether the loop is complete or not. */
dl->type = (fill_type == FULL) ? DL_POLY : DL_SEGM;
dl->parts = 1;
dl->nr = nr;
float *fp = dl->verts;
/* Build the back section. */
if (ELEM(fill_type, BACK, HALF, FULL)) {
/* Add the bottom vertex. */
fp[0] = 0.0f;
fp[1] = 0.0f;
fp[2] = -cu->extrude - cu->bevel_radius;
fp += 3;
for (int i = cu->bevresol; i >= 0; i--) {
fp[0] = 0.0f;
fp[1] = quarter_coords_x[i];
fp[2] = -quarter_coords_y[i] - cu->extrude;
fp += 3;
}
}
/* Add the extrusion if we're only building either the back or the front. */
if (use_extrude && ELEM(fill_type, FRONT, BACK)) {
fp[0] = 0.0f;
fp[1] = cu->bevel_radius;
fp[2] = (fill_type == FRONT) ? -cu->extrude : cu->extrude;
fp += 3;
}
/* Build the front section. */
if (ELEM(fill_type, FRONT, HALF, FULL)) {
/* Don't duplicate the last back vertex. */
const int front_start = (!use_extrude && ELEM(fill_type, HALF, FULL)) ? 1 : 0;
for (int i = front_start; i < cu->bevresol + 1; i++) {
fp[0] = 0.0f;
fp[1] = quarter_coords_x[i];
fp[2] = quarter_coords_y[i] + cu->extrude;
fp += 3;
}
/* Add the top vertex. */
fp[0] = 0.0f;
fp[1] = 0.0f;
fp[2] = cu->extrude + cu->bevel_radius;
fp += 3;
}
/* Build the other half only if we're building the full loop. */
if (fill_type == FULL) {
for (int i = cu->bevresol; i > 0; i--) {
fp[0] = 0.0f;
fp[1] = -quarter_coords_x[i];
fp[2] = quarter_coords_y[i] + cu->extrude;
fp += 3;
}
if (use_extrude) {
/* Add the extrusion. */
fp[0] = 0.0f;
fp[1] = -cu->bevel_radius;
fp[2] = cu->extrude;
fp += 3;
}
for (int i = 0; i < cu->bevresol + 1; i++) {
fp[0] = 0.0f;
fp[1] = -quarter_coords_x[i];
fp[2] = -quarter_coords_y[i] - cu->extrude;
fp += 3;
}
}
}
static void curve_bevel_make_full_circle(const Curve *cu, ListBase *disp)
{
const int nr = 4 + 2 * cu->bevresol;
DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
dl->verts = static_cast<float *>(MEM_malloc_arrayN(nr, sizeof(float[3]), __func__));
BLI_addtail(disp, dl);
dl->type = DL_POLY;
dl->parts = 1;
dl->flag = DL_BACK_CURVE;
dl->nr = nr;
float *fp = dl->verts;
const float dangle = (2.0f * float(M_PI) / (nr));
float angle = -(nr - 1) * dangle;
for (int i = 0; i < nr; i++) {
fp[0] = 0.0;
fp[1] = (cosf(angle) * (cu->bevel_radius));
fp[2] = (sinf(angle) * (cu->bevel_radius)) - cu->extrude;
angle += dangle;
fp += 3;
}
}
static void curve_bevel_make_only_extrude(const Curve *cu, ListBase *disp)
{
DispList *dl = static_cast<DispList *>(MEM_callocN(sizeof(DispList), __func__));
dl->verts = static_cast<float *>(MEM_malloc_arrayN(2, sizeof(float[3]), __func__));
BLI_addtail(disp, dl);
dl->type = DL_SEGM;
dl->parts = 1;
dl->flag = DL_FRONT_CURVE | DL_BACK_CURVE;
dl->nr = 2;
float *fp = dl->verts;
fp[0] = fp[1] = 0.0;
fp[2] = -cu->extrude;
fp[3] = fp[4] = 0.0;
fp[5] = cu->extrude;
}
static void curve_bevel_make_from_object(const Curve *cu, ListBase *disp)
{
if (cu->bevobj == nullptr) {
return;
}
if (cu->bevobj->type != OB_CURVES_LEGACY) {
return;
}
Curve *bevcu = static_cast<Curve *>(cu->bevobj->data);
if (bevcu->extrude == 0.0f && bevcu->bevel_radius == 0.0f) {
ListBase bevdisp = {nullptr, nullptr};
float facx = cu->bevobj->scale[0];
float facy = cu->bevobj->scale[1];
DispList *dl;
if (cu->bevobj->runtime.curve_cache) {
dl = static_cast<DispList *>(cu->bevobj->runtime.curve_cache->disp.first);
}
else {
BLI_assert(cu->bevobj->runtime.curve_cache != nullptr);
dl = nullptr;
}
while (dl) {
if (ELEM(dl->type, DL_POLY, DL_SEGM)) {
DispList *dlnew = static_cast<DispList *>(MEM_mallocN(sizeof(DispList), __func__));
*dlnew = *dl;
dlnew->verts = static_cast<float *>(
MEM_malloc_arrayN(dl->parts * dl->nr, sizeof(float[3]), __func__));
memcpy(dlnew->verts, dl->verts, sizeof(float[3]) * dl->parts * dl->nr);
if (dlnew->type == DL_SEGM) {
dlnew->flag |= (DL_FRONT_CURVE | DL_BACK_CURVE);
}
BLI_addtail(disp, dlnew);
float *fp = dlnew->verts;
int nr = dlnew->parts * dlnew->nr;
while (nr--) {
fp[2] = fp[1] * facy;
fp[1] = -fp[0] * facx;
fp[0] = 0.0;
fp += 3;
}
}
dl = dl->next;
}
BKE_displist_free(&bevdisp);
}
}
ListBase BKE_curve_bevel_make(const Curve *curve)
{
ListBase bevel_shape = {nullptr, nullptr};
if (curve->bevel_mode == CU_BEV_MODE_OBJECT) {
if (curve->bevobj != nullptr) {
curve_bevel_make_from_object(curve, &bevel_shape);
}
}
else {
const bool use_extrude = curve->extrude != 0.0f;
const bool use_bevel = curve->bevel_radius != 0.0f;
/* Pass. */
if (use_extrude && !use_bevel) {
curve_bevel_make_only_extrude(curve, &bevel_shape);
}
else if (use_extrude || use_bevel) {
CurveBevelFillType fill_type = curve_bevel_get_fill_type(curve);
if (!use_extrude && fill_type == FULL && curve->bevel_mode == CU_BEV_MODE_ROUND) {
curve_bevel_make_full_circle(curve, &bevel_shape);
}
else {
/* The general case for nonzero extrusion or an incomplete loop. */
curve_bevel_make_extrude_and_fill(curve, &bevel_shape, use_extrude, fill_type);
}
}
}
return bevel_shape;
}