Exemplo n.º 1
0
GF_Err PMF_UnquantizeRotation(PredMF *pmf, GF_FieldInfo *field)
{
	u32 i;
	void *slot;
	Fixed comp[4];
	Fixed tang[3];
	Fixed sine, delta = FIX_ONE;

	for (i=0; i<3; i++) {
		Fixed v = PMF_UnquantizeFloat(pmf->current_val[i] - (1<<(pmf->QNbBits - 1)), 0, FIX_ONE, pmf->QNbBits, 1);
		tang[i] = gf_tan(gf_mulfix(GF_PI / 4, v));
		delta += gf_mulfix(tang[i], tang[i]);
	}
    delta = gf_divfix(pmf->direction , gf_sqrt(delta) );

    comp[(pmf->orientation)%4] = delta;
    for (i=0; i<3; i++) 
		comp[(pmf->orientation + i+1)%4] = gf_mulfix(tang[i], delta);
  
	gf_sg_vrml_mf_get_item(field->far_ptr, field->fieldType, &slot, pmf->cur_field);
	delta = 2 * gf_acos(comp[0]);
	sine = gf_sin(delta / 2);
	if (sine != 0) {
		for(i=1; i<4; i++)
			comp[i] = gf_divfix(comp[i], sine);

		((SFRotation *)slot)->x = comp[1];
		((SFRotation *)slot)->y = comp[2];
		((SFRotation *)slot)->z = comp[3];
	} else {
		((SFRotation *)slot)->x = FIX_ONE;
		((SFRotation *)slot)->y = 0;
		((SFRotation *)slot)->z = 0;
	}
	((SFRotation *)slot)->q = delta;
	return GF_OK;
}
Exemplo n.º 2
0
//parses a rotation
GF_Err Q_DecRotation(GF_BifsDecoder *codec, GF_BitStream *bs, u32 NbBits, void *field_ptr)
{
	u32 i;
	Fixed q, sin2, comp[4];
	GF_Err e;

	e = Q_DecCoordOnUnitSphere(codec, bs, NbBits, 3, comp);
	if (e) return e;

	q = 2 * gf_acos(comp[0]);
	sin2 = gf_sin(q / 2);

	if (ABS(sin2) <= FIX_EPSILON) {
		for (i=1; i<4; i++) comp[i] = 0;
		comp[3] = FIX_ONE;
	} else {
		for (i=1; i<4; i++) comp[i] = gf_divfix(comp[i], sin2);
	}
	((SFRotation *)field_ptr)->x = comp[1];
	((SFRotation *)field_ptr)->y = comp[2];
	((SFRotation *)field_ptr)->z = comp[3];
	((SFRotation *)field_ptr)->q = q;
	return GF_OK;
}
Exemplo n.º 3
0
Arquivo: path2d.c Projeto: erelh/gpac
GF_EXPORT
GF_Err gf_path_add_svg_arc_to(GF_Path *gp, Fixed end_x, Fixed end_y, Fixed r_x, Fixed r_y, Fixed x_axis_rotation, Bool large_arc_flag, Bool sweep_flag)
{
	Fixed start_x, start_y;
	Fixed xmid,ymid;
	Fixed xmidp,ymidp;
	Fixed xmidpsq,ymidpsq;
	Fixed phi, cos_phi, sin_phi;
	Fixed c_x, c_y;
	Fixed cxp, cyp;
	Fixed scale;
	Fixed rxsq, rysq;
	Fixed start_angle, sweep_angle;
	Fixed radius_scale;
	Fixed vx, vy, normv;
	Fixed ux, uy, normu;
	Fixed sign;
	u32 i, num_steps;

	if (!gp->n_points) return GF_BAD_PARAM;

	if (!r_x || !r_y) {
		gf_path_add_line_to(gp, end_x, end_y);
		return GF_OK;
	}

	if (r_x < 0) r_x = -r_x;
	if (r_y < 0) r_y = -r_y;

	start_x = gp->points[gp->n_points-1].x;
	start_y = gp->points[gp->n_points-1].y;

	phi = gf_mulfix(gf_divfix(x_axis_rotation, 180), GF_PI);
	cos_phi = gf_cos(phi);
	sin_phi = gf_sin(phi);
	xmid = (start_x - end_x)/2;
	ymid = (start_y - end_y)/2;
	if (!xmid && !ymid) {
		gf_path_add_line_to(gp, end_x, end_y);
		return GF_OK;
	}

	xmidp = gf_mulfix(cos_phi, xmid) + gf_mulfix(sin_phi, ymid);
	ymidp = gf_mulfix(-sin_phi, xmid) + gf_mulfix(cos_phi, ymid);
	xmidpsq = gf_mulfix(xmidp, xmidp);
	ymidpsq = gf_mulfix(ymidp, ymidp);

	rxsq = gf_mulfix(r_x, r_x);
	rysq = gf_mulfix(r_y, r_y);
	assert(rxsq && rxsq);

	radius_scale = gf_divfix(xmidpsq, rxsq) + gf_divfix(ymidpsq, rysq);
	if (radius_scale > FIX_ONE) {
		r_x = gf_mulfix(gf_sqrt(radius_scale), r_x);
		r_y = gf_mulfix(gf_sqrt(radius_scale), r_y);
		rxsq = gf_mulfix(r_x, r_x);
		rysq = gf_mulfix(r_y, r_y);
	}

#if 0
	/* Old code with overflow problems in fixed point,
	   sign was sometimes negative (cf tango SVG icons appointment-new.svg)*/

	sign = gf_mulfix(rxsq,ymidpsq) + gf_mulfix(rysq, xmidpsq);
	scale = FIX_ONE;
	/*FIXME - what if scale is 0 ??*/
	if (sign) scale = gf_divfix(
					(gf_mulfix(rxsq,rysq) - gf_mulfix(rxsq, ymidpsq) - gf_mulfix(rysq,xmidpsq)),
					sign
			);
#else
	/* New code: the sign variable computation is split into simpler cases and
	   the expression is divided by rxsq to reduce the range */
	if ((rxsq == 0 || ymidpsq ==0) && (rysq == 0 || xmidpsq == 0)) {
		scale = FIX_ONE;
	} else if (rxsq == 0 || ymidpsq ==0) {
		scale = gf_divfix(rxsq,xmidpsq) - FIX_ONE;
	} else if (rysq == 0 || xmidpsq == 0) {
		scale = gf_divfix(rysq,ymidpsq) - FIX_ONE;
	} else {
		Fixed tmp;
		tmp = gf_mulfix(gf_divfix(rysq, rxsq), xmidpsq);
		sign = ymidpsq + tmp;
		scale = gf_divfix((rysq - ymidpsq - tmp),sign);
	}
#endif
	/* precision problem may lead to negative value around zero, we need to take care of it before sqrt */
	scale = gf_sqrt(ABS(scale));

	cxp = gf_mulfix(scale, gf_divfix(gf_mulfix(r_x, ymidp),r_y));
	cyp = gf_mulfix(scale, -gf_divfix(gf_mulfix(r_y, xmidp),r_x));
	cxp = (large_arc_flag == sweep_flag ? - cxp : cxp);
	cyp = (large_arc_flag == sweep_flag ? - cyp : cyp);

	c_x = gf_mulfix(cos_phi, cxp) - gf_mulfix(sin_phi, cyp) + (start_x + end_x)/2;
	c_y = gf_mulfix(sin_phi, cxp) + gf_mulfix(cos_phi, cyp) + (start_y + end_y)/2;

	ux = FIX_ONE;
	uy = 0;
	normu = FIX_ONE;

	vx = gf_divfix(xmidp-cxp,r_x);
	vy = gf_divfix(ymidp-cyp,r_y);
	normv = gf_sqrt(gf_mulfix(vx, vx) + gf_mulfix(vy,vy));

	sign = vy;
	start_angle = gf_acos(gf_divfix(vx,normv));
	start_angle = (sign > 0 ? start_angle: -start_angle);

	ux = vx;
	uy = vy;
	normu = normv;

	vx = gf_divfix(-xmidp-cxp,r_x);
	vy = gf_divfix(-ymidp-cyp,r_y);
	normu = gf_sqrt(gf_mulfix(ux, ux) + gf_mulfix(uy,uy));

	sign = gf_mulfix(ux, vy) - gf_mulfix(uy, vx);
	sweep_angle = gf_divfix( gf_mulfix(ux,vx) + gf_mulfix(uy, vy), gf_mulfix(normu, normv) );
	/*numerical stability safety*/
	sweep_angle = MAX(-FIX_ONE, MIN(sweep_angle, FIX_ONE));
	sweep_angle = gf_acos(sweep_angle);
	sweep_angle = (sign > 0 ? sweep_angle: -sweep_angle);
	if (sweep_flag == 0) {
		if (sweep_angle > 0) sweep_angle -= GF_2PI;
	} else {
		if (sweep_angle < 0) sweep_angle += GF_2PI;
	}

	num_steps = GF_2D_DEFAULT_RES/2;
	for (i=1; i<=num_steps; i++) {
		Fixed _vx, _vy;
		Fixed _vxp, _vyp;
		Fixed angle = start_angle + sweep_angle*i/num_steps;
		_vx = gf_mulfix(r_x, gf_cos(angle));
		_vy = gf_mulfix(r_y, gf_sin(angle));
		_vxp = gf_mulfix(cos_phi, _vx) - gf_mulfix(sin_phi, _vy) + c_x;
		_vyp = gf_mulfix(sin_phi, _vx) + gf_mulfix(cos_phi, _vy) + c_y;
		gf_path_add_line_to(gp, _vxp, _vyp);
	}
	return GF_OK;
}
Exemplo n.º 4
0
static void TraverseBillboard(GF_Node *n, void *rs, Bool is_destroy)
{
	GF_Matrix gf_mx_bckup;
	TransformStack *st = (TransformStack *)gf_node_get_private(n);
	M_Billboard *bb = (M_Billboard *)n;
	GF_TraverseState *tr_state = (GF_TraverseState *)rs;

	if (is_destroy) {
		DestroyTransform(n);
		return;
	}
	if (! tr_state->camera) return;

	/*can't cache the matrix here*/
	gf_mx_init(st->mx);
	if (tr_state->camera->is_3D) {
		SFVec3f z, axis;
		Fixed axis_len;
		SFVec3f user_pos = tr_state->camera->position;

		gf_mx_apply_vec(&tr_state->model_matrix, &user_pos);
		gf_vec_norm(&user_pos);
		axis = bb->axisOfRotation;
		axis_len = gf_vec_len(axis);
		if (axis_len<FIX_EPSILON) {
			SFVec3f x, y, t;
			/*get user's right in local coord*/
			gf_vec_diff(t, tr_state->camera->position, tr_state->camera->target);
			gf_vec_norm(&t);
			x = gf_vec_cross(tr_state->camera->up, t);
			gf_vec_norm(&x);
			gf_mx_rotate_vector(&tr_state->model_matrix, &x);
			gf_vec_norm(&x);
			/*get user's up in local coord*/
			y = tr_state->camera->up;
			gf_mx_rotate_vector(&tr_state->model_matrix, &y);
			gf_vec_norm(&y);
			z = gf_vec_cross(x, y);
			gf_vec_norm(&z);

			gf_mx_rotation_matrix_from_vectors(&st->mx, x, y, z);
			gf_mx_inverse(&st->mx);
		} else {
			SFVec3f tmp;
			Fixed d, cosw, sinw, angle;
			gf_vec_norm(&axis);
			/*map eye & z into plane with normal axis through 0.0*/
			d = -gf_vec_dot(axis, user_pos);
			tmp = gf_vec_scale(axis, d);
			gf_vec_add(user_pos, user_pos, tmp);
			gf_vec_norm(&user_pos);

			z.x = z.y = 0;
			z.z = FIX_ONE;
			d = -gf_vec_dot(axis, z);
			tmp = gf_vec_scale(axis, d);
			gf_vec_add(z, z, tmp);
			gf_vec_norm(&z);

			cosw = gf_vec_dot(user_pos, z);
			tmp = gf_vec_cross(user_pos, z);
			sinw = gf_vec_len(tmp);
			angle = gf_acos(cosw);
			gf_vec_norm(&tmp);
			if ((sinw>0) && (gf_vec_dot(axis, tmp) > 0)) gf_vec_rev(axis);
			gf_mx_add_rotation(&st->mx, angle, axis.x, axis.y, axis.z);
		}
	}

	gf_mx_copy(gf_mx_bckup, tr_state->model_matrix);
	gf_mx_add_matrix(&tr_state->model_matrix, &st->mx);

	/*note we don't clear dirty flag, this is done in traversing*/
	group_3d_traverse(n, (GroupingNode *) st, tr_state);

	gf_mx_copy(tr_state->model_matrix, gf_mx_bckup);

	if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) gf_mx_apply_bbox(&st->mx, &tr_state->bbox);
}
Exemplo n.º 5
0
static void TraverseSound(GF_Node *node, void *rs, Bool is_destroy)
{
	GF_TraverseState *tr_state = (GF_TraverseState*) rs;
	M_Sound *snd = (M_Sound *)node;
	SoundStack *st = (SoundStack *)gf_node_get_private(node);

	if (is_destroy) {
		gf_free(st);
		return;
	}
	if (!snd->source) return;

	tr_state->sound_holder = &st->snd_ifce;

	/*forward in case we're switched off*/
	if (tr_state->switched_off) {
		gf_node_traverse((GF_Node *) snd->source, tr_state);
	}
	else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
		/*we can't cull sound since*/
		tr_state->disable_cull = 1;
	} else if (tr_state->traversing_mode==TRAVERSE_SORT) {
		GF_Matrix mx;
		SFVec3f usr, snd_dir, pos;
		Fixed mag, ang;
		/*this implies no DEF/USE for real location...*/
		gf_mx_copy(st->mx, tr_state->model_matrix);
		gf_mx_copy(mx, tr_state->model_matrix);
		gf_mx_inverse(&mx);

		snd_dir = snd->direction;
		gf_vec_norm(&snd_dir);

		/*get user location*/
		usr = tr_state->camera->position;
		gf_mx_apply_vec(&mx, &usr);

		/*recenter to ellipse focal*/
		gf_vec_diff(usr, usr, snd->location);
		mag = gf_vec_len(usr);
		if (!mag) mag = FIX_ONE/10;
		ang = gf_divfix(gf_vec_dot(snd_dir, usr), mag);
	
		usr.z = gf_mulfix(ang, mag);
		usr.x = gf_sqrt(gf_mulfix(mag, mag) - gf_mulfix(usr.z, usr.z));
		usr.y = 0;
		if (!gf_vec_equal(usr, st->last_pos)) {
			st->intensity = snd_compute_gain(snd->minBack, snd->minFront, snd->maxBack, snd->maxFront, usr);
			st->intensity = gf_mulfix(st->intensity, snd->intensity);
			st->last_pos = usr;
		}
		st->identity = (st->intensity==FIX_ONE) ? 1 : 0;

		if (snd->spatialize) {
			Fixed ang, sign;
			SFVec3f cross;
			pos = snd->location;
			gf_mx_apply_vec(&tr_state->model_matrix, &pos);
			gf_vec_diff(pos, pos, tr_state->camera->position);
			gf_vec_diff(usr, tr_state->camera->target, tr_state->camera->position);
			gf_vec_norm(&pos);
			gf_vec_norm(&usr);

			ang = gf_acos(gf_vec_dot(usr, pos));
			/*get orientation*/
			cross = gf_vec_cross(usr, pos);
			sign = gf_vec_dot(cross, tr_state->camera->up);
			if (sign>0) ang *= -1;
			ang = (FIX_ONE + gf_sin(ang)) / 2;
			st->lgain = (FIX_ONE - gf_mulfix(ang, ang));
			st->rgain = FIX_ONE - gf_mulfix(FIX_ONE - ang, FIX_ONE - ang);
			/*renorm between 0 and 1*/
			st->lgain = gf_mulfix(st->lgain, 4*st->intensity/3);
			st->rgain = gf_mulfix(st->rgain, 4*st->intensity/3);

			if (st->identity && ((st->lgain!=FIX_ONE) || (st->rgain!=FIX_ONE))) st->identity = 0;
		} else {
			st->lgain = st->rgain = FIX_ONE;
		}
		gf_node_traverse((GF_Node *) snd->source, tr_state);
	}

	tr_state->sound_holder = NULL;
}
Exemplo n.º 6
0
static Bool OnCylinderSensor(GF_SensorHandler *sh, Bool is_over, Bool is_cancel, GF_Event *ev, GF_Compositor *compositor)
{
	Bool is_mouse = (ev->type<=GF_EVENT_MOUSEWHEEL) ? 1 : 0;
	M_CylinderSensor *cs = (M_CylinderSensor *)sh->sensor;
	CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->sensor);

	if (cs->isActive && (!cs->enabled
	                     || /*mouse*/((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))
	                     || /*keyboar*/(!is_mouse && (!is_over|| ((ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER))))
	                    ) ) {
		if (cs->autoOffset) {
			cs->offset = cs->rotation_changed.q;
			if (!is_cancel) gf_node_event_out(sh->sensor, 5/*"offset"*/);
		}
		cs->isActive = 0;
		if (!is_cancel) gf_node_event_out(sh->sensor, 6/*"isActive"*/);
		sh->grabbed = 0;
		return is_cancel ? 0 : 1;
	}
	else if (is_mouse) {
		if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
			GF_Ray r;
			SFVec3f yaxis;
			Fixed acute, reva;
			SFVec3f bearing;

			gf_mx_copy(st->init_matrix, compositor->hit_world_to_local);
			/*get initial angle & check disk mode*/
			r = compositor->hit_world_ray;
			gf_vec_add(r.dir, r.orig, r.dir);
			gf_mx_apply_vec(&compositor->hit_world_to_local, &r.orig);
			gf_mx_apply_vec(&compositor->hit_world_to_local, &r.dir);
			gf_vec_diff(bearing, r.orig, r.dir);
			gf_vec_norm(&bearing);
			yaxis.x = yaxis.z = 0;
			yaxis.y = FIX_ONE;
			acute = gf_vec_dot(bearing, yaxis);
			if (acute < -FIX_ONE) acute = -FIX_ONE;
			else if (acute > FIX_ONE) acute = FIX_ONE;
			acute = gf_acos(acute);
			reva = ABS(GF_PI - acute);
			if (reva<acute) acute = reva;
			st->disk_mode = (acute < cs->diskAngle) ? 1 : 0;

			st->grab_start = compositor->hit_local_point;
			/*cos we're lazy*/
			st->yplane.d = 0;
			st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0;
			st->zplane = st->xplane = st->yplane;
			st->xplane.normal.x = FIX_ONE;
			st->yplane.normal.y = FIX_ONE;
			st->zplane.normal.z = FIX_ONE;

			cs->rotation_changed.x = 0;
			cs->rotation_changed.y = FIX_ONE;
			cs->rotation_changed.z = 0;

			cs->isActive = 1;
			gf_node_event_out(sh->sensor, 6/*"isActive"*/);
			sh->grabbed = 1;
			return 1;
		}
		else if (cs->isActive) {
			GF_Ray r;
			Fixed radius, rot;
			SFVec3f dir1, dir2, cx;

			if (is_over) {
				cs->trackPoint_changed = compositor->hit_local_point;
				gf_node_event_out(sh->sensor, 8/*"trackPoint_changed"*/);
			} else {
				GF_Plane project_to;
				r = compositor->hit_world_ray;
				gf_mx_apply_ray(&st->init_matrix, &r);

				/*no intersection, use intersection with "main" fronting plane*/
				if ( ABS(r.dir.z) > ABS(r.dir.y)) {
					if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane;
					else project_to = st->yplane;
				} else {
					if (ABS(r.dir.z) > ABS(r.dir.x)) project_to = st->xplane;
					else project_to = st->zplane;
				}
				if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &compositor->hit_local_point)) return 0;
			}

			dir1.x = compositor->hit_local_point.x;
			dir1.y = 0;
			dir1.z = compositor->hit_local_point.z;
			if (st->disk_mode) {
				radius = FIX_ONE;
			} else {
				radius = gf_vec_len(dir1);
			}
			gf_vec_norm(&dir1);
			dir2.x = st->grab_start.x;
			dir2.y = 0;
			dir2.z = st->grab_start.z;
			gf_vec_norm(&dir2);
			cx = gf_vec_cross(dir2, dir1);
			gf_vec_norm(&cx);
			if (gf_vec_len(cx)<FIX_EPSILON) return 0;
			rot = gf_mulfix(radius, gf_acos(gf_vec_dot(dir2, dir1)) );
			if (fabs(cx.y + FIX_ONE) < FIX_EPSILON) rot = -rot;
			if (cs->autoOffset) rot += cs->offset;

			if (cs->minAngle < cs->maxAngle) {
				if (rot < cs->minAngle) rot = cs->minAngle;
				else if (rot > cs->maxAngle) rot = cs->maxAngle;
			}
			cs->rotation_changed.q = rot;
			gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/);
			return 1;
		}
	} else {
		if (!cs->isActive && is_over && (ev->type==GF_EVENT_KEYDOWN) && (ev->key.key_code==GF_KEY_ENTER)) {
			cs->isActive = 1;
			cs->rotation_changed.q = cs->offset;
			cs->rotation_changed.x = cs->rotation_changed.z = 0;
			cs->rotation_changed.y = FIX_ONE;
			gf_node_event_out(sh->sensor, 6/*"isActive"*/);
			return 1;
		}
		else if (cs->isActive && (ev->type==GF_EVENT_KEYDOWN)) {
			SFFloat res;
			Fixed diff = (ev->key.flags & GF_KEY_MOD_SHIFT) ? GF_PI/8 : GF_PI/64;

			res = cs->rotation_changed.q;
			switch (ev->key.key_code) {
			case GF_KEY_LEFT:
				res -= diff;
				break;
			case GF_KEY_RIGHT:
				res += diff;
				break;
			case GF_KEY_HOME:
				res = cs->offset;
				break;
			default:
				return 0;
			}
			/*clip*/
			if (cs->minAngle <= cs->maxAngle) {
				if (res < cs->minAngle) res = cs->minAngle;
				if (res > cs->maxAngle) res = cs->maxAngle;
			}
			cs->rotation_changed.q = res;
			gf_node_event_out(sh->sensor, 7/*"rotation_changed"*/);
			return 1;
		}
	}
	return 0;
}
Exemplo n.º 7
0
static void OnCylinderSensor(SensorHandler *sh, Bool is_over, GF_Event *ev, RayHitInfo *hit_info)
{
	M_CylinderSensor *cs = (M_CylinderSensor *)sh->owner;
	CylinderSensorStack *st = (CylinderSensorStack *) gf_node_get_private(sh->owner);

	if (cs->isActive && (!cs->enabled || ((ev->type==GF_EVENT_MOUSEUP) && (ev->mouse.button==GF_MOUSE_LEFT))) ) {
		if (cs->autoOffset) {
			cs->offset = cs->rotation_changed.q;
			gf_node_event_out_str(sh->owner, "offset");
		}
		cs->isActive = 0;
		gf_node_event_out_str(sh->owner, "isActive");
		R3D_SetGrabbed(st->compositor, 0);
	}
	else if (!cs->isActive && (ev->type==GF_EVENT_MOUSEDOWN) && (ev->mouse.button==GF_MOUSE_LEFT)) {
		GF_Ray r;
		SFVec3f yaxis;
		Fixed acute, reva;
		SFVec3f bearing;

		gf_mx_copy(st->init_matrix, hit_info->world_to_local);
		/*get initial angle & check disk mode*/
		r = hit_info->world_ray;
		gf_vec_add(r.dir, r.orig, r.dir);
		gf_mx_apply_vec(&hit_info->world_to_local, &r.orig);
		gf_mx_apply_vec(&hit_info->world_to_local, &r.dir);
		gf_vec_diff(bearing, r.orig, r.dir);
		gf_vec_norm(&bearing);
		yaxis.x = yaxis.z = 0;
		yaxis.y = FIX_ONE;
		acute = gf_vec_dot(bearing, yaxis);
		if (acute < -FIX_ONE) acute = -FIX_ONE; 
		else if (acute > FIX_ONE) acute = FIX_ONE;
		acute = gf_acos(acute);
		reva = ABS(GF_PI - acute);
		if (reva<acute) acute = reva;
		st->disk_mode = (acute < cs->diskAngle) ? 1 : 0;

		st->grab_start = hit_info->local_point;
		/*cos we're lazy*/
		st->yplane.d = 0; st->yplane.normal.x = st->yplane.normal.z = st->yplane.normal.y = 0;
		st->zplane = st->xplane = st->yplane;
		st->xplane.normal.x = FIX_ONE;
		st->yplane.normal.y = FIX_ONE;
		st->zplane.normal.z = FIX_ONE;

		cs->rotation_changed.x = 0;
		cs->rotation_changed.y = FIX_ONE;
		cs->rotation_changed.z = 0;

		cs->isActive = 1;
		gf_node_event_out_str(sh->owner, "isActive");
		R3D_SetGrabbed(st->compositor, 1);
	}
	else if (cs->isActive) {
		GF_Ray r;
		Fixed radius, rot;
		SFVec3f dir1, dir2, cx;

		if (is_over) {
			cs->trackPoint_changed = hit_info->local_point;
			gf_node_event_out_str(sh->owner, "trackPoint_changed");
		} else {
			GF_Plane project_to;
			r = hit_info->world_ray;
			gf_mx_apply_ray(&st->init_matrix, &r);

			/*no intersection, use intersection with "main" fronting plane*/
			if ( ABS(r.dir.z) > ABS(r.dir.y)) {
				if (ABS(r.dir.x) > ABS(r.dir.x)) project_to = st->xplane;
				else project_to = st->zplane;
			} else project_to = st->yplane;
			if (!gf_plane_intersect_line(&project_to, &r.orig, &r.dir, &hit_info->local_point)) return;
		}

  		dir1.x = hit_info->local_point.x; dir1.y = 0; dir1.z = hit_info->local_point.z;
		if (st->disk_mode) {
			radius = FIX_ONE;
		} else {
			radius = gf_vec_len(dir1);
		}
		gf_vec_norm(&dir1);
       	dir2.x = st->grab_start.x; dir2.y = 0; dir2.z = st->grab_start.z;
		gf_vec_norm(&dir2);
		cx = gf_vec_cross(dir2, dir1);
		gf_vec_norm(&cx);
		if (gf_vec_len(cx)<FIX_EPSILON) return;
		rot = gf_mulfix(radius, gf_acos(gf_vec_dot(dir2, dir1)) );
		if (fabs(cx.y + FIX_ONE) < FIX_EPSILON) rot = -rot;
		if (cs->autoOffset) rot += cs->offset;

		if (cs->minAngle < cs->maxAngle) {
			if (rot < cs->minAngle) rot = cs->minAngle;
			else if (rot > cs->maxAngle) rot = cs->maxAngle;
		}
		cs->rotation_changed.q = rot;
		gf_node_event_out_str(sh->owner, "rotation_changed");
	}
}