Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/gpencil/gpencil_paint.c
| Context not available. | |||||
| MEM_freeN(old_points); | MEM_freeN(old_points); | ||||
| } | } | ||||
| /* Apply smooth to stroke point using same algorithm used in sclupt smooth (gp_brush_smooth_apply) */ | |||||
| static bool gp_dynamic_smooth(bGPDstroke *gps, int i, float inf) | |||||
aligorith: It may be better to find a way to share this code between the smooth brush and here. So, maybe… | |||||
| { | |||||
| bGPDspoint *pt = &gps->points[i]; | |||||
| float sco[3] = { 0.0f }; | |||||
| /* Do nothing if not enough points to smooth out */ | |||||
| if (gps->totpoints <= 2) { | |||||
| return false; | |||||
| } | |||||
| /* Only affect endpoints by a fraction of the normal strength, | |||||
| * to prevent the stroke from shrinking too much | |||||
| */ | |||||
| if ((i == 0) || (i == gps->totpoints - 1)) { | |||||
| inf *= 0.1f; | |||||
| } | |||||
| /* Compute smoothed coordinate by taking the ones nearby */ | |||||
| /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ | |||||
| { | |||||
| // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) | |||||
| const int steps = 2; | |||||
| const float average_fac = 1.0f / (float)(steps * 2 + 1); | |||||
| int step; | |||||
| /* add the point itself */ | |||||
| madd_v3_v3fl(sco, &pt->x, average_fac); | |||||
| /* n-steps before/after current point */ | |||||
| // XXX: review how the endpoints are treated by this algorithm | |||||
| // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight | |||||
| for (step = 1; step <= steps; step++) { | |||||
| bGPDspoint *pt1, *pt2; | |||||
| int before = i - step; | |||||
| int after = i + step; | |||||
| CLAMP_MIN(before, 0); | |||||
| CLAMP_MAX(after, gps->totpoints - 1); | |||||
| pt1 = &gps->points[before]; | |||||
| pt2 = &gps->points[after]; | |||||
| /* add both these points to the average-sum (s += p[i]/n) */ | |||||
| madd_v3_v3fl(sco, &pt1->x, average_fac); | |||||
| madd_v3_v3fl(sco, &pt2->x, average_fac); | |||||
| } | |||||
| } | |||||
| /* Based on influence factor, blend between original and optimal smoothed coordinate */ | |||||
| interp_v3_v3v3(&pt->x, &pt->x, sco, inf); | |||||
| return true; | |||||
| } | |||||
| /* subdivide a stroke */ | |||||
| static void gp_subdivide_stroke(bGPDstroke *gps, const int subpoints) | |||||
aligorithUnsubmitted Not Done Inline Actions"subpoints" is not really that descriptive. It was only upon re-reading the code that I realised that this is the new total number of points in the subdivided stroke. Maybe something like "new_totpoints" or something like that instead? aligorith: "subpoints" is not really that descriptive. It was only upon re-reading the code that I… | |||||
| { | |||||
| int i; | |||||
| // Subdivide stroke adding a point half way existing points | |||||
| bGPDspoint loca; | |||||
aligorithUnsubmitted Not Done Inline ActionsIs there any reason why you need to have these on the stack, and thus end up copying the data back and forth? It would probably be more efficient to just do: bGPDspoint *pt_a; bGPDspoint *pt_b; bGPDspoint *pt_new; then: pt_a = &gps->points[i]; pt_b = &gps->points[i]; pt_n = &gps->points[i + 1]; /* Interpolate all values */ interp_v3_v3(&pt_n->x, &pt_a->x, &pt_b->x, 0.5f); interpf(&pt_n->pressure, pt_a->pressure, pt_b->pressure); // ... Also note, the use of the interp functions from BLI_math instead of inlining the math here :) AFAICT, there isn't really any reason why we need to make copies of everything first. It doesn't look like the ranges will overlap... aligorith: Is there any reason why you need to have these on the stack, and thus end up copying the data… | |||||
| bGPDspoint locb; | |||||
| bGPDspoint locn; | |||||
| /* Move points to insert subdivision */ | |||||
| int y = 1; | |||||
| for (i = gps->totpoints - 1; i > 0; --i) | |||||
| { | |||||
| locn = gps->points[i]; | |||||
| gps->points[subpoints - y] = locn; | |||||
| y = y + 2; | |||||
| } | |||||
| /* Create interpolated points */ | |||||
| for (i = 0; i < subpoints - 1; ++i) | |||||
| { | |||||
| loca = gps->points[i]; | |||||
| locb = gps->points[i + 2]; | |||||
| // Interpolate all values | |||||
| locn.x = 0.5f * loca.x + 0.5f * locb.x; | |||||
| locn.y = 0.5f * loca.y + 0.5f * locb.y; | |||||
| locn.z = 0.5f * loca.z + 0.5f * locb.z; | |||||
| locn.pressure = 0.5f * loca.pressure + 0.5f * locb.pressure; | |||||
| locn.time = 0.5f * loca.time + 0.5f * locb.time; | |||||
| // Set new point | |||||
| gps->points[i + 1] = locn; | |||||
| ++i; // add to loop to jump next pair | |||||
| } | |||||
| gps->totpoints = subpoints; // Increase number of points | |||||
| } | |||||
| /* make a new stroke from the buffer data */ | /* make a new stroke from the buffer data */ | ||||
| static void gp_stroke_newfrombuffer(tGPsdata *p) | static void gp_stroke_newfrombuffer(tGPsdata *p) | ||||
| Context not available. | |||||
| bGPDstroke *gps; | bGPDstroke *gps; | ||||
| bGPDspoint *pt; | bGPDspoint *pt; | ||||
| tGPspoint *ptc; | tGPspoint *ptc; | ||||
| bGPDlayer *layer = gpencil_layer_getactive(p->gpd); | |||||
| int i, totelem; | int i, totelem; | ||||
| /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ | ||||
| int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0; | int depth_margin = (p->gpd->flag & GP_DATA_DEPTH_STROKE) ? 4 : 0; | ||||
| Context not available. | |||||
| gps->inittime = p->inittime; | gps->inittime = p->inittime; | ||||
| /* allocate enough memory for a continuous array for storage points */ | /* allocate enough memory for a continuous array for storage points */ | ||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); | int sublevel = layer->sublevel; | ||||
| int subpoints = gps->totpoints; | |||||
| for (i = 0; i < sublevel; ++i) | |||||
| { | |||||
| // Avoid error if subdivide is too big (assume totpoints is right) | |||||
| if (subpoints + (subpoints - 1) > GP_STROKE_BUFFER_MAX) | |||||
| { | |||||
| sublevel = i; // reduce sublevel | |||||
| break; | |||||
| } | |||||
| subpoints += subpoints - 1; | |||||
| } | |||||
| gps->points = MEM_callocN(sizeof(bGPDspoint) * subpoints, "gp_stroke_points"); | |||||
| /* set pointer to first non-initialized point */ | /* set pointer to first non-initialized point */ | ||||
| pt = gps->points + (gps->totpoints - totelem); | pt = gps->points + (gps->totpoints - totelem); | ||||
| Context not available. | |||||
| pt->time = ptc->time; | pt->time = ptc->time; | ||||
| } | } | ||||
| /* subdivide the stroke */ | |||||
| if (sublevel > 0) | |||||
| { | |||||
| int sub = gps->totpoints; | |||||
| for (i = 0; i < sublevel; ++i) | |||||
| { | |||||
| sub += sub - 1; | |||||
| gp_subdivide_stroke(gps, sub); | |||||
| } | |||||
| } | |||||
| /* smooth stroke */ | |||||
| if (layer->gdynamic_smooth > 0.0f) // only if something to do | |||||
| { | |||||
| for (i = 0; i < gps->totpoints; i++) | |||||
| { | |||||
| gp_dynamic_smooth(gps, i, layer->gdynamic_smooth); | |||||
| } | |||||
| } | |||||
| if (depth_arr) | if (depth_arr) | ||||
| MEM_freeN(depth_arr); | MEM_freeN(depth_arr); | ||||
| } | } | ||||
| /* add stroke to frame */ | /* add stroke to frame */ | ||||
| BLI_addtail(&p->gpf->strokes, gps); | BLI_addtail(&p->gpf->strokes, gps); | ||||
| gp_stroke_added_enable(p); | gp_stroke_added_enable(p); | ||||
| Context not available. | |||||
It may be better to find a way to share this code between the smooth brush and here. So, maybe:
static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, const int radius, const int co[2]) { GP_EditBrush_Data *brush = gso->brush; bGPDspoint *pt = &gps->points[i]; float inf = gp_brush_influence_calc(gso, radius, co); /* perform smoothing */ return gp_smooth_stroke(gps, i, inf); }