/** * Subdivide a stroke once, by adding a point half way between each pair of existing points * \param gps: Stroke data * \param new_totpoints: Total number of points (after subdividing) */ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) { /* Move points towards end of enlarged points array to leave space for new points */ int y = 1; for (int i = gps->totpoints - 1; i > 0; i--) { gps->points[new_totpoints - y] = gps->points[i]; y += 2; } /* Create interpolated points */ for (int i = 0; i < new_totpoints - 1; i += 2) { bGPDspoint *prev = &gps->points[i]; bGPDspoint *pt = &gps->points[i + 1]; bGPDspoint *next = &gps->points[i + 2]; /* Interpolate all values */ interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); pt->pressure = interpf(prev->pressure, next->pressure, 0.5f); pt->strength = interpf(prev->strength, next->strength, 0.5f); CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = interpf(prev->time, next->time, 0.5f); } /* Update to new total number of points */ gps->totpoints = new_totpoints; }
/* Evalautes the given set of F-Curve Modifiers using the given data * Should only be called after evaluate_time_fmodifiers() has been called... */ void evaluate_value_fmodifiers(ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime) { FModifier *fcm; /* sanity checks */ if (ELEM(NULL, modifiers, modifiers->first)) return; /* evaluate modifiers */ for (fcm = modifiers->first; fcm; fcm = fcm->next) { FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); if (fmi == NULL) continue; /* only evaluate if there's a callback for this, and if F-Modifier can be evaluated on this frame */ if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime)) ) { if (fmi->evaluate_modifier) { if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) { float influence = eval_fmodifier_influence(fcm, evaltime); float nval = *cvalue; fmi->evaluate_modifier(fcu, fcm, &nval, evaltime); *cvalue = interpf(nval, *cvalue, influence); } } } } }
float data_transfer_interp_float_do( const int mix_mode, const float val_dst, const float val_src, const float mix_factor) { float val_ret; if (((mix_mode == CDT_MIX_REPLACE_ABOVE_THRESHOLD && (val_dst < mix_factor)) || (mix_mode == CDT_MIX_REPLACE_BELOW_THRESHOLD && (val_dst > mix_factor)))) { return val_dst; /* Do not affect destination. */ } switch (mix_mode) { case CDT_MIX_REPLACE_ABOVE_THRESHOLD: case CDT_MIX_REPLACE_BELOW_THRESHOLD: return val_src; case CDT_MIX_MIX: val_ret = (val_dst + val_src) * 0.5f; break; case CDT_MIX_ADD: val_ret = val_dst + val_src; break; case CDT_MIX_SUB: val_ret = val_dst - val_src; break; case CDT_MIX_MUL: val_ret = val_dst * val_src; break; case CDT_MIX_TRANSFER: default: val_ret = val_src; break; } return interpf(val_ret, val_dst, mix_factor); }
static void stroke_elem_interp( struct StrokeElem *selem_out, const struct StrokeElem *selem_a, const struct StrokeElem *selem_b, float t) { interp_v2_v2v2(selem_out->mval, selem_a->mval, selem_b->mval, t); interp_v3_v3v3(selem_out->location_world, selem_a->location_world, selem_b->location_world, t); interp_v3_v3v3(selem_out->location_local, selem_a->location_local, selem_b->location_local, t); selem_out->pressure = interpf(selem_a->pressure, selem_b->pressure, t); }
/* evaluate time modifications imposed by some F-Curve Modifiers * - this step acts as an optimization to prevent the F-Curve stack being evaluated * several times by modifiers requesting the time be modified, as the final result * would have required using the modified time * - modifiers only ever receive the unmodified time, as subsequent modifiers should be * working on the 'global' result of the modified curve, not some localised segment, * so nevaltime gets set to whatever the last time-modifying modifier likes... * - we start from the end of the stack, as only the last one matters for now * * Note: *fcu might be NULL */ float evaluate_time_fmodifiers(FModifiersStackStorage *storage, ListBase *modifiers, FCurve *fcu, float cvalue, float evaltime) { /* sanity checks */ if (ELEM(NULL, modifiers, modifiers->last)) { return evaltime; } if (fcu && fcu->flag & FCURVE_MOD_OFF) { return evaltime; } /* Starting from the end of the stack, calculate the time effects of various stacked modifiers * on the time the F-Curve should be evaluated at. * * This is done in reverse order to standard evaluation, as when this is done in standard * order, each modifier would cause jumps to other points in the curve, forcing all * previous ones to be evaluated again for them to be correct. However, if we did in the * reverse order as we have here, we can consider them a macro to micro type of waterfall * effect, which should get us the desired effects when using layered time manipulations * (such as multiple 'stepped' modifiers in sequence, causing different stepping rates) */ uint fcm_index = storage->modifier_count - 1; for (FModifier *fcm = modifiers->last; fcm; fcm = fcm->prev, fcm_index--) { const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); if (fmi == NULL) { continue; } /* If modifier cannot be applied on this frame * (whatever scale it is on, it won't affect the results) * hence we shouldn't bother seeing what it would do given the chance. */ if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime))) { /* only evaluate if there's a callback for this */ if (fmi->evaluate_modifier_time) { if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) { void *storage_ptr = POINTER_OFFSET(storage->buffer, fcm_index * storage->size_per_modifier); float nval = fmi->evaluate_modifier_time(fcu, fcm, cvalue, evaltime, storage_ptr); float influence = eval_fmodifier_influence(fcm, evaltime); evaltime = interpf(nval, evaltime, influence); } } } } /* return the modified evaltime */ return evaltime; }
/* Evaluates the given set of F-Curve Modifiers using the given data * Should only be called after evaluate_time_fmodifiers() has been called... */ void evaluate_value_fmodifiers(FModifiersStackStorage *storage, ListBase *modifiers, FCurve *fcu, float *cvalue, float evaltime) { FModifier *fcm; /* sanity checks */ if (ELEM(NULL, modifiers, modifiers->first)) { return; } if (fcu->flag & FCURVE_MOD_OFF) { return; } /* evaluate modifiers */ uint fcm_index = 0; for (fcm = modifiers->first; fcm; fcm = fcm->next, fcm_index++) { const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); if (fmi == NULL) { continue; } /* Only evaluate if there's a callback for this, * and if F-Modifier can be evaluated on this frame. */ if ((fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) == 0 || ((fcm->sfra <= evaltime) && (fcm->efra >= evaltime))) { if (fmi->evaluate_modifier) { if ((fcm->flag & (FMODIFIER_FLAG_DISABLED | FMODIFIER_FLAG_MUTED)) == 0) { void *storage_ptr = POINTER_OFFSET(storage->buffer, fcm_index * storage->size_per_modifier); float nval = *cvalue; fmi->evaluate_modifier(fcu, fcm, &nval, evaltime, storage_ptr); float influence = eval_fmodifier_influence(fcm, evaltime); *cvalue = interpf(nval, *cvalue, influence); } } } } }
/* Evaluate spline IK for a given bone */ static void splineik_evaluate_bone(tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan, int index, float ctime) { bSplineIKConstraint *ikData = tree->ikData; float poseHead[3], poseTail[3], poseMat[4][4]; float splineVec[3], scaleFac, radius = 1.0f; /* firstly, calculate the bone matrix the standard way, since this is needed for roll control */ BKE_pose_where_is_bone(scene, ob, pchan, ctime, 1); copy_v3_v3(poseHead, pchan->pose_head); copy_v3_v3(poseTail, pchan->pose_tail); /* step 1: determine the positions for the endpoints of the bone */ { float vec[4], dir[3], rad; float tailBlendFac = 1.0f; /* determine if the bone should still be affected by SplineIK */ if (tree->points[index + 1] >= 1.0f) { /* spline doesn't affect the bone anymore, so done... */ pchan->flag |= POSE_DONE; return; } else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) { /* blending factor depends on the amount of the bone still left on the chain */ tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]); } /* tail endpoint */ if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) { /* apply curve's object-mode transforms to the position * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) */ if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) mul_m4_v3(ikData->tar->obmat, vec); /* convert the position to pose-space, then store it */ mul_m4_v3(ob->imat, vec); interp_v3_v3v3(poseTail, pchan->pose_tail, vec, tailBlendFac); /* set the new radius */ radius = rad; } /* head endpoint */ if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) { /* apply curve's object-mode transforms to the position * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root) */ if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) == 0) mul_m4_v3(ikData->tar->obmat, vec); /* store the position, and convert it to pose space */ mul_m4_v3(ob->imat, vec); copy_v3_v3(poseHead, vec); /* set the new radius (it should be the average value) */ radius = (radius + rad) / 2; } } /* step 2: determine the implied transform from these endpoints * - splineVec: the vector direction that the spline applies on the bone * - scaleFac: the factor that the bone length is scaled by to get the desired amount */ sub_v3_v3v3(splineVec, poseTail, poseHead); scaleFac = len_v3(splineVec) / pchan->bone->length; /* step 3: compute the shortest rotation needed to map from the bone rotation to the current axis * - this uses the same method as is used for the Damped Track Constraint (see the code there for details) */ { float dmat[3][3], rmat[3][3], tmat[3][3]; float raxis[3], rangle; /* compute the raw rotation matrix from the bone's current matrix by extracting only the * orientation-relevant axes, and normalizing them */ copy_v3_v3(rmat[0], pchan->pose_mat[0]); copy_v3_v3(rmat[1], pchan->pose_mat[1]); copy_v3_v3(rmat[2], pchan->pose_mat[2]); normalize_m3(rmat); /* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */ normalize_v3(splineVec); /* calculate smallest axis-angle rotation necessary for getting from the * current orientation of the bone, to the spline-imposed direction */ cross_v3_v3v3(raxis, rmat[1], splineVec); rangle = dot_v3v3(rmat[1], splineVec); CLAMP(rangle, -1.0f, 1.0f); rangle = acosf(rangle); /* multiply the magnitude of the angle by the influence of the constraint to * control the influence of the SplineIK effect */ rangle *= tree->con->enforce; /* construct rotation matrix from the axis-angle rotation found above * - this call takes care to make sure that the axis provided is a unit vector first */ axis_angle_to_mat3(dmat, raxis, rangle); /* combine these rotations so that the y-axis of the bone is now aligned as the spline dictates, * while still maintaining roll control from the existing bone animation */ mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */ normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */ copy_m4_m3(poseMat, tmat); } /* step 4: set the scaling factors for the axes */ { /* only multiply the y-axis by the scaling factor to get nice volume-preservation */ mul_v3_fl(poseMat[1], scaleFac); /* set the scaling factors of the x and z axes from... */ switch (ikData->xzScaleMode) { case CONSTRAINT_SPLINEIK_XZS_ORIGINAL: { /* original scales get used */ float scale; /* x-axis scale */ scale = len_v3(pchan->pose_mat[0]); mul_v3_fl(poseMat[0], scale); /* z-axis scale */ scale = len_v3(pchan->pose_mat[2]); mul_v3_fl(poseMat[2], scale); break; } case CONSTRAINT_SPLINEIK_XZS_INVERSE: { /* old 'volume preservation' method using the inverse scale */ float scale; /* calculate volume preservation factor which is * basically the inverse of the y-scaling factor */ if (fabsf(scaleFac) != 0.0f) { scale = 1.0f / fabsf(scaleFac); /* we need to clamp this within sensible values */ /* NOTE: these should be fine for now, but should get sanitised in future */ CLAMP(scale, 0.0001f, 100000.0f); } else scale = 1.0f; /* apply the scaling */ mul_v3_fl(poseMat[0], scale); mul_v3_fl(poseMat[2], scale); break; } case CONSTRAINT_SPLINEIK_XZS_VOLUMETRIC: { /* improved volume preservation based on the Stretch To constraint */ float final_scale; /* as the basis for volume preservation, we use the inverse scale factor... */ if (fabsf(scaleFac) != 0.0f) { /* NOTE: The method here is taken wholesale from the Stretch To constraint */ float bulge = powf(1.0f / fabsf(scaleFac), ikData->bulge); if (bulge > 1.0f) { if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MAX) { float bulge_max = max_ff(ikData->bulge_max, 1.0f); float hard = min_ff(bulge, bulge_max); float range = bulge_max - 1.0f; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; float soft = 1.0f + range * atanf((bulge - 1.0f) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, ikData->bulge_smooth); } } if (bulge < 1.0f) { if (ikData->flag & CONSTRAINT_SPLINEIK_USE_BULGE_MIN) { float bulge_min = CLAMPIS(ikData->bulge_min, 0.0f, 1.0f); float hard = max_ff(bulge, bulge_min); float range = 1.0f - bulge_min; float scale = (range > 0.0f) ? 1.0f / range : 0.0f; float soft = 1.0f - range * atanf((1.0f - bulge) * scale) / (float)M_PI_2; bulge = interpf(soft, hard, ikData->bulge_smooth); } } /* compute scale factor for xz axes from this value */ final_scale = sqrtf(bulge); } else { /* no scaling, so scale factor is simple */ final_scale = 1.0f; } /* apply the scaling (assuming normalised scale) */ mul_v3_fl(poseMat[0], final_scale); mul_v3_fl(poseMat[2], final_scale); break; } } /* finally, multiply the x and z scaling by the radius of the curve too, * to allow automatic scales to get tweaked still */ if ((ikData->flag & CONSTRAINT_SPLINEIK_NO_CURVERAD) == 0) { mul_v3_fl(poseMat[0], radius); mul_v3_fl(poseMat[2], radius); } } /* step 5: set the location of the bone in the matrix */ if (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT) { /* when the 'no-root' option is affected, the chain can retain * the shape but be moved elsewhere */ copy_v3_v3(poseHead, pchan->pose_head); } else if (tree->con->enforce < 1.0f) { /* when the influence is too low * - blend the positions for the 'root' bone * - stick to the parent for any other */ if (pchan->parent) { copy_v3_v3(poseHead, pchan->pose_head); } else { /* FIXME: this introduces popping artifacts when we reach 0.0 */ interp_v3_v3v3(poseHead, pchan->pose_head, poseHead, tree->con->enforce); } } copy_v3_v3(poseMat[3], poseHead); /* finally, store the new transform */ copy_m4_m4(pchan->pose_mat, poseMat); copy_v3_v3(pchan->pose_head, poseHead); /* recalculate tail, as it's now outdated after the head gets adjusted above! */ BKE_pose_where_is_bone_tail(pchan); /* done! */ pchan->flag |= POSE_DONE; }
static int update_reports_display_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); ReportList *reports = CTX_wm_reports(C); Report *report; ReportTimerInfo *rti; float progress = 0.0, color_progress = 0.0; float neutral_col[3] = {0.35, 0.35, 0.35}; float neutral_gray = 0.6; float timeout = 0.0, color_timeout = 0.0; int send_note = 0; /* escape if not our timer */ if ((reports->reporttimer == NULL) || (reports->reporttimer != event->customdata) || ((report = BKE_reports_last_displayable(reports)) == NULL) /* may have been deleted */ ) { return OPERATOR_PASS_THROUGH; } rti = (ReportTimerInfo *)reports->reporttimer->customdata; timeout = (report->type & RPT_ERROR_ALL) ? ERROR_TIMEOUT : INFO_TIMEOUT; color_timeout = (report->type & RPT_ERROR_ALL) ? ERROR_COLOR_TIMEOUT : INFO_COLOR_TIMEOUT; /* clear the report display after timeout */ if ((float)reports->reporttimer->duration > timeout) { WM_event_remove_timer(wm, NULL, reports->reporttimer); reports->reporttimer = NULL; WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } if (rti->widthfac == 0.0f) { /* initialize colors based on report type */ if (report->type & RPT_ERROR_ALL) { rti->col[0] = 1.0; rti->col[1] = 0.2; rti->col[2] = 0.0; } else if (report->type & RPT_WARNING_ALL) { rti->col[0] = 1.0; rti->col[1] = 1.0; rti->col[2] = 0.0; } else if (report->type & RPT_INFO_ALL) { rti->col[0] = 0.3; rti->col[1] = 0.45; rti->col[2] = 0.7; } rti->grayscale = 0.75; rti->widthfac = 1.0; } progress = (float)reports->reporttimer->duration / timeout; color_progress = (float)reports->reporttimer->duration / color_timeout; /* save us from too many draws */ if (color_progress <= 1.0f) { send_note = 1; /* fade colors out sharply according to progress through fade-out duration */ interp_v3_v3v3(rti->col, rti->col, neutral_col, color_progress); rti->grayscale = interpf(neutral_gray, rti->grayscale, color_progress); } /* collapse report at end of timeout */ if (progress * timeout > timeout - COLLAPSE_TIMEOUT) { rti->widthfac = (progress * timeout - (timeout - COLLAPSE_TIMEOUT)) / COLLAPSE_TIMEOUT; rti->widthfac = 1.0f - rti->widthfac; send_note = 1; } if (send_note) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_INFO, NULL); } return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); }
static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { bGPDspoint *pt; Nurb *nu = (curnu) ? *curnu : NULL; BezTriple *bezt, *prev_bezt = NULL; int i, tot, old_nbezt = 0; const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3]; const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); /* create new 'nurb' or extend current one within the curve */ if (nu) { old_nbezt = nu->pntsu; /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke, * so no need to add it. * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes. */ BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points); } else { nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); nu->pntsu = gps->totpoints + add_start_end_points; nu->resolu = 12; nu->resolv = 12; nu->type = CU_BEZIER; nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts"); stitch = false; /* Security! */ } if (do_gtd) { gp_timing_data_set_nbr(gtd, nu->pntsu); } tot = gps->totpoints; /* get initial coordinates */ pt = gps->points; if (tot) { gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); } } /* If needed, make the link between both strokes with two zero-radius additional points */ if (curnu && old_nbezt) { BLI_assert(gps->prev != NULL); /* Update last point's second handle */ if (stitch) { bezt = &nu->bezt[old_nbezt - 1]; interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC); copy_v3_v3(bezt->vec[2], h2); pt++; } /* Create "link points" */ /* About "zero-radius" point interpolations: * - If we have at least two points in current curve (most common case), we linearly extrapolate * the last segment to get the first point (p1) position and timing. * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point * with the first point of the current stroke. * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... */ else { float p1[3], p2[3]; float dt1 = 0.0f, dt2 = 0.0f; prev_bezt = NULL; if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) { /* Only use last curve segment if previous stroke was not a single-point one! */ prev_bezt = &nu->bezt[old_nbezt - 2]; } bezt = &nu->bezt[old_nbezt - 1]; /* First point */ if (prev_bezt) { interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC); if (do_gtd) { const int idx = gps->prev->totpoints - 1; dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); } } else { interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC); if (do_gtd) { dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); } } /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (tot > 1) { interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); } } else { interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); } } /* Second handle of last point of previous stroke. */ interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC); copy_v3_v3(bezt->vec[2], h2); /* First point */ interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC); interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC); bezt++; gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1, 0.0f, rad_fac, minmax_weights); /* Second point */ interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC); interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC); bezt++; gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); old_nbezt += 2; copy_v3_v3(p3d_prev, p2); } } else if (add_start_point) { float p[3]; float dt = 0.0f; if (gps->totpoints > 1) { interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); } } else { copy_v3_v3(p, p3d_cur); p[0] -= GAP_DFAC; /* Rather arbitrary... */ dt = -GAP_DFAC; /* Rather arbitrary too! */ } interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC); interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC); bezt = &nu->bezt[old_nbezt]; gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); old_nbezt++; copy_v3_v3(p3d_prev, p); } if (old_nbezt) { prev_bezt = &nu->bezt[old_nbezt - 1]; } /* add points */ for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); } else { interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC); } if (i < tot - 1) { interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC); } else { interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC); } gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); /* shift coord vects */ copy_v3_v3(p3d_prev, p3d_cur); copy_v3_v3(p3d_cur, p3d_next); if (i + 2 < tot) { gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; } if (add_end_point) { float p[3]; float dt = 0.0f; if (gps->totpoints > 1) { interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC); if (do_gtd) { const int idx = gps->totpoints - 1; dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); } } else { copy_v3_v3(p, prev_bezt->vec[1]); p[0] += GAP_DFAC; /* Rather arbitrary... */ dt = GAP_DFAC; /* Rather arbitrary too! */ } /* Second handle of last point of this stroke. */ interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC); copy_v3_v3(prev_bezt->vec[2], h2); /* The end point */ interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC); interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC); /* Note bezt has already been incremented in main loop above, so it points to the right place. */ gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); } /* must calculate handles or else we crash */ BKE_nurb_handles_calc(nu); if (!curnu || !*curnu) { BLI_addtail(&cu->nurb, nu); } if (curnu) { *curnu = nu; } }
static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { bGPDspoint *pt; Nurb *nu = (curnu) ? *curnu : NULL; BPoint *bp, *prev_bp = NULL; const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); int i, old_nbp = 0; /* create new 'nurb' or extend current one within the curve */ if (nu) { old_nbp = nu->pntsu; /* If stitch, the first point of this stroke is already present in current nu. * Else, we have to add two additional points to make the zero-radius link between strokes. */ BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points); } else { nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); nu->pntsu = gps->totpoints + add_start_end_points; nu->pntsv = 1; nu->orderu = 2; /* point-to-point! */ nu->type = CU_NURBS; nu->flagu = CU_NURB_ENDPOINT; nu->resolu = cu->resolu; nu->resolv = cu->resolv; nu->knotsu = NULL; nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints"); stitch = false; /* Security! */ } if (do_gtd) { gp_timing_data_set_nbr(gtd, nu->pntsu); } /* If needed, make the link between both strokes with two zero-radius additional points */ /* About "zero-radius" point interpolations: * - If we have at least two points in current curve (most common case), we linearly extrapolate * the last segment to get the first point (p1) position and timing. * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point * with the first point of the current stroke. * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... */ if (curnu && !stitch && old_nbp) { float p1[3], p2[3], p[3], next_p[3]; float dt1 = 0.0f, dt2 = 0.0f; BLI_assert(gps->prev != NULL); prev_bp = NULL; if ((old_nbp > 1) && (gps->prev->totpoints > 1)) { /* Only use last curve segment if previous stroke was not a single-point one! */ prev_bp = &nu->bp[old_nbp - 2]; } bp = &nu->bp[old_nbp - 1]; /* First point */ gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { const int idx = gps->prev->totpoints - 1; dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); } } else { interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC); if (do_gtd) { dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); } } bp++; gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1, 0.0f, rad_fac, minmax_weights); /* Second point */ /* Note dt2 is always negative, which marks the gap. */ if (gps->totpoints > 1) { gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); } } else { interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC); if (do_gtd) { dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); } } bp++; gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); old_nbp += 2; } else if (add_start_point) { float p[3], next_p[3]; float dt = 0.0f; gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); if (gps->totpoints > 1) { gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); interp_v3_v3v3(p, p, next_p, -GAP_DFAC); if (do_gtd) { dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); } } else { p[0] -= GAP_DFAC; /* Rather arbitrary... */ dt = -GAP_DFAC; /* Rather arbitrary too! */ } bp = &nu->bp[old_nbp]; /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value) * would not work (it would be *before* gtd->inittime, which is not supported currently). */ gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); old_nbp++; } if (old_nbp) { prev_bp = &nu->bp[old_nbp - 1]; } /* add points */ for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp]; i < gps->totpoints; i++, pt++, bp++) { float p[3]; float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; /* get coordinates to add at */ gp_strokepoint_convertcoords(C, gps, pt, p, subrect); gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); prev_bp = bp; } if (add_end_point) { float p[3]; float dt = 0.0f; if (gps->totpoints > 1) { interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC); if (do_gtd) { const int idx = gps->totpoints - 1; dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); } } else { copy_v3_v3(p, prev_bp->vec); p[0] += GAP_DFAC; /* Rather arbitrary... */ dt = GAP_DFAC; /* Rather arbitrary too! */ } /* Note bp has already been incremented in main loop above, so it points to the right place. */ gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); } /* add nurb to curve */ if (!curnu || !*curnu) { BLI_addtail(&cu->nurb, nu); } if (curnu) { *curnu = nu; } BKE_nurb_knot_calc_u(nu); }
/** * Specialized slerp that uses a sphere defined by each points normal. */ static void interp_slerp_co_no_v3( const float co_a[3], const float no_a[3], const float co_b[3], const float no_b[3], const float no_dir[3], /* caller already knows, avoid normalize */ float fac, float r_co[3]) { /* center of the sphere defined by both normals */ float center[3]; BLI_assert(len_squared_v3v3(no_a, no_b) != 0); /* calculate sphere 'center' */ { /* use point on plane to */ float plane_a[4], plane_b[4], plane_c[4]; float no_mid[3], no_ortho[3]; /* pass this as an arg instead */ #if 0 float no_dir[3]; #endif float v_a_no_ortho[3], v_b_no_ortho[3]; add_v3_v3v3(no_mid, no_a, no_b); normalize_v3(no_mid); #if 0 sub_v3_v3v3(no_dir, co_a, co_b); normalize_v3(no_dir); #endif /* axis of slerp */ cross_v3_v3v3(no_ortho, no_mid, no_dir); normalize_v3(no_ortho); /* create planes */ cross_v3_v3v3(v_a_no_ortho, no_ortho, no_a); cross_v3_v3v3(v_b_no_ortho, no_ortho, no_b); project_v3_plane(v_a_no_ortho, no_ortho, v_a_no_ortho); project_v3_plane(v_b_no_ortho, no_ortho, v_b_no_ortho); plane_from_point_normal_v3(plane_a, co_a, v_a_no_ortho); plane_from_point_normal_v3(plane_b, co_b, v_b_no_ortho); plane_from_point_normal_v3(plane_c, co_b, no_ortho); /* find the sphere center from 3 planes */ if (isect_plane_plane_plane_v3(plane_a, plane_b, plane_c, center)) { /* pass */ } else { mid_v3_v3v3(center, co_a, co_b); } } /* calculate the final output 'r_co' */ { float ofs_a[3], ofs_b[3], ofs_slerp[3]; float dist_a, dist_b; sub_v3_v3v3(ofs_a, co_a, center); sub_v3_v3v3(ofs_b, co_b, center); dist_a = normalize_v3(ofs_a); dist_b = normalize_v3(ofs_b); if (interp_v3_v3v3_slerp(ofs_slerp, ofs_a, ofs_b, fac)) { madd_v3_v3v3fl(r_co, center, ofs_slerp, interpf(dist_b, dist_a, fac)); } else { interp_v3_v3v3(r_co, co_a, co_b, fac); } } }