Esempio n. 1
0
/* Calculate median point of markers of tracks marked as used for
 * 2D stabilization.
 *
 * NOTE: frame number should be in clip space, not scene space
 */
static bool stabilization_median_point_get(MovieTracking *tracking, int framenr, float median[2])
{
	bool ok = false;
	float min[2], max[2];
	MovieTrackingTrack *track;

	INIT_MINMAX2(min, max);

	track = tracking->tracks.first;
	while (track) {
		if (track->flag & TRACK_USE_2D_STAB) {
			MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);

			minmax_v2v2_v2(min, max, marker->pos);

			ok = true;
		}

		track = track->next;
	}

	median[0] = (max[0] + min[0]) / 2.0f;
	median[1] = (max[1] + min[1]) / 2.0f;

	return ok;
}
bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest(
    rcti *input, ReadBufferOperation *readOperation, rcti *output)
{
  float min[2], max[2];
  INIT_MINMAX2(min, max);

  for (int sample = 0; sample < this->m_motion_blur_samples; ++sample) {
    float UVs[4][2];
    float deriv[2][2];
    MotionSample *sample_data = &this->m_samples[sample];
    /* TODO(sergey): figure out proper way to do this. */
    warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv);
    warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv);
    warpCoord(input->xmax + 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv);
    warpCoord(input->xmin - 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv);
    for (int i = 0; i < 4; i++) {
      minmax_v2v2_v2(min, max, UVs[i]);
    }
  }

  rcti newInput;

  newInput.xmin = min[0] - 1;
  newInput.ymin = min[1] - 1;
  newInput.xmax = max[0] + 1;
  newInput.ymax = max[1] + 1;

  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
}
Esempio n. 3
0
static bool selected_boundbox(SpaceClip *sc, float min[2], float max[2])
{
	MovieClip *clip = ED_space_clip_get_clip(sc);
	MovieTrackingTrack *track;
	int width, height;
	bool ok = false;
	ListBase *tracksbase = BKE_tracking_get_active_tracks(&clip->tracking);
	int framenr = ED_space_clip_get_clip_frame_number(sc);

	INIT_MINMAX2(min, max);

	ED_space_clip_get_size(sc, &width, &height);

	track = tracksbase->first;
	while (track) {
		if (TRACK_VIEW_SELECTED(sc, track)) {
			MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);

			if (marker) {
				float pos[3];

				pos[0] = marker->pos[0] + track->offset[0];
				pos[1] = marker->pos[1] + track->offset[1];
				pos[2] = 0.0f;

				/* undistortion happens for normalized coords */
				if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
					/* undistortion happens for normalized coords */
					ED_clip_point_undistorted_pos(sc, pos, pos);
				}

				pos[0] *= width;
				pos[1] *= height;

				mul_v3_m4v3(pos, sc->stabmat, pos);

				minmax_v2v2_v2(min, max, pos);

				ok = true;
			}
		}

		track = track->next;
	}

	return ok;
}
Esempio n. 4
0
static int node_get_selected_minmax(bNodeTree *ntree, bNode *gnode, float *min, float *max)
{
	bNode *node;
	float loc[2];
	int totselect = 0;
	
	INIT_MINMAX2(min, max);
	for (node = ntree->nodes.first; node; node = node->next) {
		if (node_group_make_use_node(node, gnode)) {
			nodeToView(node, 0.0f, 0.0f, &loc[0], &loc[1]);
			minmax_v2v2_v2(min, max, loc);
			++totselect;
		}
	}
	
	/* sane min/max if no selected nodes */
	if (totselect == 0) {
		min[0] = min[1] = max[0] = max[1] = 0.0f;
	}
	
	return totselect;
}
Esempio n. 5
0
static void draw_distortion(SpaceClip *sc, ARegion *ar, MovieClip *clip,
                            int width, int height, float zoomx, float zoomy)
{
	float x, y;
	const int n = 10;
	int i, j, a;
	float pos[2], tpos[2], grid[11][11][2];
	MovieTracking *tracking = &clip->tracking;
	bGPdata *gpd = NULL;
	float aspy = 1.0f / tracking->camera.pixel_aspect;
	float dx = (float)width / n, dy = (float)height / n * aspy;
	float offsx = 0.0f, offsy = 0.0f;

	if (!tracking->camera.focal)
		return;

	if ((sc->flag & SC_SHOW_GRID) == 0 && (sc->flag & SC_MANUAL_CALIBRATION) == 0)
		return;

	UI_view2d_view_to_region_fl(&ar->v2d, 0.0f, 0.0f, &x, &y);

	glPushMatrix();
	glTranslatef(x, y, 0);
	glScalef(zoomx, zoomy, 0);
	glMultMatrixf(sc->stabmat);
	glScalef(width, height, 0);

	/* grid */
	if (sc->flag & SC_SHOW_GRID) {
		float val[4][2], idx[4][2];
		float min[2], max[2];

		for (a = 0; a < 4; a++) {
			if (a < 2)
				val[a][a % 2] = FLT_MAX;
			else
				val[a][a % 2] = -FLT_MAX;
		}

		zero_v2(pos);
		for (i = 0; i <= n; i++) {
			for (j = 0; j <= n; j++) {
				if (i == 0 || j == 0 || i == n || j == n) {
					BKE_tracking_distort_v2(tracking, pos, tpos);

					for (a = 0; a < 4; a++) {
						int ok;

						if (a < 2)
							ok = tpos[a % 2] < val[a][a % 2];
						else
							ok = tpos[a % 2] > val[a][a % 2];

						if (ok) {
							copy_v2_v2(val[a], tpos);
							idx[a][0] = j;
							idx[a][1] = i;
						}
					}
				}

				pos[0] += dx;
			}

			pos[0] = 0.0f;
			pos[1] += dy;
		}

		INIT_MINMAX2(min, max);

		for (a = 0; a < 4; a++) {
			pos[0] = idx[a][0] * dx;
			pos[1] = idx[a][1] * dy;

			BKE_tracking_undistort_v2(tracking, pos, tpos);

			minmax_v2v2_v2(min, max, tpos);
		}

		copy_v2_v2(pos, min);
		dx = (max[0] - min[0]) / n;
		dy = (max[1] - min[1]) / n;

		for (i = 0; i <= n; i++) {
			for (j = 0; j <= n; j++) {
				BKE_tracking_distort_v2(tracking, pos, grid[i][j]);

				grid[i][j][0] /= width;
				grid[i][j][1] /= height * aspy;

				pos[0] += dx;
			}

			pos[0] = min[0];
			pos[1] += dy;
		}

		glColor3f(1.0f, 0.0f, 0.0f);

		for (i = 0; i <= n; i++) {
			glBegin(GL_LINE_STRIP);
			for (j = 0; j <= n; j++) {
				glVertex2fv(grid[i][j]);
			}
			glEnd();
		}

		for (j = 0; j <= n; j++) {
			glBegin(GL_LINE_STRIP);
			for (i = 0; i <= n; i++) {
				glVertex2fv(grid[i][j]);
			}
			glEnd();
		}
	}

	if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) {
		MovieTrackingTrack *track = BKE_tracking_track_get_active(&sc->clip->tracking);

		if (track) {
			int framenr = ED_space_clip_get_clip_frame_number(sc);
			MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr);

			offsx = marker->pos[0];
			offsy = marker->pos[1];

			gpd = track->gpd;
		}

	}
	else {
		gpd = clip->gpd;
	}

	if (sc->flag & SC_MANUAL_CALIBRATION && gpd) {
		bGPDlayer *layer = gpd->layers.first;

		while (layer) {
			bGPDframe *frame = layer->frames.first;

			if (layer->flag & GP_LAYER_HIDE) {
				layer = layer->next;
				continue;
			}

			glColor4fv(layer->color);
			glLineWidth(layer->thickness);
			glPointSize((float)(layer->thickness + 2));

			while (frame) {
				bGPDstroke *stroke = frame->strokes.first;

				while (stroke) {
					if (stroke->flag & GP_STROKE_2DSPACE) {
						if (stroke->totpoints > 1) {
							glBegin(GL_LINE_STRIP);
							for (i = 0; i < stroke->totpoints - 1; i++) {
								float npos[2], dpos[2], len;
								int steps;

								pos[0] = (stroke->points[i].x + offsx) * width;
								pos[1] = (stroke->points[i].y + offsy) * height * aspy;

								npos[0] = (stroke->points[i + 1].x + offsx) * width;
								npos[1] = (stroke->points[i + 1].y + offsy) * height * aspy;

								len = len_v2v2(pos, npos);
								steps = ceil(len / 5.0f);

								/* we want to distort only long straight lines */
								if (stroke->totpoints == 2) {
									BKE_tracking_undistort_v2(tracking, pos, pos);
									BKE_tracking_undistort_v2(tracking, npos, npos);
								}

								sub_v2_v2v2(dpos, npos, pos);
								mul_v2_fl(dpos, 1.0f / steps);

								for (j = 0; j <= steps; j++) {
									BKE_tracking_distort_v2(tracking, pos, tpos);
									glVertex2f(tpos[0] / width, tpos[1] / (height * aspy));

									add_v2_v2(pos, dpos);
								}
							}
							glEnd();
						}
						else if (stroke->totpoints == 1) {
							glBegin(GL_POINTS);
							glVertex2f(stroke->points[0].x + offsx, stroke->points[0].y + offsy);
							glEnd();
						}
					}

					stroke = stroke->next;
				}

				frame = frame->next;
			}

			layer = layer->next;
		}

		glLineWidth(1.0f);
		glPointSize(1.0f);
	}

	glPopMatrix();
}
Esempio n. 6
0
/* return non-zero if spline is selected */
static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline *spline,
                               const char draw_flag, const char draw_type,
                               const float xscale, const float yscale)
{
	const bool is_spline_sel = (spline->flag & SELECT) && (masklay->restrictflag & MASK_RESTRICT_SELECT) == 0;
	const bool is_smooth = (draw_flag & MASK_DRAWFLAG_SMOOTH) != 0;

	unsigned char rgb_spline[4];
	MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline);
	SpaceClip *sc = CTX_wm_space_clip(C);
	bool undistort = false;

	int i, handle_size, tot_feather_point;
	float (*feather_points)[2], (*fp)[2];
	float min[2], max[2];

	if (!spline->tot_point)
		return;

	if (sc)
		undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT);

	/* TODO, add this to sequence editor */
	handle_size = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE) * U.pixelsize;

	glPointSize(handle_size);

	mask_spline_color_get(masklay, spline, is_spline_sel, rgb_spline);

	/* feather points */
	feather_points = fp = BKE_mask_spline_feather_points(spline, &tot_feather_point);
	for (i = 0; i < spline->tot_point; i++) {

		/* watch it! this is intentionally not the deform array, only check for sel */
		MaskSplinePoint *point = &spline->points[i];

		int j;

		for (j = 0; j <= point->tot_uw; j++) {
			float feather_point[2];
			bool sel = false;

			copy_v2_v2(feather_point, *fp);

			if (undistort)
				mask_point_undistort_pos(sc, feather_point, feather_point);

			if (j == 0) {
				sel = MASKPOINT_ISSEL_ANY(point);
			}
			else {
				sel = (point->uw[j - 1].flag & SELECT) != 0;
			}

			if (sel) {
				if (point == masklay->act_point)
					glColor3f(1.0f, 1.0f, 1.0f);
				else
					UI_ThemeColor(TH_HANDLE_VERTEX_SELECT);
			}
			else {
				UI_ThemeColor(TH_HANDLE_VERTEX);
			}

			glBegin(GL_POINTS);
			glVertex2fv(feather_point);
			glEnd();

			fp++;
		}
	}
	MEM_freeN(feather_points);

	if (is_smooth) {
		glEnable(GL_LINE_SMOOTH);
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	}

	/* control points */
	INIT_MINMAX2(min, max);
	for (i = 0; i < spline->tot_point; i++) {

		/* watch it! this is intentionally not the deform array, only check for sel */
		MaskSplinePoint *point = &spline->points[i];
		MaskSplinePoint *point_deform = &points_array[i];
		BezTriple *bezt = &point_deform->bezt;

		float vert[2];

		copy_v2_v2(vert, bezt->vec[1]);

		if (undistort) {
			mask_point_undistort_pos(sc, vert, vert);
		}

		/* draw handle segment */
		if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) {
			float handle[2];
			BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_STICK, handle);
			if (undistort) {
				mask_point_undistort_pos(sc, handle, handle);
			}
			draw_single_handle(masklay, point, MASK_WHICH_HANDLE_STICK,
			                   draw_type, handle_size, xscale, yscale, vert, handle);
		}
		else {
			float handle_left[2], handle_right[2];
			BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_LEFT, handle_left);
			BKE_mask_point_handle(point_deform, MASK_WHICH_HANDLE_RIGHT, handle_right);
			if (undistort) {
				mask_point_undistort_pos(sc, handle_left, handle_left);
				mask_point_undistort_pos(sc, handle_left, handle_left);
			}
			draw_single_handle(masklay, point, MASK_WHICH_HANDLE_LEFT,
			                   draw_type, handle_size, xscale, yscale, vert, handle_left);
			draw_single_handle(masklay, point, MASK_WHICH_HANDLE_RIGHT,
			                   draw_type, handle_size, xscale, yscale, vert, handle_right);
		}

		/* draw CV point */
		if (MASKPOINT_ISSEL_KNOT(point)) {
			if (point == masklay->act_point)
				glColor3f(1.0f, 1.0f, 1.0f);
			else
				UI_ThemeColor(TH_HANDLE_VERTEX_SELECT);
		}
		else
			UI_ThemeColor(TH_HANDLE_VERTEX);

		glBegin(GL_POINTS);
		glVertex2fv(vert);
		glEnd();

		minmax_v2v2_v2(min, max, vert);
	}

	if (is_spline_sel) {
		float x = (min[0] + max[0]) / 2.0f;
		float y = (min[1] + max[1]) / 2.0f;
		/* TODO(sergey): Remove hardcoded colors. */
		if (masklay->act_spline == spline) {
			glColor3ub(255, 255, 255);
		}
		else {
			glColor3ub(255, 255, 0);
		}

		draw_circle(x, y, 6.0f, true, xscale, yscale);

		glColor3ub(0, 0, 0);
		draw_circle(x, y, 6.0f, false, xscale, yscale);
	}

	if (is_smooth) {
		glDisable(GL_LINE_SMOOTH);
		glDisable(GL_BLEND);
	}
}
Esempio n. 7
0
void BKE_mask_spline_feather_collapse_inner_loops(
        MaskSpline *spline, float (*feather_points)[2], const unsigned int tot_feather_point)
{
#define BUCKET_INDEX(co) \
	feather_bucket_index_from_coord(co, min, bucket_scale, buckets_per_side)

	int buckets_per_side, tot_bucket;
	float bucket_size, bucket_scale[2];

	FeatherEdgesBucket *buckets;

	unsigned int i;
	float min[2], max[2];
	float max_delta_x = -1.0f, max_delta_y = -1.0f, max_delta;

	if (tot_feather_point < 4) {
		/* self-intersection works only for quads at least,
		 * in other cases polygon can't be self-intersecting anyway
		 */

		return;
	}

	/* find min/max corners of mask to build buckets in that space */
	INIT_MINMAX2(min, max);

	for (i = 0; i < tot_feather_point; i++) {
		unsigned int next = i + 1;
		float delta;

		minmax_v2v2_v2(min, max, feather_points[i]);

		if (next == tot_feather_point) {
			if (spline->flag & MASK_SPLINE_CYCLIC)
				next = 0;
			else
				break;
		}

		delta = fabsf(feather_points[i][0] - feather_points[next][0]);
		if (delta > max_delta_x)
			max_delta_x = delta;

		delta = fabsf(feather_points[i][1] - feather_points[next][1]);
		if (delta > max_delta_y)
			max_delta_y = delta;
	}

	/* prevent divisionsby zero by ensuring bounding box is not collapsed */
	if (max[0] - min[0] < FLT_EPSILON) {
		max[0] += 0.01f;
		min[0] -= 0.01f;
	}

	if (max[1] - min[1] < FLT_EPSILON) {
		max[1] += 0.01f;
		min[1] -= 0.01f;
	}

	/* use dynamically calculated buckets per side, so we likely wouldn't
	 * run into a situation when segment doesn't fit two buckets which is
	 * pain collecting candidates for intersection
	 */

	max_delta_x /= max[0] - min[0];
	max_delta_y /= max[1] - min[1];

	max_delta = MAX2(max_delta_x, max_delta_y);

	buckets_per_side = min_ii(512, 0.9f / max_delta);

	if (buckets_per_side == 0) {
		/* happens when some segment fills the whole bounding box across some of dimension */

		buckets_per_side = 1;
	}

	tot_bucket = buckets_per_side * buckets_per_side;
	bucket_size = 1.0f / buckets_per_side;

	/* pre-compute multipliers, to save mathematical operations in loops */
	bucket_scale[0] = 1.0f / ((max[0] - min[0]) * bucket_size);
	bucket_scale[1] = 1.0f / ((max[1] - min[1]) * bucket_size);

	/* fill in buckets' edges */
	buckets = MEM_callocN(sizeof(FeatherEdgesBucket) * tot_bucket, "feather buckets");

	for (i = 0; i < tot_feather_point; i++) {
		int start = i, end = i + 1;
		int start_bucket_index, end_bucket_index;

		if (end == tot_feather_point) {
			if (spline->flag & MASK_SPLINE_CYCLIC)
				end = 0;
			else
				break;
		}

		start_bucket_index = BUCKET_INDEX(feather_points[start]);
		end_bucket_index = BUCKET_INDEX(feather_points[end]);

		feather_bucket_add_edge(&buckets[start_bucket_index], start, end);

		if (start_bucket_index != end_bucket_index) {
			FeatherEdgesBucket *end_bucket = &buckets[end_bucket_index];
			FeatherEdgesBucket *diagonal_bucket_a, *diagonal_bucket_b;

			feather_bucket_get_diagonal(buckets, start_bucket_index, end_bucket_index, buckets_per_side,
			                            &diagonal_bucket_a, &diagonal_bucket_b);

			feather_bucket_add_edge(end_bucket, start, end);
			feather_bucket_add_edge(diagonal_bucket_a, start, end);
			feather_bucket_add_edge(diagonal_bucket_a, start, end);
		}
	}

	/* check all edges for intersection with edges from their buckets */
	for (i = 0; i < tot_feather_point; i++) {
		int cur_a = i, cur_b = i + 1;
		int start_bucket_index, end_bucket_index;

		FeatherEdgesBucket *start_bucket;

		if (cur_b == tot_feather_point)
			cur_b = 0;

		start_bucket_index = BUCKET_INDEX(feather_points[cur_a]);
		end_bucket_index = BUCKET_INDEX(feather_points[cur_b]);

		start_bucket = &buckets[start_bucket_index];

		feather_bucket_check_intersect(feather_points, tot_feather_point, start_bucket, cur_a, cur_b);

		if (start_bucket_index != end_bucket_index) {
			FeatherEdgesBucket *end_bucket = &buckets[end_bucket_index];
			FeatherEdgesBucket *diagonal_bucket_a, *diagonal_bucket_b;

			feather_bucket_get_diagonal(buckets, start_bucket_index, end_bucket_index, buckets_per_side,
			                            &diagonal_bucket_a, &diagonal_bucket_b);

			feather_bucket_check_intersect(feather_points, tot_feather_point, end_bucket, cur_a, cur_b);
			feather_bucket_check_intersect(feather_points, tot_feather_point, diagonal_bucket_a, cur_a, cur_b);
			feather_bucket_check_intersect(feather_points, tot_feather_point, diagonal_bucket_b, cur_a, cur_b);
		}
	}

	/* free buckets */
	for (i = 0; i < tot_bucket; i++) {
		if (buckets[i].segments)
			MEM_freeN(buckets[i].segments);
	}

	MEM_freeN(buckets);

#undef BUCKET_INDEX
}
Esempio n. 8
0
static void feather_bucket_check_intersect(
        float (*feather_points)[2], int tot_feather_point, FeatherEdgesBucket *bucket,
        int cur_a, int cur_b)
{
	int i;

	const float *v1 = (float *) feather_points[cur_a];
	const float *v2 = (float *) feather_points[cur_b];

	for (i = 0; i < bucket->tot_segment; i++) {
		int check_a = bucket->segments[i][0];
		int check_b = bucket->segments[i][1];

		const float *v3 = (float *) feather_points[check_a];
		const float *v4 = (float *) feather_points[check_b];

		if (check_a >= cur_a - 1 || cur_b == check_a)
			continue;

		if (isect_seg_seg_v2_simple(v1, v2, v3, v4)) {
			int k;
			float p[2];
			float min_a[2], max_a[2];
			float min_b[2], max_b[2];

			isect_seg_seg_v2_point(v1, v2, v3, v4, p);

			INIT_MINMAX2(min_a, max_a);
			INIT_MINMAX2(min_b, max_b);

			/* collapse loop with smaller AABB */
			for (k = 0; k < tot_feather_point; k++) {
				if (k >= check_b && k <= cur_a) {
					minmax_v2v2_v2(min_a, max_a, feather_points[k]);
				}
				else {
					minmax_v2v2_v2(min_b, max_b, feather_points[k]);
				}
			}

			if (max_a[0] - min_a[0] < max_b[0] - min_b[0] ||
			    max_a[1] - min_a[1] < max_b[1] - min_b[1])
			{
				for (k = check_b; k <= cur_a; k++) {
					copy_v2_v2(feather_points[k], p);
				}
			}
			else {
				for (k = 0; k <= check_a; k++) {
					copy_v2_v2(feather_points[k], p);
				}

				if (cur_b != 0) {
					for (k = cur_b; k < tot_feather_point; k++) {
						copy_v2_v2(feather_points[k], p);
					}
				}
			}
		}
	}
}
Esempio n. 9
0
bNode *node_group_make_from_selected(bNodeTree *ntree)
{
    bNodeLink *link, *linkn;
    bNode *node, *gnode, *nextn;
    bNodeTree *ngroup;
    bNodeSocket *gsock;
    ListBase anim_basepaths = {NULL, NULL};
    float min[2], max[2];
    int totnode=0;
    bNodeTemplate ntemp;

    INIT_MINMAX2(min, max);

    /* is there something to group? also do some clearing */
    for(node= ntree->nodes.first; node; node= node->next) {
        if(node->flag & NODE_SELECT) {
            /* no groups in groups */
            if(node->type==NODE_GROUP)
                return NULL;
            DO_MINMAX2( (&node->locx), min, max);
            totnode++;
        }
        node->done= 0;
    }
    if(totnode==0) return NULL;

    /* check if all connections are OK, no unselected node has both
    	inputs and outputs to a selection */
    for(link= ntree->links.first; link; link= link->next) {
        if(link->fromnode && link->tonode && link->fromnode->flag & NODE_SELECT)
            link->tonode->done |= 1;
        if(link->fromnode && link->tonode && link->tonode->flag & NODE_SELECT)
            link->fromnode->done |= 2;
    }

    for(node= ntree->nodes.first; node; node= node->next) {
        if((node->flag & NODE_SELECT)==0)
            if(node->done==3)
                break;
    }
    if(node)
        return NULL;

    /* OK! new nodetree */
    ngroup= ntreeAddTree("NodeGroup", ntree->type, NODE_GROUP);

    /* move nodes over */
    for(node= ntree->nodes.first; node; node= nextn) {
        nextn= node->next;
        if(node->flag & NODE_SELECT) {
            /* keep track of this node's RNA "base" path (the part of the pat identifying the node)
             * if the old nodetree has animation data which potentially covers this node
             */
            if (ntree->adt) {
                PointerRNA ptr;
                char *path;

                RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr);
                path = RNA_path_from_ID_to_struct(&ptr);

                if (path)
                    BLI_addtail(&anim_basepaths, BLI_genericNodeN(path));
            }

            /* change node-collection membership */
            BLI_remlink(&ntree->nodes, node);
            BLI_addtail(&ngroup->nodes, node);

            node->locx-= 0.5f*(min[0]+max[0]);
            node->locy-= 0.5f*(min[1]+max[1]);
        }
    }

    /* move animation data over */
    if (ntree->adt) {
        LinkData *ld, *ldn=NULL;

        BKE_animdata_separate_by_basepath(&ntree->id, &ngroup->id, &anim_basepaths);

        /* paths + their wrappers need to be freed */
        for (ld = anim_basepaths.first; ld; ld = ldn) {
            ldn = ld->next;

            MEM_freeN(ld->data);
            BLI_freelinkN(&anim_basepaths, ld);
        }
    }

    /* make group node */
    ntemp.type = NODE_GROUP;
    ntemp.ngroup = ngroup;
    gnode= nodeAddNode(ntree, &ntemp);
    gnode->locx= 0.5f*(min[0]+max[0]);
    gnode->locy= 0.5f*(min[1]+max[1]);

    /* relink external sockets */
    for(link= ntree->links.first; link; link= linkn) {
        linkn= link->next;

        if(link->fromnode && link->tonode && (link->fromnode->flag & link->tonode->flag & NODE_SELECT)) {
            BLI_remlink(&ntree->links, link);
            BLI_addtail(&ngroup->links, link);
        }
        else if(link->tonode && (link->tonode->flag & NODE_SELECT)) {
            gsock = node_group_expose_socket(ngroup, link->tosock, SOCK_IN);
            link->tosock->link = nodeAddLink(ngroup, NULL, gsock, link->tonode, link->tosock);
            link->tosock = node_group_add_extern_socket(ntree, &gnode->inputs, SOCK_IN, gsock);
            link->tonode = gnode;
        }
        else if(link->fromnode && (link->fromnode->flag & NODE_SELECT)) {
            /* search for existing group node socket */
            for (gsock=ngroup->outputs.first; gsock; gsock=gsock->next)
                if (gsock->link && gsock->link->fromsock==link->fromsock)
                    break;
            if (!gsock) {
                gsock = node_group_expose_socket(ngroup, link->fromsock, SOCK_OUT);
                gsock->link = nodeAddLink(ngroup, link->fromnode, link->fromsock, NULL, gsock);
                link->fromsock = node_group_add_extern_socket(ntree, &gnode->outputs, SOCK_OUT, gsock);
            }
            else
                link->fromsock = node_group_find_output(gnode, gsock);
            link->fromnode = gnode;
        }
    }

    ngroup->update |= NTREE_UPDATE;
    ntreeUpdateTree(ngroup);
    ntree->update |= NTREE_UPDATE_NODES|NTREE_UPDATE_LINKS;
    ntreeUpdateTree(ntree);

    return gnode;
}
KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTriangulation()
{
	MovieClipUser user = {0};
	TriangulationData *triangulation;
	MovieTracking *tracking = &this->m_movieClip->tracking;
	MovieTrackingTrack *track;
	VoronoiSite *sites, *site;
	ImBuf *ibuf;
	ListBase *tracksbase;
	ListBase edges = {NULL, NULL};
	int sites_total;
	int i;
	int width = this->getWidth();
	int height = this->getHeight();
	int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, this->m_framenumber);

	if (this->m_trackingObject[0]) {
		MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, this->m_trackingObject);

		if (!object)
			return NULL;

		tracksbase = BKE_tracking_object_get_tracks(tracking, object);
	}
	else
		tracksbase = BKE_tracking_get_active_tracks(tracking);

	/* count sites */
	for (track = (MovieTrackingTrack *) tracksbase->first, sites_total = 0; track; track = track->next) {
		MovieTrackingMarker *marker = BKE_tracking_marker_get(track, clip_frame);
		float pos[2];

		if (marker->flag & MARKER_DISABLED)
			continue;

		add_v2_v2v2(pos, marker->pos, track->offset);

		if (!IN_RANGE_INCL(pos[0], 0.0f, 1.0f) ||
		    !IN_RANGE_INCL(pos[1], 0.0f, 1.0f))
		{
			continue;
		}

		sites_total++;
	}

	if (!sites_total)
		return NULL;

	BKE_movieclip_user_set_frame(&user, clip_frame);
	ibuf = BKE_movieclip_get_ibuf(this->m_movieClip, &user);

	if (!ibuf)
		return NULL;

	triangulation = (TriangulationData *) MEM_callocN(sizeof(TriangulationData), "keying screen triangulation data");

	sites = (VoronoiSite *) MEM_callocN(sizeof(VoronoiSite) * sites_total, "keyingscreen voronoi sites");
	track = (MovieTrackingTrack *) tracksbase->first;
	for (track = (MovieTrackingTrack *) tracksbase->first, site = sites; track; track = track->next) {
		MovieTrackingMarker *marker = BKE_tracking_marker_get(track, clip_frame);
		ImBuf *pattern_ibuf;
		int j;
		float pos[2];

		if (marker->flag & MARKER_DISABLED)
			continue;

		add_v2_v2v2(pos, marker->pos, track->offset);

		if (!IN_RANGE_INCL(pos[0], 0.0f, 1.0f) ||
		    !IN_RANGE_INCL(pos[1], 0.0f, 1.0f))
		{
			continue;
		}

		pattern_ibuf = BKE_tracking_get_pattern_imbuf(ibuf, track, marker, TRUE, FALSE);

		zero_v3(site->color);

		if (pattern_ibuf) {
			for (j = 0; j < pattern_ibuf->x * pattern_ibuf->y; j++) {
				if (pattern_ibuf->rect_float) {
					add_v3_v3(site->color, &pattern_ibuf->rect_float[4 * j]);
				}
				else {
					unsigned char *rrgb = (unsigned char *)pattern_ibuf->rect;

					site->color[0] += srgb_to_linearrgb((float)rrgb[4 * j + 0] / 255.0f);
					site->color[1] += srgb_to_linearrgb((float)rrgb[4 * j + 1] / 255.0f);
					site->color[2] += srgb_to_linearrgb((float)rrgb[4 * j + 2] / 255.0f);
				}
			}

			mul_v3_fl(site->color, 1.0f / (pattern_ibuf->x * pattern_ibuf->y));
			IMB_freeImBuf(pattern_ibuf);
		}

		site->co[0] = pos[0] * width;
		site->co[1] = pos[1] * height;

		site++;
	}

	IMB_freeImBuf(ibuf);

	BLI_voronoi_compute(sites, sites_total, width, height, &edges);

	BLI_voronoi_triangulate(sites, sites_total, &edges, width, height,
	                        &triangulation->triangulated_points, &triangulation->triangulated_points_total,
	                        &triangulation->triangles, &triangulation->triangles_total);

	MEM_freeN(sites);
	BLI_freelistN(&edges);

	if (triangulation->triangles_total) {
		rctf *rect;
		rect = triangulation->triangles_AABB =
			(rctf *) MEM_callocN(sizeof(rctf) * triangulation->triangles_total, "voronoi triangulation AABB");

		for (i = 0; i < triangulation->triangles_total; i++, rect++) {
			int *triangle = triangulation->triangles[i];
			VoronoiTriangulationPoint *a = &triangulation->triangulated_points[triangle[0]],
			                          *b = &triangulation->triangulated_points[triangle[1]],
			                          *c = &triangulation->triangulated_points[triangle[2]];

			float min[2], max[2];

			INIT_MINMAX2(min, max);

			minmax_v2v2_v2(min, max, a->co);
			minmax_v2v2_v2(min, max, b->co);
			minmax_v2v2_v2(min, max, c->co);

			rect->xmin = min[0];
			rect->ymin = min[1];

			rect->xmax = max[0];
			rect->ymax = max[1];
		}
	}

	return triangulation;
}