Esempio n. 1
0
/* adjust bone roll to align Z axis with vector
 * vec is in local space and is normalized
 */
float ED_armature_ebone_roll_to_vector(const EditBone *bone, const float align_axis[3], const bool axis_only)
{
	float mat[3][3], nor[3];
	float vec[3], align_axis_proj[3], roll = 0.0f;

	BLI_ASSERT_UNIT_V3(align_axis);

	sub_v3_v3v3(nor, bone->tail, bone->head);

	/* If tail == head or the bone is aligned with the axis... */
	if (normalize_v3(nor) <= FLT_EPSILON || (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON))) {
		return roll;
	}

	vec_roll_to_mat3_normalized(nor, 0.0f, mat);

	/* project the new_up_axis along the normal */
	project_v3_v3v3_normalized(vec, align_axis, nor);
	sub_v3_v3v3(align_axis_proj, align_axis, vec);

	if (axis_only) {
		if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI_2)) {
			negate_v3(align_axis_proj);
		}
	}

	roll = angle_v3v3(align_axis_proj, mat[2]);

	cross_v3_v3v3(vec, mat[2], align_axis_proj);

	if (dot_v3v3(vec, nor) < 0.0f) {
		return -roll;
	}
	return roll;
}
Esempio n. 2
0
/* adjust bone roll to align Z axis with vector
 * vec is in local space and is normalized
 */
float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const short axis_only)
{
	float mat[3][3], nor[3];

	sub_v3_v3v3(nor, bone->tail, bone->head);
	vec_roll_to_mat3(nor, 0.0f, mat);
	
	/* check the bone isn't aligned with the axis */
	if (!is_zero_v3(align_axis) && angle_v3v3(align_axis, mat[2]) > FLT_EPSILON) {
		float vec[3], align_axis_proj[3], roll;
		
		/* project the new_up_axis along the normal */
		project_v3_v3v3(vec, align_axis, nor);
		sub_v3_v3v3(align_axis_proj, align_axis, vec);
		
		if (axis_only) {
			if (angle_v3v3(align_axis_proj, mat[2]) > (float)(M_PI / 2.0)) {
				negate_v3(align_axis_proj);
			}
		}
		
		roll = angle_v3v3(align_axis_proj, mat[2]);
		
		cross_v3_v3v3(vec, mat[2], align_axis_proj);
		
		if (dot_v3v3(vec, nor) < 0) {
			roll = -roll;
		}
		
		return roll;
	}

	return 0.0f;
}
Esempio n. 3
0
float angle_signed_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3])
{
	float v1_proj[3], v2_proj[3], tproj[3];
	float angle;

	sub_v3_v3v3(v1_proj, v1, v2);
	sub_v3_v3v3(v2_proj, v3, v2);

	/* project the vectors onto the axis */
	project_v3_v3v3(tproj, v1_proj, axis);
	sub_v3_v3(v1_proj, tproj);

	project_v3_v3v3(tproj, v2_proj, axis);
	sub_v3_v3(v2_proj, tproj);

	angle = angle_v3v3(v1_proj, v2_proj);

	/* calculate the sign (reuse 'tproj') */
	cross_v3_v3v3(tproj, v2_proj, v1_proj);
	if (dot_v3v3(tproj, axis) < 0.0f) {
		angle = ((float)(M_PI * 2.0)) - angle;
	}

	return angle;
}
Esempio n. 4
0
/**
 * angle between 2 vectors defined by 3 coords, about an axis. */
float angle_on_axis_v3v3v3_v3(const float v1[3], const float v2[3], const float v3[3], const float axis[3])
{
	float v1_proj[3], v2_proj[3], tproj[3];

	sub_v3_v3v3(v1_proj, v1, v2);
	sub_v3_v3v3(v2_proj, v3, v2);

	/* project the vectors onto the axis */
	project_v3_v3v3(tproj, v1_proj, axis);
	sub_v3_v3(v1_proj, tproj);

	project_v3_v3v3(tproj, v2_proj, axis);
	sub_v3_v3(v2_proj, tproj);

	return angle_v3v3(v1_proj, v2_proj);
}
static void select_similar_direction(bArmature *arm, EditBone *ebone_act, const float thresh)
{
	EditBone *ebone;
	float dir_act[3];
	sub_v3_v3v3(dir_act, ebone_act->head, ebone_act->tail);

	for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
		if (EBONE_SELECTABLE(arm, ebone)) {
			float dir[3];
			sub_v3_v3v3(dir, ebone->head, ebone->tail);

			if (angle_v3v3(dir_act, dir) / (float)M_PI < thresh) {
				ED_armature_edit_bone_select(ebone);
			}
		}
	}
}
Esempio n. 6
0
/* Note this modifies nos_new in-place. */
static void mix_normals(
        const float mix_factor, MDeformVert *dvert, const int defgrp_index, const bool use_invert_vgroup,
        const float mix_limit, const short mix_mode,
        const int num_verts, MLoop *mloop, float (*nos_old)[3], float (*nos_new)[3], const int num_loops)
{
	/* Mix with org normals... */
	float *facs = NULL, *wfac;
	float (*no_new)[3], (*no_old)[3];
	int i;

	if (dvert) {
		facs = MEM_malloc_arrayN((size_t)num_loops, sizeof(*facs), __func__);
		BKE_defvert_extract_vgroup_to_loopweights(
		            dvert, defgrp_index, num_verts, mloop, num_loops, facs, use_invert_vgroup);
	}

	for (i = num_loops, no_new = nos_new, no_old = nos_old, wfac = facs; i--; no_new++, no_old++, wfac++) {
		const float fac = facs ? *wfac * mix_factor : mix_factor;

		switch (mix_mode) {
			case MOD_NORMALEDIT_MIX_ADD:
				add_v3_v3(*no_new, *no_old);
				normalize_v3(*no_new);
				break;
			case MOD_NORMALEDIT_MIX_SUB:
				sub_v3_v3(*no_new, *no_old);
				normalize_v3(*no_new);
				break;
			case MOD_NORMALEDIT_MIX_MUL:
				mul_v3_v3(*no_new, *no_old);
				normalize_v3(*no_new);
				break;
			case MOD_NORMALEDIT_MIX_COPY:
				break;
		}

		interp_v3_v3v3_slerp_safe(
		        *no_new, *no_old, *no_new,
		        (mix_limit < (float)M_PI) ? min_ff(fac, mix_limit / angle_v3v3(*no_new, *no_old)) : fac);
	}

	MEM_SAFE_FREE(facs);
}
static void axisProjection(TransInfo *t, const float axis[3], const float in[3], float out[3])
{
	float norm[3], vec[3], factor, angle;
	float t_con_center[3];

	if (is_zero_v3(in)) {
		return;
	}

	copy_v3_v3(t_con_center, t->center_global);

	/* checks for center being too close to the view center */
	viewAxisCorrectCenter(t, t_con_center);
	
	angle = fabsf(angle_v3v3(axis, t->viewinv[2]));
	if (angle > (float)M_PI_2) {
		angle = (float)M_PI - angle;
	}
	angle = RAD2DEGF(angle);

	/* For when view is parallel to constraint... will cause NaNs otherwise
	 * So we take vertical motion in 3D space and apply it to the
	 * constraint axis. Nice for camera grab + MMB */
	if (angle < 5.0f) {
		project_v3_v3v3(vec, in, t->viewinv[1]);
		factor = dot_v3v3(t->viewinv[1], vec) * 2.0f;
		/* since camera distance is quite relative, use quadratic relationship. holding shift can compensate */
		if (factor < 0.0f) factor *= -factor;
		else factor *= factor;

		copy_v3_v3(out, axis);
		normalize_v3(out);
		mul_v3_fl(out, -factor);  /* -factor makes move down going backwards */
	}
	else {
		float v[3], i1[3], i2[3];
		float v2[3], v4[3];
		float norm_center[3];
		float plane[3];

		getViewVector(t, t_con_center, norm_center);
		cross_v3_v3v3(plane, norm_center, axis);

		project_v3_v3v3(vec, in, plane);
		sub_v3_v3v3(vec, in, vec);
		
		add_v3_v3v3(v, vec, t_con_center);
		getViewVector(t, v, norm);

		/* give arbitrary large value if projection is impossible */
		factor = dot_v3v3(axis, norm);
		if (1.0f - fabsf(factor) < 0.0002f) {
			copy_v3_v3(out, axis);
			if (factor > 0) {
				mul_v3_fl(out, 1000000000.0f);
			}
			else {
				mul_v3_fl(out, -1000000000.0f);
			}
		}
		else {
			add_v3_v3v3(v2, t_con_center, axis);
			add_v3_v3v3(v4, v, norm);
			
			isect_line_line_v3(t_con_center, v2, v, v4, i1, i2);
			
			sub_v3_v3v3(v, i2, v);
	
			sub_v3_v3v3(out, i1, t_con_center);

			/* possible some values become nan when
			 * viewpoint and object are both zero */
			if (!finite(out[0])) out[0] = 0.0f;
			if (!finite(out[1])) out[1] = 0.0f;
			if (!finite(out[2])) out[2] = 0.0f;
		}
	}
}
Esempio n. 8
0
static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
                                  DerivedMesh *derivedData,
                                  ModifierApplyFlag flag)
{
	DerivedMesh *dm = derivedData;
	DerivedMesh *result;
	ScrewModifierData *ltmd = (ScrewModifierData *) md;
	const int useRenderParams = flag & MOD_APPLY_RENDER;
	
	int *origindex;
	int mpoly_index = 0;
	unsigned int step;
	unsigned int i, j;
	unsigned int i1, i2;
	unsigned int step_tot = useRenderParams ? ltmd->render_steps : ltmd->steps;
	const bool do_flip = ltmd->flag & MOD_SCREW_NORMAL_FLIP ? 1 : 0;

	const int quad_ord[4] = {
	    do_flip ? 3 : 0,
	    do_flip ? 2 : 1,
	    do_flip ? 1 : 2,
	    do_flip ? 0 : 3,
	};
	const int quad_ord_ofs[4] = {
	    do_flip ? 2 : 0,
	    do_flip ? 1 : 1,
	    do_flip ? 0 : 2,
	    do_flip ? 3 : 3,
	};

	unsigned int maxVerts = 0, maxEdges = 0, maxPolys = 0;
	const unsigned int totvert = (unsigned int)dm->getNumVerts(dm);
	const unsigned int totedge = (unsigned int)dm->getNumEdges(dm);
	const unsigned int totpoly = (unsigned int)dm->getNumPolys(dm);

	unsigned int *edge_poly_map = NULL;  /* orig edge to orig poly */
	unsigned int *vert_loop_map = NULL;  /* orig vert to orig loop */

	/* UV Coords */
	const unsigned int mloopuv_layers_tot = (unsigned int)CustomData_number_of_layers(&dm->loopData, CD_MLOOPUV);
	MLoopUV **mloopuv_layers = BLI_array_alloca(mloopuv_layers, mloopuv_layers_tot);
	float uv_u_scale;
	float uv_v_minmax[2] = {FLT_MAX, -FLT_MAX};
	float uv_v_range_inv;
	float uv_axis_plane[4];

	char axis_char = 'X';
	bool close;
	float angle = ltmd->angle;
	float screw_ofs = ltmd->screw_ofs;
	float axis_vec[3] = {0.0f, 0.0f, 0.0f};
	float tmp_vec1[3], tmp_vec2[3]; 
	float mat3[3][3];
	float mtx_tx[4][4]; /* transform the coords by an object relative to this objects transformation */
	float mtx_tx_inv[4][4]; /* inverted */
	float mtx_tmp_a[4][4];
	
	unsigned int vc_tot_linked = 0;
	short other_axis_1, other_axis_2;
	const float *tmpf1, *tmpf2;

	unsigned int edge_offset;
	
	MPoly *mpoly_orig, *mpoly_new, *mp_new;
	MLoop *mloop_orig, *mloop_new, *ml_new;
	MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new;
	MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base;

	ScrewVertConnect *vc, *vc_tmp, *vert_connect = NULL;

	const char mpoly_flag = (ltmd->flag & MOD_SCREW_SMOOTH_SHADING) ? ME_SMOOTH : 0;

	/* don't do anything? */
	if (!totvert)
		return CDDM_from_template(dm, 0, 0, 0, 0, 0);

	switch (ltmd->axis) {
		case 0:
			other_axis_1 = 1;
			other_axis_2 = 2;
			break;
		case 1:
			other_axis_1 = 0;
			other_axis_2 = 2;
			break;
		default: /* 2, use default to quiet warnings */
			other_axis_1 = 0;
			other_axis_2 = 1;
			break;
	}

	axis_vec[ltmd->axis] = 1.0f;

	if (ltmd->ob_axis) {
		/* calc the matrix relative to the axis object */
		invert_m4_m4(mtx_tmp_a, ob->obmat);
		copy_m4_m4(mtx_tx_inv, ltmd->ob_axis->obmat);
		mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv);

		/* calc the axis vec */
		mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */
		normalize_v3(axis_vec);

		/* screw */
		if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) {
			/* find the offset along this axis relative to this objects matrix */
			float totlen = len_v3(mtx_tx[3]);

			if (totlen != 0.0f) {
				float zero[3] = {0.0f, 0.0f, 0.0f};
				float cp[3];
				screw_ofs = closest_to_line_v3(cp, mtx_tx[3], zero, axis_vec);
			}
			else {
				screw_ofs = 0.0f;
			}
		}

		/* angle */

#if 0   /* cant incluide this, not predictable enough, though quite fun. */
		if (ltmd->flag & MOD_SCREW_OBJECT_ANGLE) {
			float mtx3_tx[3][3];
			copy_m3_m4(mtx3_tx, mtx_tx);

			float vec[3] = {0, 1, 0};
			float cross1[3];
			float cross2[3];
			cross_v3_v3v3(cross1, vec, axis_vec);

			mul_v3_m3v3(cross2, mtx3_tx, cross1);
			{
				float c1[3];
				float c2[3];
				float axis_tmp[3];

				cross_v3_v3v3(c1, cross2, axis_vec);
				cross_v3_v3v3(c2, axis_vec, c1);


				angle = angle_v3v3(cross1, c2);

				cross_v3_v3v3(axis_tmp, cross1, c2);
				normalize_v3(axis_tmp);

				if (len_v3v3(axis_tmp, axis_vec) > 1.0f)
					angle = -angle;

			}
		}
#endif
	}
	else {
		/* exis char is used by i_rotate*/
		axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */

		/* useful to be able to use the axis vec in some cases still */
		zero_v3(axis_vec);
		axis_vec[ltmd->axis] = 1.0f;
	}

	/* apply the multiplier */
	angle *= (float)ltmd->iter;
	screw_ofs *= (float)ltmd->iter;
	uv_u_scale = 1.0f / (float)(step_tot);

	/* multiplying the steps is a bit tricky, this works best */
	step_tot = ((step_tot + 1) * ltmd->iter) - (ltmd->iter - 1);

	/* will the screw be closed?
	 * Note! smaller then FLT_EPSILON * 100 gives problems with float precision so its never closed. */
	if (fabsf(screw_ofs) <= (FLT_EPSILON * 100.0f) &&
	    fabsf(fabsf(angle) - ((float)M_PI * 2.0f)) <= (FLT_EPSILON * 100.0f))
	{
		close = 1;
		step_tot--;
		if (step_tot < 3) step_tot = 3;
	
		maxVerts = totvert  * step_tot;   /* -1 because we're joining back up */
		maxEdges = (totvert * step_tot) + /* these are the edges between new verts */
		           (totedge * step_tot);  /* -1 because vert edges join */
		maxPolys = totedge * step_tot;

		screw_ofs = 0.0f;
	}
	else {
		close = 0;
		if (step_tot < 3) step_tot = 3;

		maxVerts =  totvert  * step_tot; /* -1 because we're joining back up */
		maxEdges =  (totvert * (step_tot - 1)) + /* these are the edges between new verts */
		           (totedge * step_tot);  /* -1 because vert edges join */
		maxPolys =  totedge * (step_tot - 1);
	}

	if ((ltmd->flag & MOD_SCREW_UV_STRETCH_U) == 0) {
		uv_u_scale = (uv_u_scale / (float)ltmd->iter) * (angle / ((float)M_PI * 2.0f));
	}
	
	result = CDDM_from_template(dm, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys);
	
	/* copy verts from mesh */
	mvert_orig =    dm->getVertArray(dm);
	medge_orig =    dm->getEdgeArray(dm);
	
	mvert_new =     result->getVertArray(result);
	mpoly_new =     result->getPolyArray(result);
	mloop_new =     result->getLoopArray(result);
	medge_new =     result->getEdgeArray(result);

	if (!CustomData_has_layer(&result->polyData, CD_ORIGINDEX)) {
		CustomData_add_layer(&result->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys);
	}

	origindex = CustomData_get_layer(&result->polyData, CD_ORIGINDEX);

	DM_copy_vert_data(dm, result, 0, 0, (int)totvert); /* copy first otherwise this overwrites our own vertex normals */

	if (mloopuv_layers_tot) {
		float zero_co[3] = {0};
		plane_from_point_normal_v3(uv_axis_plane, zero_co, axis_vec);
	}

	if (mloopuv_layers_tot) {
		unsigned int uv_lay;
		for (uv_lay = 0; uv_lay < mloopuv_layers_tot; uv_lay++) {
			mloopuv_layers[uv_lay] = CustomData_get_layer_n(&result->loopData, CD_MLOOPUV, (int)uv_lay);
		}

		if (ltmd->flag & MOD_SCREW_UV_STRETCH_V) {
			for (i = 0, mv_orig = mvert_orig; i < totvert; i++, mv_orig++) {
				const float v = dist_squared_to_plane_v3(mv_orig->co, uv_axis_plane);
				uv_v_minmax[0] = min_ff(v, uv_v_minmax[0]);
				uv_v_minmax[1] = max_ff(v, uv_v_minmax[1]);
			}
			uv_v_minmax[0] = sqrtf_signed(uv_v_minmax[0]);
			uv_v_minmax[1] = sqrtf_signed(uv_v_minmax[1]);
		}

		uv_v_range_inv = uv_v_minmax[1] - uv_v_minmax[0];
		uv_v_range_inv = uv_v_range_inv ? 1.0f / uv_v_range_inv : 0.0f;
	}

	/* Set the locations of the first set of verts */
	
	mv_new = mvert_new;
	mv_orig = mvert_orig;
	
	/* Copy the first set of edges */
	med_orig = medge_orig;
	med_new = medge_new;
	for (i = 0; i < totedge; i++, med_orig++, med_new++) {
		med_new->v1 = med_orig->v1;
		med_new->v2 = med_orig->v2;
		med_new->crease = med_orig->crease;
		med_new->flag = med_orig->flag &  ~ME_LOOSEEDGE;
	}
	
	/* build polygon -> edge map */
	if (totpoly) {
		MPoly *mp_orig;

		mpoly_orig = dm->getPolyArray(dm);
		mloop_orig = dm->getLoopArray(dm);
		edge_poly_map = MEM_mallocN(sizeof(*edge_poly_map) * totedge, __func__);
		memset(edge_poly_map, 0xff, sizeof(*edge_poly_map) * totedge);

		vert_loop_map = MEM_mallocN(sizeof(*vert_loop_map) * totvert, __func__);
		memset(vert_loop_map, 0xff, sizeof(*vert_loop_map) * totvert);

		for (i = 0, mp_orig = mpoly_orig; i < totpoly; i++, mp_orig++) {
			unsigned int loopstart = (unsigned int)mp_orig->loopstart;
			unsigned int loopend = loopstart + (unsigned int)mp_orig->totloop;

			MLoop *ml_orig = &mloop_orig[loopstart];
			unsigned int k;
			for (k = loopstart; k < loopend; k++, ml_orig++) {
				edge_poly_map[ml_orig->e] = i;
				vert_loop_map[ml_orig->v] = k;

				/* also order edges based on faces */
				if (medge_new[ml_orig->e].v1 != ml_orig->v) {
					SWAP(unsigned int, medge_new[ml_orig->e].v1, medge_new[ml_orig->e].v2);
				}
			}
		}
	}
Esempio n. 9
0
/* the arguments are the desired situation */
void ED_view3d_smooth_view_ex(
        /* avoid passing in the context */
        wmWindowManager *wm, wmWindow *win, ScrArea *sa,

        View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera,
        const float *ofs, const float *quat, const float *dist, const float *lens,
        const int smooth_viewtx)
{
	RegionView3D *rv3d = ar->regiondata;
	struct SmoothView3DStore sms = {{0}};
	bool ok = false;
	
	/* initialize sms */
	view3d_smooth_view_state_backup(&sms.dst, v3d, rv3d);
	view3d_smooth_view_state_backup(&sms.src, v3d, rv3d);
	/* if smoothview runs multiple times... */
	if (rv3d->sms == NULL) {
		view3d_smooth_view_state_backup(&sms.org, v3d, rv3d);
		sms.org_view = rv3d->view;
	}
	else {
		sms.org = rv3d->sms->org;
		sms.org_view = rv3d->sms->org_view;
	}
	/* sms.to_camera = false; */  /* initizlized to zero anyway */

	/* note on camera locking, this is a little confusing but works ok.
	 * we may be changing the view 'as if' there is no active camera, but in fact
	 * there is an active camera which is locked to the view.
	 *
	 * In the case where smooth view is moving _to_ a camera we don't want that
	 * camera to be moved or changed, so only when the camera is not being set should
	 * we allow camera option locking to initialize the view settings from the camera.
	 */
	if (camera == NULL && oldcamera == NULL) {
		ED_view3d_camera_lock_init(v3d, rv3d);
	}

	/* store the options we want to end with */
	if (ofs)  copy_v3_v3(sms.dst.ofs, ofs);
	if (quat) copy_qt_qt(sms.dst.quat, quat);
	if (dist) sms.dst.dist = *dist;
	if (lens) sms.dst.lens = *lens;

	if (camera) {
		sms.dst.dist = ED_view3d_offset_distance(camera->obmat, ofs, VIEW3D_DIST_FALLBACK);
		ED_view3d_from_object(camera, sms.dst.ofs, sms.dst.quat, &sms.dst.dist, &sms.dst.lens);
		sms.to_camera = true; /* restore view3d values in end */
	}
	
	/* skip smooth viewing for render engine draw */
	if (smooth_viewtx && v3d->drawtype != OB_RENDER) {
		bool changed = false; /* zero means no difference */
		
		if (oldcamera != camera)
			changed = true;
		else if (sms.dst.dist != rv3d->dist)
			changed = true;
		else if (sms.dst.lens != v3d->lens)
			changed = true;
		else if (!equals_v3v3(sms.dst.ofs, rv3d->ofs))
			changed = true;
		else if (!equals_v4v4(sms.dst.quat, rv3d->viewquat))
			changed = true;
		
		/* The new view is different from the old one
		 * so animate the view */
		if (changed) {
			/* original values */
			if (oldcamera) {
				sms.src.dist = ED_view3d_offset_distance(oldcamera->obmat, rv3d->ofs, 0.0f);
				/* this */
				ED_view3d_from_object(oldcamera, sms.src.ofs, sms.src.quat, &sms.src.dist, &sms.src.lens);
			}
			/* grid draw as floor */
			if ((rv3d->viewlock & RV3D_LOCKED) == 0) {
				/* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
				rv3d->view = RV3D_VIEW_USER;
			}

			sms.time_allowed = (double)smooth_viewtx / 1000.0;
			
			/* if this is view rotation only
			 * we can decrease the time allowed by
			 * the angle between quats 
			 * this means small rotations wont lag */
			if (quat && !ofs && !dist) {
				float vec1[3] = {0, 0, 1}, vec2[3] = {0, 0, 1};
				float q1[4], q2[4];

				invert_qt_qt(q1, sms.dst.quat);
				invert_qt_qt(q2, sms.src.quat);

				mul_qt_v3(q1, vec1);
				mul_qt_v3(q2, vec2);

				/* scale the time allowed by the rotation */
				sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */
			}

			/* ensure it shows correct */
			if (sms.to_camera) {
				/* use ortho if we move from an ortho view to an ortho camera */
				rv3d->persp = (((rv3d->is_persp == false) &&
				                (camera->type == OB_CAMERA) &&
				                (((Camera *)camera->data)->type == CAM_ORTHO)) ?
				                RV3D_ORTHO : RV3D_PERSP);
			}

			rv3d->rflag |= RV3D_NAVIGATING;
			
			/* not essential but in some cases the caller will tag the area for redraw,
			 * and in that case we can get a flicker of the 'org' user view but we want to see 'src' */
			view3d_smooth_view_state_restore(&sms.src, v3d, rv3d);

			/* keep track of running timer! */
			if (rv3d->sms == NULL) {
				rv3d->sms = MEM_mallocN(sizeof(struct SmoothView3DStore), "smoothview v3d");
			}
			*rv3d->sms = sms;
			if (rv3d->smooth_timer) {
				WM_event_remove_timer(wm, win, rv3d->smooth_timer);
			}
			/* TIMER1 is hardcoded in keymap */
			rv3d->smooth_timer = WM_event_add_timer(wm, win, TIMER1, 1.0 / 100.0); /* max 30 frs/sec */

			ok = true;
		}
	}
	
	/* if we get here nothing happens */
	if (ok == false) {
		if (sms.to_camera == false) {
			copy_v3_v3(rv3d->ofs, sms.dst.ofs);
			copy_qt_qt(rv3d->viewquat, sms.dst.quat);
			rv3d->dist = sms.dst.dist;
			v3d->lens = sms.dst.lens;

			ED_view3d_camera_lock_sync(v3d, rv3d);
		}

		if (rv3d->viewlock & RV3D_BOXVIEW) {
			view3d_boxview_copy(sa, ar);
		}

		ED_region_tag_redraw(ar);
	}
}
Esempio n. 10
0
static DerivedMesh *applyModifier(ModifierData *md, Object *ob,
                                  DerivedMesh *derivedData,
                                  int useRenderParams,
                                  int UNUSED(isFinalCalc))
{
    DerivedMesh *dm= derivedData;
    DerivedMesh *result;
    ScrewModifierData *ltmd= (ScrewModifierData*) md;

    int *origindex;
    int mface_index=0;
    int step;
    int i, j;
    int i1,i2;
    int step_tot= useRenderParams ? ltmd->render_steps : ltmd->steps;
    const int do_flip = ltmd->flag & MOD_SCREW_NORMAL_FLIP ? 1 : 0;
    int maxVerts=0, maxEdges=0, maxFaces=0;
    int totvert= dm->getNumVerts(dm);
    int totedge= dm->getNumEdges(dm);

    char axis_char= 'X', close;
    float angle= ltmd->angle;
    float screw_ofs= ltmd->screw_ofs;
    float axis_vec[3]= {0.0f, 0.0f, 0.0f};
    float tmp_vec1[3], tmp_vec2[3];
    float mat3[3][3];
    float mtx_tx[4][4]; /* transform the coords by an object relative to this objects transformation */
    float mtx_tx_inv[4][4]; /* inverted */
    float mtx_tmp_a[4][4];

    int vc_tot_linked= 0;
    short other_axis_1, other_axis_2;
    float *tmpf1, *tmpf2;

    MFace *mface_new, *mf_new;
    MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new;
    MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base;

    ScrewVertConnect *vc, *vc_tmp, *vert_connect= NULL;

    /* dont do anything? */
    if (!totvert)
        return CDDM_from_template(dm, 0, 0, 0);

    switch(ltmd->axis) {
    case 0:
        other_axis_1=1;
        other_axis_2=2;
        break;
    case 1:
        other_axis_1=0;
        other_axis_2=2;
        break;
    default: /* 2, use default to quiet warnings */
        other_axis_1=0;
        other_axis_2=1;
        break;
    }

    axis_vec[ltmd->axis]= 1.0f;

    if (ltmd->ob_axis) {
        /* calc the matrix relative to the axis object */
        invert_m4_m4(mtx_tmp_a, ob->obmat);
        copy_m4_m4(mtx_tx_inv, ltmd->ob_axis->obmat);
        mul_m4_m4m4(mtx_tx, mtx_tx_inv, mtx_tmp_a);

        /* calc the axis vec */
        mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */
        normalize_v3(axis_vec);

        /* screw */
        if(ltmd->flag & MOD_SCREW_OBJECT_OFFSET) {
            /* find the offset along this axis relative to this objects matrix */
            float totlen = len_v3(mtx_tx[3]);

            if(totlen != 0.0f) {
                float zero[3]= {0.0f, 0.0f, 0.0f};
                float cp[3];
                screw_ofs= closest_to_line_v3(cp, mtx_tx[3], zero, axis_vec);
            }
            else {
                screw_ofs= 0.0f;
            }
        }

        /* angle */

#if 0	// cant incluide this, not predictable enough, though quite fun,.
        if(ltmd->flag & MOD_SCREW_OBJECT_ANGLE) {
            float mtx3_tx[3][3];
            copy_m3_m4(mtx3_tx, mtx_tx);

            float vec[3] = {0,1,0};
            float cross1[3];
            float cross2[3];
            cross_v3_v3v3(cross1, vec, axis_vec);

            mul_v3_m3v3(cross2, mtx3_tx, cross1);
            {
                float c1[3];
                float c2[3];
                float axis_tmp[3];

                cross_v3_v3v3(c1, cross2, axis_vec);
                cross_v3_v3v3(c2, axis_vec, c1);


                angle= angle_v3v3(cross1, c2);

                cross_v3_v3v3(axis_tmp, cross1, c2);
                normalize_v3(axis_tmp);

                if(len_v3v3(axis_tmp, axis_vec) > 1.0f)
                    angle= -angle;

            }
        }
#endif
    }
    else {
        /* exis char is used by i_rotate*/
        axis_char += ltmd->axis; /* 'X' + axis */

        /* useful to be able to use the axis vec in some cases still */
        zero_v3(axis_vec);
        axis_vec[ltmd->axis]= 1.0f;
    }

    /* apply the multiplier */
    angle *= ltmd->iter;
    screw_ofs *= ltmd->iter;

    /* multiplying the steps is a bit tricky, this works best */
    step_tot = ((step_tot + 1) * ltmd->iter) - (ltmd->iter - 1);

    /* will the screw be closed?
     * Note! smaller then FLT_EPSILON*100 gives problems with float precision so its never closed. */
    if (fabsf(screw_ofs) <= (FLT_EPSILON*100.0f) && fabsf(fabsf(angle) - ((float)M_PI * 2.0f)) <= (FLT_EPSILON*100.0f)) {
        close= 1;
        step_tot--;
        if(step_tot < 3) step_tot= 3;

        maxVerts =	totvert  * step_tot; /* -1 because we're joining back up */
        maxEdges =	(totvert * step_tot) + /* these are the edges between new verts */
                    (totedge * step_tot); /* -1 because vert edges join */
        maxFaces =	totedge * step_tot;

        screw_ofs= 0.0f;
    }
    else {
        close= 0;
        if(step_tot < 3) step_tot= 3;

        maxVerts =	totvert  * step_tot; /* -1 because we're joining back up */
        maxEdges =	(totvert * (step_tot-1)) + /* these are the edges between new verts */
                    (totedge * step_tot); /* -1 because vert edges join */
        maxFaces =	totedge * (step_tot-1);
    }

    result= CDDM_from_template(dm, maxVerts, maxEdges, maxFaces);

    /* copy verts from mesh */
    mvert_orig =	dm->getVertArray(dm);
    medge_orig =	dm->getEdgeArray(dm);

    mvert_new =		result->getVertArray(result);
    mface_new =		result->getFaceArray(result);
    medge_new =		result->getEdgeArray(result);

    origindex= result->getFaceDataArray(result, CD_ORIGINDEX);

    DM_copy_vert_data(dm, result, 0, 0, totvert); /* copy first otherwise this overwrites our own vertex normals */

    /* Set the locations of the first set of verts */

    mv_new= mvert_new;
    mv_orig= mvert_orig;

    /* Copy the first set of edges */
    med_orig= medge_orig;
    med_new= medge_new;
    for (i=0; i < totedge; i++, med_orig++, med_new++) {
        med_new->v1= med_orig->v1;
        med_new->v2= med_orig->v2;
        med_new->crease= med_orig->crease;
        med_new->flag= med_orig->flag &  ~ME_LOOSEEDGE;
    }

    if(ltmd->flag & MOD_SCREW_NORMAL_CALC) {
        /*
         * Normal Calculation (for face flipping)
         * Sort edge verts for correct face flipping
         * NOT REALLY NEEDED but face flipping is nice.
         *
         * */


        /* Notice!
         *
         * Since we are only ordering the edges here it can avoid mallocing the
         * extra space by abusing the vert array berfore its filled with new verts.
         * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert
         * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3
         * so its safe to use the second 2 thrids of MVert the array for vert_connect,
         * just make sure ScrewVertConnect struct is no more then twice as big as MVert,
         * at the moment there is no chance of that being a problem,
         * unless MVert becomes half its current size.
         *
         * once the edges are ordered, vert_connect is not needed and it can be used for verts
         *
         * This makes the modifier faster with one less alloc.
         */

        vert_connect= MEM_mallocN(sizeof(ScrewVertConnect) * totvert, "ScrewVertConnect");
        //vert_connect= (ScrewVertConnect *) &medge_new[totvert]; /* skip the first slice of verts */
        vc= vert_connect;

        /* Copy Vert Locations */
        /* - We can do this in a later loop - only do here if no normal calc */
        if (!totedge) {
            for (i=0; i < totvert; i++, mv_orig++, mv_new++) {
                copy_v3_v3(mv_new->co, mv_orig->co);
                normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */
            }
        }
        else {
            /*printf("\n\n\n\n\nStarting Modifier\n");*/
            /* set edge users */
            med_new= medge_new;
            mv_new= mvert_new;

            if (ltmd->ob_axis) {
                /*mtx_tx is initialized early on */
                for (i=0; i < totvert; i++, mv_new++, mv_orig++, vc++) {
                    vc->co[0]= mv_new->co[0]= mv_orig->co[0];
                    vc->co[1]= mv_new->co[1]= mv_orig->co[1];
                    vc->co[2]= mv_new->co[2]= mv_orig->co[2];

                    vc->flag= 0;
                    vc->e[0]= vc->e[1]= NULL;
                    vc->v[0]= vc->v[1]= -1;

                    mul_m4_v3(mtx_tx, vc->co);
                    /* length in 2d, dont sqrt because this is only for comparison */
                    vc->dist =	vc->co[other_axis_1]*vc->co[other_axis_1] +
                                vc->co[other_axis_2]*vc->co[other_axis_2];

                    /* printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);*/
                }
            }
            else {
                for (i=0; i < totvert; i++, mv_new++, mv_orig++, vc++) {
                    vc->co[0]= mv_new->co[0]= mv_orig->co[0];
                    vc->co[1]= mv_new->co[1]= mv_orig->co[1];
                    vc->co[2]= mv_new->co[2]= mv_orig->co[2];

                    vc->flag= 0;
                    vc->e[0]= vc->e[1]= NULL;
                    vc->v[0]= vc->v[1]= -1;

                    /* length in 2d, dont sqrt because this is only for comparison */
                    vc->dist =	vc->co[other_axis_1]*vc->co[other_axis_1] +
                                vc->co[other_axis_2]*vc->co[other_axis_2];

                    /* printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);*/
                }
            }

            /* this loop builds connectivity info for verts */
            for (i=0; i<totedge; i++, med_new++) {
                vc= &vert_connect[med_new->v1];

                if (vc->v[0] == -1) { /* unused */
                    vc->v[0]= med_new->v2;
                    vc->e[0]= med_new;
                }
                else if (vc->v[1] == -1) {
                    vc->v[1]= med_new->v2;
                    vc->e[1]= med_new;
                }
                else {
                    vc->v[0]= vc->v[1]= -2; /* erro value  - dont use, 3 edges on vert */
                }

                vc= &vert_connect[med_new->v2];

                /* same as above but swap v1/2 */
                if (vc->v[0] == -1) { /* unused */
                    vc->v[0]= med_new->v1;
                    vc->e[0]= med_new;
                }
                else if (vc->v[1] == -1) {
                    vc->v[1]= med_new->v1;
                    vc->e[1]= med_new;
                }
                else {
                    vc->v[0]= vc->v[1]= -2; /* erro value  - dont use, 3 edges on vert */
                }
            }

            /* find the first vert */
            vc= vert_connect;
            for (i=0; i < totvert; i++, vc++) {
                /* Now do search for connected verts, order all edges and flip them
                 * so resulting faces are flipped the right way */
                vc_tot_linked= 0; /* count the number of linked verts for this loop */
                if (vc->flag == 0) {
                    int v_best=-1, ed_loop_closed=0; /* vert and vert new */
                    ScrewVertIter lt_iter;
                    int ed_loop_flip= 0; /* compiler complains if not initialized, but it should be initialized below */
                    float fl= -1.0f;

                    /*printf("Loop on connected vert: %i\n", i);*/

                    for(j=0; j<2; j++) {
                        /*printf("\tSide: %i\n", j);*/
                        screwvert_iter_init(&lt_iter, vert_connect, i, j);
                        if (j == 1) {
                            screwvert_iter_step(&lt_iter);
                        }
                        while (lt_iter.v_poin) {
                            /*printf("\t\tVERT: %i\n", lt_iter.v);*/
                            if (lt_iter.v_poin->flag) {
                                /*printf("\t\t\tBreaking Found end\n");*/
                                //endpoints[0]= endpoints[1]= -1;
                                ed_loop_closed= 1; /* circle */
                                break;
                            }
                            lt_iter.v_poin->flag= 1;
                            vc_tot_linked++;
                            /*printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist);*/
                            if (fl <= lt_iter.v_poin->dist) {
                                fl= lt_iter.v_poin->dist;
                                v_best= lt_iter.v;
                                /*printf("\t\t\tVERT BEST: %i\n", v_best);*/
                            }
                            screwvert_iter_step(&lt_iter);
                            if (!lt_iter.v_poin) {
                                /*printf("\t\t\tFound End Also Num %i\n", j);*/
                                /*endpoints[j]= lt_iter.v_other;*/ /* other is still valid */
                                break;
                            }
                        }
                    }

                    /* now we have a collection of used edges. flip their edges the right way*/
                    /*if (v_best != -1) - */

                    /*printf("Done Looking - vc_tot_linked: %i\n", vc_tot_linked);*/

                    if (vc_tot_linked>1) {
                        float vf_1, vf_2, vf_best;

                        vc_tmp= &vert_connect[v_best];

                        tmpf1= vert_connect[vc_tmp->v[0]].co;
                        tmpf2= vert_connect[vc_tmp->v[1]].co;


                        /* edge connects on each side! */
                        if ((vc_tmp->v[0] > -1) && (vc_tmp->v[1] > -1)) {
                            /*printf("Verts on each side (%i %i)\n", vc_tmp->v[0], vc_tmp->v[1]);*/
                            /* find out which is higher */

                            vf_1= tmpf1[ltmd->axis];
                            vf_2= tmpf2[ltmd->axis];
                            vf_best= vc_tmp->co[ltmd->axis];

                            if (vf_1 < vf_best && vf_best < vf_2) {
                                ed_loop_flip= 0;
                            }
                            else if (vf_1 > vf_best && vf_best > vf_2) {
                                ed_loop_flip= 1;
                            }
                            else {
                                /* not so simple to work out which edge is higher */
                                sub_v3_v3v3(tmp_vec1, tmpf1, vc_tmp->co);
                                sub_v3_v3v3(tmp_vec2, tmpf2, vc_tmp->co);
                                normalize_v3(tmp_vec1);
                                normalize_v3(tmp_vec2);

                                if (tmp_vec1[ltmd->axis] < tmp_vec2[ltmd->axis]) {
                                    ed_loop_flip= 1;
                                }
                                else {
                                    ed_loop_flip= 0;
                                }
                            }
                        }
                        else if (vc_tmp->v[0] >= 0) { /*vertex only connected on 1 side */
                            /*printf("Verts on ONE side (%i %i)\n", vc_tmp->v[0], vc_tmp->v[1]);*/
                            if (tmpf1[ltmd->axis] < vc_tmp->co[ltmd->axis]) { /* best is above */
                                ed_loop_flip= 1;
                            }
                            else { /* best is below or even... in even case we cant know whet  to do. */
                                ed_loop_flip= 0;
                            }

                        }/* else {
							printf("No Connected ___\n");
						}*/

                        /*printf("flip direction %i\n", ed_loop_flip);*/


                        /* switch the flip option if set
                         * note: flip is now done at face level so copying vgroup slizes is easier */
                        /*
                        if (do_flip)
                        	ed_loop_flip= !ed_loop_flip;
                        */

                        if (angle < 0.0f)
                            ed_loop_flip= !ed_loop_flip;

                        /* if its closed, we only need 1 loop */
                        for(j=ed_loop_closed; j<2; j++) {
                            /*printf("Ordering Side J %i\n", j);*/

                            screwvert_iter_init(&lt_iter, vert_connect, v_best, j);
                            /*printf("\n\nStarting - Loop\n");*/
                            lt_iter.v_poin->flag= 1; /* so a non loop will traverse the other side */


                            /* If this is the vert off the best vert and
                             * the best vert has 2 edges connected too it
                             * then swap the flip direction */
                            if (j == 1 && (vc_tmp->v[0] > -1) && (vc_tmp->v[1] > -1))
                                ed_loop_flip= !ed_loop_flip;

                            while (lt_iter.v_poin && lt_iter.v_poin->flag != 2) {
                                /*printf("\tOrdering Vert V %i\n", lt_iter.v);*/

                                lt_iter.v_poin->flag= 2;
                                if (lt_iter.e) {
                                    if (lt_iter.v == lt_iter.e->v1) {
                                        if (ed_loop_flip == 0) {
                                            /*printf("\t\t\tFlipping 0\n");*/
                                            SWAP(int, lt_iter.e->v1, lt_iter.e->v2);
                                        }/* else {
											printf("\t\t\tFlipping Not 0\n");
										}*/
                                    }
                                    else if (lt_iter.v == lt_iter.e->v2) {
                                        if (ed_loop_flip == 1) {
                                            /*printf("\t\t\tFlipping 1\n");*/
                                            SWAP(int, lt_iter.e->v1, lt_iter.e->v2);
                                        }/* else {
											printf("\t\t\tFlipping Not 1\n");
										}*/
                                    }/* else {
										printf("\t\tIncorrect edge topology");
									}*/
                                }/* else {
									printf("\t\tNo Edge at this point\n");
								}*/
                                screwvert_iter_step(&lt_iter);
                            }
                        }
Esempio n. 11
0
/* the arguments are the desired situation */
void smooth_view(bContext *C, View3D *v3d, ARegion *ar, Object *oldcamera, Object *camera,
                 float *ofs, float *quat, float *dist, float *lens)
{
	wmWindowManager *wm= CTX_wm_manager(C);
	wmWindow *win= CTX_wm_window(C);
	ScrArea *sa= CTX_wm_area(C);

	RegionView3D *rv3d= ar->regiondata;
	struct SmoothViewStore sms= {0};
	short ok= FALSE;
	
	/* initialize sms */
	copy_v3_v3(sms.new_ofs, rv3d->ofs);
	copy_qt_qt(sms.new_quat, rv3d->viewquat);
	sms.new_dist= rv3d->dist;
	sms.new_lens= v3d->lens;
	sms.to_camera= 0;

	/* note on camera locking, this is a little confusing but works ok.
	 * we may be changing the view 'as if' there is no active camera, but infact
	 * there is an active camera which is locked to the view.
	 *
	 * In the case where smooth view is moving _to_ a camera we dont want that
	 * camera to be moved or changed, so only when the camera is not being set should
	 * we allow camera option locking to initialize the view settings from the camera.
	 */
	if(camera == NULL && oldcamera == NULL) {
		ED_view3d_camera_lock_init(v3d, rv3d);
	}

	/* store the options we want to end with */
	if(ofs) copy_v3_v3(sms.new_ofs, ofs);
	if(quat) copy_qt_qt(sms.new_quat, quat);
	if(dist) sms.new_dist= *dist;
	if(lens) sms.new_lens= *lens;

	if (camera) {
		ED_view3d_from_object(camera, sms.new_ofs, sms.new_quat, &sms.new_dist, &sms.new_lens);
		sms.to_camera= 1; /* restore view3d values in end */
	}
	
	if (C && U.smooth_viewtx) {
		int changed = 0; /* zero means no difference */
		
		if (oldcamera != camera)
			changed = 1;
		else if (sms.new_dist != rv3d->dist)
			changed = 1;
		else if (sms.new_lens != v3d->lens)
			changed = 1;
		else if (!equals_v3v3(sms.new_ofs, rv3d->ofs))
			changed = 1;
		else if (!equals_v4v4(sms.new_quat, rv3d->viewquat))
			changed = 1;
		
		/* The new view is different from the old one
			* so animate the view */
		if (changed) {

			/* original values */
			if (oldcamera) {
				sms.orig_dist= rv3d->dist; // below function does weird stuff with it...
				ED_view3d_from_object(oldcamera, sms.orig_ofs, sms.orig_quat, &sms.orig_dist, &sms.orig_lens);
			}
			else {
				copy_v3_v3(sms.orig_ofs, rv3d->ofs);
				copy_qt_qt(sms.orig_quat, rv3d->viewquat);
				sms.orig_dist= rv3d->dist;
				sms.orig_lens= v3d->lens;
			}
			/* grid draw as floor */
			if((rv3d->viewlock & RV3D_LOCKED)==0) {
				/* use existing if exists, means multiple calls to smooth view wont loose the original 'view' setting */
				sms.orig_view= rv3d->sms ? rv3d->sms->orig_view : rv3d->view;
				rv3d->view= RV3D_VIEW_USER;
			}

			sms.time_allowed= (double)U.smooth_viewtx / 1000.0;
			
			/* if this is view rotation only
				* we can decrease the time allowed by
				* the angle between quats 
				* this means small rotations wont lag */
			if (quat && !ofs && !dist) {
				float vec1[3]={0,0,1}, vec2[3]= {0,0,1};
				float q1[4], q2[4];

				invert_qt_qt(q1, sms.new_quat);
				invert_qt_qt(q2, sms.orig_quat);

				mul_qt_v3(q1, vec1);
				mul_qt_v3(q2, vec2);

				/* scale the time allowed by the rotation */
				sms.time_allowed *= (double)angle_v3v3(vec1, vec2) / M_PI; /* 180deg == 1.0 */
			}

			/* ensure it shows correct */
			if(sms.to_camera) rv3d->persp= RV3D_PERSP;

			rv3d->rflag |= RV3D_NAVIGATING;
			
			/* keep track of running timer! */
			if(rv3d->sms==NULL)
				rv3d->sms= MEM_mallocN(sizeof(struct SmoothViewStore), "smoothview v3d");
			*rv3d->sms= sms;
			if(rv3d->smooth_timer)
				WM_event_remove_timer(wm, win, rv3d->smooth_timer);
			/* TIMER1 is hardcoded in keymap */
			rv3d->smooth_timer= WM_event_add_timer(wm, win, TIMER1, 1.0/100.0);	/* max 30 frs/sec */
			
			ok= TRUE;
		}
	}
	
	/* if we get here nothing happens */
	if(ok == FALSE) {
		if(sms.to_camera==0) {
			copy_v3_v3(rv3d->ofs, sms.new_ofs);
			copy_qt_qt(rv3d->viewquat, sms.new_quat);
			rv3d->dist = sms.new_dist;
			v3d->lens = sms.new_lens;
		}

		if(rv3d->viewlock & RV3D_BOXVIEW)
			view3d_boxview_copy(sa, ar);

		ED_region_tag_redraw(ar);
	}
}