Ejemplo n.º 1
0
/**
 * 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;
}
Ejemplo n.º 2
0
/* 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);
				}
			}
		}
	}
} 
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
/* 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;
}
Ejemplo n.º 6
0
/* 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);
        }
      }
    }
  }
}
Ejemplo n.º 7
0
/* 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;
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 11
0
/**
 * 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);
		}
	}
}