diff options
author | Joshua Leung <aligorith@gmail.com> | 2009-03-21 06:49:22 +0300 |
---|---|---|
committer | Joshua Leung <aligorith@gmail.com> | 2009-03-21 06:49:22 +0300 |
commit | b4209c56565660c20718fc2e1ad74d4257683a3e (patch) | |
tree | 122710c4991e46fcdbfda575b517d8cff827da50 /source | |
parent | 90a58790b24a7e0303f890f054ae7f730e242491 (diff) |
F-Curve Modifiers: Envelope Modifier
Got the basic envelope modifier code working, including primitive drawing of relevant helper info in the graph view. It doesn't work in a very intuitive way yet, so I will recode it soon.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/blenkernel/intern/fcurve.c | 59 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readfile.c | 7 | ||||
-rw-r--r-- | source/blender/blenloader/intern/writefile.c | 11 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_buttons.c | 245 | ||||
-rw-r--r-- | source/blender/editors/space_graph/graph_draw.c | 68 |
5 files changed, 358 insertions, 32 deletions
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 72321eb7292..f58616cdf92 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -1384,21 +1384,40 @@ static FModifierTypeInfo FMI_GENERATOR = { static void fcm_envelope_free (FModifier *fcm) { - FMod_Envelope *data= (FMod_Envelope *)fcm->data; + FMod_Envelope *env= (FMod_Envelope *)fcm->data; /* free envelope data array */ - if (data->data) - MEM_freeN(data->data); + if (env->data) + MEM_freeN(env->data); } static void fcm_envelope_copy (FModifier *fcm, FModifier *src) { - FMod_Envelope *gen= (FMod_Envelope *)fcm->data; - FMod_Envelope *ogen= (FMod_Envelope *)src->data; + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FMod_Envelope *oenv= (FMod_Envelope *)src->data; /* copy envelope data array */ - if (ogen->data) - gen->data= MEM_dupallocN(ogen->data); + if (oenv->data) + env->data= MEM_dupallocN(oenv->data); +} + +static void fcm_envelope_new_data (void *mdata) +{ + FMod_Envelope *env= (FMod_Envelope *)mdata; + + /* set default min/max ranges */ + env->min= -1.0f; + env->max= 1.0f; +} + +static void fcm_envelope_verify (FModifier *fcm) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + + /* if the are points, perform bubble-sort on them, as user may have changed the order */ + if (env->data) { + // XXX todo... + } } static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, float evaltime) @@ -1412,7 +1431,7 @@ static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, f if (env->data == NULL) return; prevfed= env->data; fed= prevfed + 1; - lastfed= prevfed + env->totvert-1; + lastfed= prevfed + (env->totvert-1); /* get min/max values for envelope at evaluation time (relative to mid-value) */ if (prevfed->time >= evaltime) { @@ -1434,10 +1453,10 @@ static void fcm_envelope_evaluate (FCurve *fcu, FModifier *fcm, float *cvalue, f diff= fed->time - prevfed->time; afac= (evaltime - prevfed->time) / diff; - bfac= (fed->time - evaltime)/(diff); + bfac= (fed->time - evaltime) / diff; - min= afac*prevfed->min + bfac*fed->min; - max= afac*prevfed->max + bfac*fed->max; + min= bfac*prevfed->min + afac*fed->min; + max= bfac*prevfed->max + afac*fed->max; break; } @@ -1460,8 +1479,8 @@ static FModifierTypeInfo FMI_ENVELOPE = { "FMod_Envelope", /* struct name */ fcm_envelope_free, /* free data */ fcm_envelope_copy, /* copy data */ - NULL, /* new data */ - NULL /*fcm_envelope_verify*/, /* verify */ + fcm_envelope_new_data, /* new data */ + fcm_envelope_verify, /* verify */ fcm_envelope_evaluate /* evaluate */ }; @@ -1831,11 +1850,13 @@ void fcurve_remove_modifier (FCurve *fcu, FModifier *fcm) return; /* free modifier's special data (stored inside fcm->data) */ - if (fmi && fmi->free_data) - fmi->free_data(fcm); - - /* free modifier's data (fcm->data) */ - MEM_freeN(fcm->data); + if (fcm->data) { + if (fmi && fmi->free_data) + fmi->free_data(fcm); + + /* free modifier's data (fcm->data) */ + MEM_freeN(fcm->data); + } /* remove modifier from stack */ if (fcu) @@ -1917,7 +1938,7 @@ void fcurve_set_active_modifier (FCurve *fcu, FModifier *fcm) /* sanity checks */ if ELEM(NULL, fcu, fcu->modifiers.first) - return NULL; + return; /* deactivate all, and set current one active */ for (fm= fcu->modifiers.first; fm; fm= fm->next) diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index d64abd99d9c..046618a1f6d 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1728,6 +1728,13 @@ static void direct_link_fcurves(FileData *fd, ListBase *list) data->coefficients= newdataadr(fd, data->coefficients); } break; + case FMODIFIER_TYPE_ENVELOPE: + { + FMod_Envelope *data= (FMod_Envelope *)fcm->data; + + data->data= newdataadr(fd, data->data); + } + break; case FMODIFIER_TYPE_PYTHON: { FMod_Python *data= (FMod_Python *)fcm->data; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index f898a363da7..2aa87883fdb 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -810,6 +810,15 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves) writedata(wd, DATA, sizeof(float)*(data->arraysize), data->coefficients); } break; + case FMODIFIER_TYPE_ENVELOPE: + { + FMod_Envelope *data= (FMod_Envelope *)fcm->data; + + /* write envelope data */ + if (data->data) + writedata(wd, DATA, sizeof(FCM_EnvelopeData)*(data->totvert), data->data); + } + break; case FMODIFIER_TYPE_PYTHON: { FMod_Python *data = (FMod_Python *)fcm->data; @@ -822,7 +831,7 @@ static void write_fcurves(WriteData *wd, ListBase *fcurves) } } - /* Write the constraint */ + /* Write the modifier */ writestruct(wd, DATA, "FModifier", 1, fcm); } } diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index cfb143b407b..7892fe029b6 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -133,7 +133,7 @@ static void graph_panel_properties(const bContext *C, ARegion *ar, short cntrl, */ } -/* -------------- */ +/* ******************* drivers ******************************** */ #define B_IPO_DEPCHANGE 10 @@ -217,7 +217,7 @@ static void graph_panel_drivers(const bContext *C, ARegion *ar, short cntrl, bAn } } -/* -------------- */ +/* ******************* f-modifiers ******************************** */ #define B_FMODIFIER_REDRAW 20 @@ -262,7 +262,6 @@ static void activate_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) fcurve_set_active_modifier(fcu, fcm); } - /* callback to remove the given modifier */ static void delete_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) { @@ -272,6 +271,8 @@ static void delete_fmodifier_cb (bContext *C, void *fcu_v, void *fcm_v) /* remove the given F-Modifier from the F-Curve */ fcurve_remove_modifier(fcu, fcm); } + +/* --------------- */ /* draw settings for generator modifier */ static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) @@ -449,7 +450,9 @@ static void draw_modifier__generator(uiBlock *block, FCurve *fcu, FModifier *fcm } } -/* draw settings for generator modifier */ +/* --------------- */ + +/* draw settings for cycles modifier */ static void draw_modifier__cycles(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) { FMod_Cycles *data= (FMod_Cycles *)fcm->data; @@ -477,6 +480,223 @@ static void draw_modifier__cycles(uiBlock *block, FCurve *fcu, FModifier *fcm, i uiBlockEndAlign(block); } +/* --------------- */ + +#define BINARYSEARCH_FRAMEEQ_THRESH 0.0001 + +/* Binary search algorithm for finding where to insert Envelope Data Point. + * Returns the index to insert at (data already at that index will be offset if replace is 0) + */ +static int binarysearch_fcm_envelopedata_index (FCM_EnvelopeData array[], float frame, int arraylen, short *exists) +{ + int start=0, end=arraylen; + int loopbreaker= 0, maxloop= arraylen * 2; + + /* initialise exists-flag first */ + *exists= 0; + + /* sneaky optimisations (don't go through searching process if...): + * - keyframe to be added is to be added out of current bounds + * - keyframe to be added would replace one of the existing ones on bounds + */ + if ((arraylen <= 0) || (array == NULL)) { + printf("Warning: binarysearch_fcm_envelopedata_index() encountered invalid array \n"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Point (when only one point, this case is used) */ + framenum= array[0].time; + if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists = 1; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Point */ + framenum= array[(arraylen-1)].time; + if (IS_EQT(frame, framenum, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists= 1; + return (arraylen - 1); + } + else if (frame > framenum) + return arraylen; + } + + + /* most of the time, this loop is just to find where to put it + * - 'loopbreaker' is just here to prevent infinite loops + */ + for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { + /* compute and get midpoint */ + int mid = start + ((end - start) / 2); /* we calculate the midpoint this way to avoid int overflows... */ + float midfra= array[mid].time; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, BINARYSEARCH_FRAMEEQ_THRESH)) { + *exists = 1; + return mid; + } + + /* repeat in upper/lower half */ + if (frame > midfra) + start= mid + 1; + else if (frame < midfra) + end= mid - 1; + } + + /* print error if loop-limit exceeded */ + if (loopbreaker == (maxloop-1)) { + printf("Error: binarysearch_fcm_envelopedata_index() was taking too long \n"); + + // include debug info + printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen); + } + + /* not found, so return where to place it */ + return start; +} + +/* callback to add new envelope data point */ +// TODO: should we have a separate file for things like this? +static void fmod_envelope_addpoint_cb (bContext *C, void *fcm_dv, void *dummy) +{ + Scene *scene= CTX_data_scene(C); + FMod_Envelope *env= (FMod_Envelope *)fcm_dv; + FCM_EnvelopeData *fedn; + FCM_EnvelopeData fed; + + /* init template data */ + fed.min= -1.0f; + fed.max= 1.0f; + fed.time= (float)scene->r.cfra; // XXX make this int for ease of use? + fed.f1= fed.f2= 0; + + /* check that no data exists for the current frame... */ + if (env->data) { + short exists = -1; + int i= binarysearch_fcm_envelopedata_index(env->data, (float)(scene->r.cfra), env->totvert, &exists); + + /* binarysearch_...() will set exists by default to 0, so if it is non-zero, that means that the point exists already */ + if (exists) + return; + + /* add new */ + fedn= MEM_callocN((env->totvert+1)*sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); + + /* add the points that should occur before the point to be pasted */ + if (i > 0) + memcpy(fedn, env->data, i*sizeof(FCM_EnvelopeData)); + + /* add point to paste at index i */ + *(fedn + i)= fed; + + /* add the points that occur after the point to be pasted */ + if (i < env->totvert) + memcpy(fedn+i+1, env->data+i, (env->totvert-i)*sizeof(FCM_EnvelopeData)); + + /* replace (+ free) old with new */ + MEM_freeN(env->data); + env->data= fedn; + + env->totvert++; + } + else { + env->data= MEM_callocN(sizeof(FCM_EnvelopeData), "FCM_EnvelopeData"); + *(env->data)= fed; + + env->totvert= 1; + } +} + +/* callback to remove envelope data point */ +// TODO: should we have a separate file for things like this? +static void fmod_envelope_deletepoint_cb (bContext *C, void *fcm_dv, void *ind_v) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm_dv; + FCM_EnvelopeData *fedn; + int index= GET_INT_FROM_POINTER(ind_v); + + /* check that no data exists for the current frame... */ + if (env->totvert > 1) { + /* allocate a new smaller array */ + fedn= MEM_callocN(sizeof(FCM_EnvelopeData)*(env->totvert-1), "FCM_EnvelopeData"); + + memcpy(fedn, &env->data, sizeof(FCM_EnvelopeData)*(index)); + memcpy(&fedn[index], &env->data[index+1], sizeof(FCM_EnvelopeData)*(env->totvert-index-1)); + + /* free old array, and set the new */ + MEM_freeN(env->data); + env->data= fedn; + env->totvert--; + } + else { + /* just free array, since the only vert was deleted */ + if (env->data) + MEM_freeN(env->data); + env->totvert= 0; + } +} + +/* draw settings for envelope modifier */ +static void draw_modifier__envelope(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco, short *height, short width, short active, int rb_col) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FCM_EnvelopeData *fed; + uiBut *but; + int cy= (*yco - 30); + int i; + + /* set the height: + * - basic settings + variable height from envelope controls + */ + (*height) = 96 + (25 * env->totvert); + + /* basic settings (backdrop + general settings + some padding) */ + //DRAW_BACKDROP((*height)); // XXX buggy... + + /* General Settings */ + uiDefBut(block, LABEL, 1, "Envelope:", 10, cy, 100, 20, NULL, 0.0, 0.0, 0, 0, "Settings for cycling before first keyframe"); + cy -= 20; + + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Reference Val:", 10, cy, 300, 20, &env->midval, -FLT_MAX, FLT_MAX, 10, 3, ""); + cy -= 20; + + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 10, cy, 150, 20, &env->min, -FLT_MAX, env->max, 10, 3, "Minimum value (relative to Reference Value) that is used as the 'normal' minimum value"); + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 160, cy, 150, 20, &env->max, env->min, FLT_MAX, 10, 3, "Maximum value (relative to Reference Value) that is used as the 'normal' maximum value"); + cy -= 35; + uiBlockEndAlign(block); + + + /* Points header */ + uiDefBut(block, LABEL, 1, "Control Points:", 10, cy, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + + but= uiDefBut(block, BUT, B_FMODIFIER_REDRAW, "Add Point", 160,cy,150,19, NULL, 0, 0, 0, 0, "Adds a new control-point to the envelope on the current frame"); + uiButSetFunc(but, fmod_envelope_addpoint_cb, env, NULL); + cy -= 35; + + /* Points List */ + for (i=0, fed=env->data; i < env->totvert; i++, fed++) { + uiBlockBeginAlign(block); + but=uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Fra:", 5, cy, 100, 20, &fed->time, -FLT_MAX, FLT_MAX, 10, 3, "Frame that envelope point occurs"); + uiButSetFunc(but, validate_fmodifier_cb, fcu, fcm); + + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Min:", 105, cy, 100, 20, &fed->min, -FLT_MAX, FLT_MAX, 10, 3, "Minimum bound of envelope at this point"); + uiDefButF(block, NUM, B_FMODIFIER_REDRAW, "Max:", 205, cy, 100, 20, &fed->max, -FLT_MAX, FLT_MAX, 10, 3, "Maximum bound of envelope at this point"); + + but= uiDefIconBut(block, BUT, B_FMODIFIER_REDRAW, ICON_X, 305, cy, 20, 20, NULL, 0.0, 0.0, 0.0, 0.0, "Delete envelope control point"); + uiButSetFunc(but, fmod_envelope_deletepoint_cb, env, SET_INT_IN_POINTER(i)); + uiBlockBeginAlign(block); + cy -= 25; + } +} + +/* --------------- */ + static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fcm, int *yco) { FModifierTypeInfo *fmi= fmodifier_get_typeinfo(fcm); @@ -499,14 +719,17 @@ static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fc #endif // XXX buggy /* expand */ - uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT, 10-7, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded"); + uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_EXPANDED, B_REDR, ICON_TRIA_RIGHT, 5, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is expanded"); + + /* checkbox for 'active' status (for now) */ + but= uiDefIconButBitS(block, ICONTOG, FMODIFIER_FLAG_ACTIVE, B_REDR, ICON_RADIOBUT_OFF, 25, *yco-1, 20, 20, &fcm->flag, 0.0, 0.0, 0, 0, "Modifier is active one"); + uiButSetFunc(but, activate_fmodifier_cb, fcu, fcm); /* name */ if (fmi) - but= uiDefBut(block, LABEL, 1, fmi->name, 10+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); + but= uiDefBut(block, LABEL, 1, fmi->name, 10+40, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); else - but= uiDefBut(block, LABEL, 1, "<Unknown Modifier>", 10+35, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); - uiButSetFunc(but, activate_fmodifier_cb, fcu, fcm); + but= uiDefBut(block, LABEL, 1, "<Unknown Modifier>", 10+40, *yco, 240, 20, NULL, 0.0, 0.0, 0, 0, "F-Curve Modifier Type. Click to make modifier active one."); /* delete button */ but= uiDefIconBut(block, BUT, B_REDR, ICON_X, 10+(width-30), *yco, 19, 19, NULL, 0.0, 0.0, 0.0, 0.0, "Delete F-Curve Modifier."); @@ -526,6 +749,10 @@ static void graph_panel_modifier_draw(uiBlock *block, FCurve *fcu, FModifier *fc case FMODIFIER_TYPE_CYCLES: /* Cycles */ draw_modifier__cycles(block, fcu, fcm, yco, &height, width, active, rb_col); break; + + case FMODIFIER_TYPE_ENVELOPE: /* Envelope */ + draw_modifier__envelope(block, fcu, fcm, yco, &height, width, active, rb_col); + break; default: /* unknown type */ height= 96; @@ -566,7 +793,7 @@ static void graph_panel_modifiers(const bContext *C, ARegion *ar, short cntrl, b uiNewPanelHeight(block, 204); } -/* -------------- */ +/* ******************* general ******************************** */ /* Find 'active' F-Curve. It must be editable, since that's the purpose of these buttons (subject to change). * We return the 'wrapper' since it contains valuable context info (about hierarchy), which will need to be freed diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index f7f47760057..dad4a771cd9 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -91,7 +91,59 @@ extern void ui_rasterpos_safe(float x, float y, float aspect); extern void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad); /* *************************** */ -/* Curve Drawing */ +/* F-Curve Modifier Drawing */ + +/* Envelope -------------- */ + +// TODO: draw a shaded poly showing the region of influence too? +static void draw_fcurve_modifier_controls_envelope (FCurve *fcu, FModifier *fcm, View2D *v2d) +{ + FMod_Envelope *env= (FMod_Envelope *)fcm->data; + FCM_EnvelopeData *fed; + const float fac= 0.05f * (v2d->cur.xmax - v2d->cur.xmin); + int i; + + /* draw two black lines showing the standard reference levels */ + // TODO: how to make these more distinctive? + glColor3f(0.0f, 0.0f, 0.0f); + setlinestyle(5); + + glBegin(GL_LINES); + glVertex2f(v2d->cur.xmin, env->midval+env->min); + glVertex2f(v2d->cur.xmax, env->midval+env->min); + + glVertex2f(v2d->cur.xmin, env->midval+env->max); + glVertex2f(v2d->cur.xmax, env->midval+env->max); + glEnd(); // GL_LINES + setlinestyle(0); + + + /* set size of vertices (non-adjustable for now) */ + glPointSize(2.0f); + + // for now, point color is fixed, and is white + glColor3f(1.0f, 1.0f, 1.0f); + + /* we use bgl points not standard gl points, to workaround vertex + * drawing bugs that some drivers have (probably legacy ones only though) + */ + bglBegin(GL_POINTS); + for (i=0, fed=env->data; i < env->totvert; i++, fed++) { + /* only draw if visible + * - min/max here are fixed, not relative + */ + if IN_RANGE(fed->time, (v2d->cur.xmin - fac), (v2d->cur.xmax + fac)) { + glVertex2f(fed->time, fed->min); + glVertex2f(fed->time, fed->max); + } + } + bglEnd(); + + glPointSize(1.0f); +} + +/* *************************** */ +/* F-Curve Drawing */ /* Points ---------------- */ @@ -768,8 +820,18 @@ void graph_draw_curves (bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGri } /* 2) draw handles and vertices as appropriate based on active */ - if ( ((fcm) && (fcm->type != FMODIFIER_TYPE_CYCLES)) || (fcu->modifiers.first) ) { - // TODO: need to add code to show these... for cycles modifier, should fall through though... + if ( ((fcm) && (fcm->type != FMODIFIER_TYPE_CYCLES)) || (fcu->modifiers.first && !fcm) ) { + /* draw controls for the 'active' modifier (if applicable) + * NOTE: cycles modifier is currently an exception where the original points can still be edited, so + * this branch is skipped... (TODO: set up the generic system for this so that we don't need special hacks like this) + */ + if (fcm) { + switch (fcm->type) { + case FMODIFIER_TYPE_ENVELOPE: /* envelope */ + draw_fcurve_modifier_controls_envelope(fcu, fcm, &ar->v2d); + break; + } + } } else if ( ((fcu->bezt) || (fcu->fpt)) && (fcu->totvert) ) { if (fcu->bezt) { |