/**
 * $Id: editarmature.c 17146 2008-10-21 08:53:39Z aligorith $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 * editarmature.c: Interface for creating and posing armature objects
 */

#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <math.h> 

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "MEM_guardedalloc.h"

#include "BMF_Api.h"

#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_ID.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_nla_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_view3d_types.h"
#include "DNA_modifier_types.h"
#include "DNA_ipo_types.h"
#include "DNA_curve_types.h"

#include "BLI_blenlib.h"
#include "BLI_arithb.h"
#include "BLI_editVert.h"
#include "BLI_ghash.h"

#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_deform.h"
#include "BKE_depsgraph.h"
#include "BKE_DerivedMesh.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_subsurf.h"
#include "BKE_utildefines.h"
#include "BKE_modifier.h"

#include "BIF_editaction.h"
#include "BIF_editmode_undo.h"
#include "BIF_editdeform.h"
#include "BIF_editarmature.h"
#include "BIF_editconstraint.h"
#include "BIF_gl.h"
#include "BIF_graphics.h"
#include "BIF_interface.h"
#include "BIF_meshlaplacian.h"
#include "BIF_meshtools.h"
#include "BIF_poseobject.h"
#include "BIF_mywindow.h"
#include "BIF_resources.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BIF_transform.h"

#include "BDR_editobject.h"
#include "BDR_drawobject.h"

#include "BSE_edit.h"
#include "BSE_view.h"
#include "BSE_trans_types.h"

#include "PIL_time.h"

#include "reeb.h" // FIX ME

#include "mydevice.h"
#include "blendef.h"
#include "nla.h"

extern	float center[3], centroid[3];	/* Originally defined in editobject.c */

/*	Macros	*/
#define TEST_EDITARMATURE {if(G.obedit==0) return; if( (G.vd->lay & G.obedit->lay)==0 ) return;}

/* prototypes for later */
static EditBone *editbone_name_exists (ListBase *ebones, char *name);	// proto for below

/* **************** tools on Editmode Armature **************** */

/* converts Bones to EditBone list, used for tools as well */
void make_boneList(ListBase *list, ListBase *bones, EditBone *parent)
{
	EditBone	*eBone;
	Bone		*curBone;
	float delta[3];
	float premat[3][3];
	float postmat[3][3];
	float imat[3][3];
	float difmat[3][3];
		
	for (curBone=bones->first; curBone; curBone=curBone->next) {
		eBone= MEM_callocN(sizeof(EditBone), "make_editbone");
		
		/*	Copy relevant data from bone to eBone */
		eBone->parent= parent;
		BLI_strncpy(eBone->name, curBone->name, 32);
		eBone->flag = curBone->flag;
		
		/* fix selection flags */
		if (eBone->flag & BONE_SELECTED) {
			eBone->flag |= BONE_TIPSEL;
			if (eBone->parent && (eBone->flag & BONE_CONNECTED))
				eBone->parent->flag |= BONE_TIPSEL;
			else 
				eBone->flag |= BONE_ROOTSEL;
		}
		else 
			eBone->flag &= ~BONE_ROOTSEL;
		
		VECCOPY(eBone->head, curBone->arm_head);
		VECCOPY(eBone->tail, curBone->arm_tail);		
		
		eBone->roll= 0.0;
		
		/* roll fixing */
		VecSubf(delta, eBone->tail, eBone->head);
		vec_roll_to_mat3(delta, 0.0, postmat);
		
		Mat3CpyMat4(premat, curBone->arm_mat);
		
		Mat3Inv(imat, postmat);
		Mat3MulMat3(difmat, imat, premat);
		
		eBone->roll = atan2(difmat[2][0], difmat[2][2]);
		
		/* rest of stuff copy */
		eBone->length= curBone->length;
		eBone->dist= curBone->dist;
		eBone->weight= curBone->weight;
		eBone->xwidth= curBone->xwidth;
		eBone->zwidth= curBone->zwidth;
		eBone->ease1= curBone->ease1;
		eBone->ease2= curBone->ease2;
		eBone->rad_head= curBone->rad_head;
		eBone->rad_tail= curBone->rad_tail;
		eBone->segments = curBone->segments;		
		eBone->layer = curBone->layer;
		
		BLI_addtail(list, eBone);
		
		/*	Add children if necessary */
		if (curBone->childbase.first) 
			make_boneList(list, &curBone->childbase, eBone);
	}
}

/* nasty stuff for converting roll in editbones into bones */
/* also sets restposition in armature (arm_mat) */
static void fix_bonelist_roll (ListBase *bonelist, ListBase *editbonelist)
{
	Bone *curBone;
	EditBone *ebone;
	float premat[3][3];
	float postmat[3][3];
	float difmat[3][3];
	float imat[3][3];
	float delta[3];
	
	for (curBone=bonelist->first; curBone; curBone=curBone->next) {
		/* sets local matrix and arm_mat (restpos) */
		where_is_armature_bone(curBone, curBone->parent);
		
		/* Find the associated editbone */
		for (ebone = editbonelist->first; ebone; ebone=ebone->next)
			if ((Bone*)ebone->temp == curBone)
				break;
		
		if (ebone) {
			/* Get the ebone premat */
			VecSubf(delta, ebone->tail, ebone->head);
			vec_roll_to_mat3(delta, ebone->roll, premat);
			
			/* Get the bone postmat */
			Mat3CpyMat4(postmat, curBone->arm_mat);
			
			Mat3Inv(imat, premat);
			Mat3MulMat3(difmat, imat, postmat);
#if 0
			printf ("Bone %s\n", curBone->name);
			printmatrix4("premat", premat);
			printmatrix4("postmat", postmat);
			printmatrix4("difmat", difmat);
			printf ("Roll = %f\n",  (-atan2(difmat[2][0], difmat[2][2]) * (180.0/M_PI)));
#endif
			curBone->roll = -atan2(difmat[2][0], difmat[2][2]);
			
			/* and set restposition again */
			where_is_armature_bone(curBone, curBone->parent);
		}
		fix_bonelist_roll(&curBone->childbase, editbonelist);
	}
}

/* converts the editbones back to the armature */
void editbones_to_armature (ListBase *list, Object *ob)
{
	bArmature *arm;
	EditBone *eBone, *neBone;
	Bone	*newBone;
	Object *obt;
	
	arm = get_armature(ob);
	if (!list) return;
	if (!arm) return;
	
	/* armature bones */
	free_bones(arm);
	
	/* remove zero sized bones, this gives instable restposes */
	for (eBone=list->first; eBone; eBone= neBone) {
		float len= VecLenf(eBone->head, eBone->tail);
		neBone= eBone->next;
		if (len <= FLT_EPSILON) {
			EditBone *fBone;
			
			/*	Find any bones that refer to this bone	*/
			for (fBone=list->first; fBone; fBone= fBone->next) {
				if (fBone->parent==eBone)
					fBone->parent= eBone->parent;
			}
			printf("Warning: removed zero sized bone: %s\n", eBone->name);
			BLI_freelinkN(list, eBone);
		}
	}
	
	/*	Copy the bones from the editData into the armature */
	for (eBone=list->first; eBone; eBone=eBone->next) {
		newBone= MEM_callocN(sizeof(Bone), "bone");
		eBone->temp= newBone;	/* Associate the real Bones with the EditBones */
		
		BLI_strncpy(newBone->name, eBone->name, 32);
		memcpy(newBone->head, eBone->head, sizeof(float)*3);
		memcpy(newBone->tail, eBone->tail, sizeof(float)*3);
		newBone->flag= eBone->flag;
		if (eBone->flag & BONE_ACTIVE) 
			newBone->flag |= BONE_SELECTED;	/* important, editbones can be active with only 1 point selected */
		newBone->roll = 0.0f;
		
		newBone->weight = eBone->weight;
		newBone->dist = eBone->dist;
		
		newBone->xwidth = eBone->xwidth;
		newBone->zwidth = eBone->zwidth;
		newBone->ease1= eBone->ease1;
		newBone->ease2= eBone->ease2;
		newBone->rad_head= eBone->rad_head;
		newBone->rad_tail= eBone->rad_tail;
		newBone->segments= eBone->segments;
		newBone->layer = eBone->layer;
	}
	
	/*	Fix parenting in a separate pass to ensure ebone->bone connections
		are valid at this point */
	for (eBone=list->first;eBone;eBone=eBone->next) {
		newBone= (Bone *)eBone->temp;
		if (eBone->parent) {
			newBone->parent= (Bone *)eBone->parent->temp;
			BLI_addtail(&newBone->parent->childbase, newBone);
			
			{
				float M_boneRest[3][3];
				float M_parentRest[3][3];
				float iM_parentRest[3][3];
				float	delta[3];
				
				/* Get the parent's  matrix (rotation only) */
				VecSubf(delta, eBone->parent->tail, eBone->parent->head);
				vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
				
				/* Get this bone's  matrix (rotation only) */
				VecSubf(delta, eBone->tail, eBone->head);
				vec_roll_to_mat3(delta, eBone->roll, M_boneRest);
				
				/* Invert the parent matrix */
				Mat3Inv(iM_parentRest, M_parentRest);
				
				/* Get the new head and tail */
				VecSubf(newBone->head, eBone->head, eBone->parent->tail);
				VecSubf(newBone->tail, eBone->tail, eBone->parent->tail);
				
				Mat3MulVecfl(iM_parentRest, newBone->head);
				Mat3MulVecfl(iM_parentRest, newBone->tail);
			}
		}
		/*	...otherwise add this bone to the armature's bonebase */
		else
			BLI_addtail(&arm->bonebase, newBone);
	}
	
	/* Make a pass through the new armature to fix rolling */
	/* also builds restposition again (like where_is_armature) */
	fix_bonelist_roll(&arm->bonebase, list);
	
	/* so all users of this armature should get rebuilt */
	for (obt= G.main->object.first; obt; obt= obt->id.next) {
		if (obt->data==arm)
			armature_rebuild_pose(obt, arm);
	}
	
	DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
}



void apply_rot_armature (Object *ob, float mat[3][3])
{
	ListBase	list;
	EditBone *ebone;
	bArmature *arm;
	float scale = Mat3ToScalef(mat);	/* store the scale of the matrix here to use on envelopes */
	arm = get_armature(ob);

	if (!arm)
		return;	
	
	/* Put the armature into editmode */
	list.first= list.last = NULL;
	make_boneList(&list, &arm->bonebase, NULL);

	/* Do the rotations */
	for (ebone = list.first; ebone; ebone=ebone->next){
		Mat3MulVecfl(mat, ebone->head);
		Mat3MulVecfl(mat, ebone->tail);
		
		ebone->rad_head	*= scale;
		ebone->rad_tail	*= scale;
		ebone->dist		*= scale;
	}
	
	/* Turn the list into an armature */
	editbones_to_armature(&list, ob);
	
	/* Free the editbones */
	if (list.first){
		BLI_freelistN (&list);
	}
}

/* 0 == do center, 1 == center new, 2 == center cursor */
void docenter_armature (Object *ob, int centermode)
{
	ListBase	list;
	EditBone *ebone;
	bArmature *arm;
	float cent[3] = {0.0f, 0.0f, 0.0f};
	float min[3], max[3];
	float omat[3][3];
		
	arm = get_armature(ob);
	if (!arm) return;

	/* Put the armature into editmode */
	list.first= list.last = NULL;
	make_boneList(&list, &arm->bonebase, NULL);

	/* Find the centerpoint */
	if (centermode == 2) {
		VECCOPY(cent, give_cursor());
		Mat4Invert(ob->imat, ob->obmat);
		Mat4MulVecfl(ob->imat, cent);
	}
	else {
		INIT_MINMAX(min, max);
		
		for (ebone= list.first; ebone; ebone=ebone->next) {
			DO_MINMAX(ebone->head, min, max);
			DO_MINMAX(ebone->tail, min, max);
		}
		
		cent[0]= (min[0]+max[0])/2.0f;
		cent[1]= (min[1]+max[1])/2.0f;
		cent[2]= (min[2]+max[2])/2.0f;
	}
	
	/* Do the adjustments */
	for (ebone= list.first; ebone; ebone=ebone->next){
		VecSubf(ebone->head, ebone->head, cent);
		VecSubf(ebone->tail, ebone->tail, cent);
	}
	
	/* Turn the list into an armature */
	editbones_to_armature(&list, ob);
	
	/* Free the editbones */
	if (list.first){
		BLI_freelistN(&list);
	}
	
	/* Adjust object location for new centerpoint */
	if(centermode && G.obedit==0) {
		Mat3CpyMat4(omat, ob->obmat);
		
		Mat3MulVecfl(omat, cent);
		ob->loc[0]+= cent[0];
		ob->loc[1]+= cent[1];
		ob->loc[2]+= cent[2];
	}
}

/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */
static void applyarmature_fix_boneparents (Object *armob)
{
	Object *ob;
	
	/* go through all objects in database */
	for (ob= G.main->object.first; ob; ob= ob->id.next) {
		/* if parent is bone in this armature, apply corrections */
		if ((ob->parent == armob) && (ob->partype == PARBONE)) {
			/* apply current transform from parent (not yet destroyed), 
			 * then calculate new parent inverse matrix
			 */
			apply_obmat(ob);
			
			what_does_parent(ob);
			Mat4Invert(ob->parentinv, workob.obmat);
		}
	}
}

/* set the current pose as the restpose */
void apply_armature_pose2bones(void)
{
	Object	*ob;
	bArmature *arm;
	bPose *pose;
	bPoseChannel *pchan;
	EditBone *curbone;
	
	/* don't check if editmode (should be done by caller) */
	ob= OBACT;
	if (ob->type!=OB_ARMATURE) return;
	if (object_data_is_libdata(ob)) {
		error_libdata();
		return;
	}
	arm= get_armature(ob); 
	
	/* helpful warnings... */
	// TODO: add warnings to be careful about actions, applying deforms first, etc.
	
	/* Get editbones of active armature to alter */
	if (G.edbo.first) BLI_freelistN(&G.edbo);
	make_boneList(&G.edbo, &arm->bonebase, NULL);
	
	/* get pose of active object and move it out of posemode */
	pose= ob->pose;
	
	for (pchan=pose->chanbase.first; pchan; pchan=pchan->next) {
		curbone= editbone_name_exists(&G.edbo, pchan->name);
		
		/* simply copy the head/tail values from pchan over to curbone */
		VECCOPY(curbone->head, pchan->pose_head);
		VECCOPY(curbone->tail, pchan->pose_tail);
		
		/* fix roll:
		 *	1. find auto-calculated roll value for this bone now
		 *	2. remove this from the 'visual' y-rotation
		 */
		{
			float premat[3][3], imat[3][3],pmat[3][3], tmat[3][3];
			float delta[3], eul[3];
			
			/* obtain new auto y-rotation */
			VecSubf(delta, curbone->tail, curbone->head);
			vec_roll_to_mat3(delta, 0.0, premat);
			Mat3Inv(imat, premat);
			
			/* get pchan 'visual' matrix */
			Mat3CpyMat4(pmat, pchan->pose_mat);
			
			/* remove auto from visual and get euler rotation */
			Mat3MulMat3(tmat, imat, pmat);
			Mat3ToEul(tmat, eul);
			
			/* just use this euler-y as new roll value */
			curbone->roll= eul[1];
		}
		
		/* clear transform values for pchan */
		pchan->loc[0]= pchan->loc[1]= pchan->loc[2]= 0;
		pchan->quat[1]= pchan->quat[2]= pchan->quat[3]= 0;
		pchan->quat[0]= pchan->size[0]= pchan->size[1]= pchan->size[2]= 1;
		
		/* set anim lock */
		curbone->flag |= BONE_UNKEYED;
	}
	
	/* convert editbones back to bones */
	editbones_to_armature(&G.edbo, ob);
	if (G.edbo.first) BLI_freelistN(&G.edbo);
	
	/* flush positions of posebones */
	where_is_pose(ob);
	
	/* fix parenting of objects which are bone-parented */
	applyarmature_fix_boneparents(ob);
	
	BIF_undo_push("Apply new restpose");
	allqueue(REDRAWVIEW3D, 0);
}


/* Helper function for armature joining - link fixing */
static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
{
	Object *ob;
	bPose *pose;
	bPoseChannel *pchant;
	bConstraint *con;
	
	/* let's go through all objects in database */
	for (ob= G.main->object.first; ob; ob= ob->id.next) {
		/* do some object-type specific things */
		if (ob->type == OB_ARMATURE) {
			pose= ob->pose;
			for (pchant= pose->chanbase.first; pchant; pchant= pchant->next) {
				for (con= pchant->constraints.first; con; con= con->next) {
					bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					/* constraint targets */
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct= targets.first; ct; ct= ct->next) {
							if (ct->tar == srcArm) {
								if (strcmp(ct->subtarget, "")==0) {
									ct->tar = tarArm;
								}
								else if (strcmp(ct->subtarget, pchan->name)==0) {
									ct->tar = tarArm;
									strcpy(ct->subtarget, curbone->name);
								}
							}
						}
						
						if (cti->flush_constraint_targets)
							cti->flush_constraint_targets(con, &targets, 0);
					}
					
					/* action constraint? */
					if (con->type == CONSTRAINT_TYPE_ACTION) {
						bActionConstraint *data= con->data;
						bAction *act;
						bActionChannel *achan;
						
						if (data->act) {
							act= data->act;
							
							for (achan= act->chanbase.first; achan; achan= achan->next) {
								if (strcmp(achan->name, pchan->name)==0)
									BLI_strncpy(achan->name, curbone->name, 32);
							}
						}
					}
					
				}
			}
		}
			
		/* fix object-level constraints */
		if (ob != srcArm) {
			for (con= ob->constraints.first; con; con= con->next) {
				bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				/* constraint targets */
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(con, &targets);
					
					for (ct= targets.first; ct; ct= ct->next) {
						if (ct->tar == srcArm) {
							if (strcmp(ct->subtarget, "")==0) {
								ct->tar = tarArm;
							}
							else if (strcmp(ct->subtarget, pchan->name)==0) {
								ct->tar = tarArm;
								strcpy(ct->subtarget, curbone->name);
							}
						}
					}
					
					if (cti->flush_constraint_targets)
						cti->flush_constraint_targets(con, &targets, 0);
				}
			}
		}
		
		/* See if an object is parented to this armature */
		if (ob->parent && (ob->parent == srcArm)) {
			/* Is object parented to a bone of this src armature? */
			if (ob->partype==PARBONE) {
				/* bone name in object */
				if (!strcmp(ob->parsubstr, pchan->name))
					BLI_strncpy(ob->parsubstr, curbone->name, 32);
			}
			
			/* make tar armature be new parent */
			ob->parent = tarArm;
		}
	}	
}

int join_armature(void)
{
	Object	*ob;
	bArmature *arm;
	Base	*base, *nextbase;
	bPose *pose, *opose;
	bPoseChannel *pchan, *pchann;
	ListBase ebbase, eblist;
	EditBone *curbone;
	float	mat[4][4], oimat[4][4];
	
	/*	Ensure we're not in editmode and that the active object is an armature*/
	/* if(G.obedit) return; */ /* Alredy checked in join_menu() */
	
	ob= OBACT;
	if (ob->type!=OB_ARMATURE) return 0;
	if (object_data_is_libdata(ob)) {
		error_libdata();
		return 0;
	}
	arm= get_armature(ob); 
	
	/* Get editbones of active armature to add editbones to */
	ebbase.first=ebbase.last= NULL;
	make_boneList(&ebbase, &arm->bonebase, NULL);
	
	/* get pose of active object and move it out of posemode */
	pose= ob->pose;
	ob->flag &= ~OB_POSEMODE;
	BASACT->flag &= ~OB_POSEMODE;
	
	for (base=FIRSTBASE; base; base=nextbase) {
		nextbase = base->next;
		if (TESTBASE(base)){
			if ((base->object->type==OB_ARMATURE) && (base->object!=ob)) {
				/* Make a list of editbones in current armature */
				eblist.first=eblist.last= NULL;
				make_boneList(&eblist, &((bArmature *)base->object->data)->bonebase, NULL);
				
				/* Get Pose of current armature */
				opose= base->object->pose;
				base->object->flag &= ~OB_POSEMODE;
				BASACT->flag &= ~OB_POSEMODE;
				
				/* Find the difference matrix */
				Mat4Invert(oimat, ob->obmat);
				Mat4MulMat4(mat, base->object->obmat, oimat);
				
				/* Copy bones and posechannels from the object to the edit armature */
				for (pchan=opose->chanbase.first; pchan; pchan=pchann) {
					pchann= pchan->next;
					curbone= editbone_name_exists(&eblist, pchan->name);
					
					/* Get new name */
					unique_editbone_name(&ebbase, curbone->name);
					
					/* Transform the bone */
					{
						float premat[4][4];
						float postmat[4][4];
						float difmat[4][4];
						float imat[4][4];
						float temp[3][3];
						float delta[3];
						
						/* Get the premat */
						VecSubf(delta, curbone->tail, curbone->head);
						vec_roll_to_mat3(delta, curbone->roll, temp);
						
						Mat4MulMat34(premat, temp, mat);
						
						Mat4MulVecfl(mat, curbone->head);
						Mat4MulVecfl(mat, curbone->tail);
						
						/* Get the postmat */
						VecSubf(delta, curbone->tail, curbone->head);
						vec_roll_to_mat3(delta, curbone->roll, temp);
						Mat4CpyMat3(postmat, temp);
						
						/* Find the roll */
						Mat4Invert(imat, premat);
						Mat4MulMat4(difmat, postmat, imat);
						
						curbone->roll -= atan2(difmat[2][0], difmat[2][2]);
					}
					
					/* Fix Constraints and Other Links to this Bone and Armature */
					joined_armature_fix_links(ob, base->object, pchan, curbone);
					
					/* Rename pchan */
					sprintf(pchan->name, curbone->name);
					
					/* Jump Ship! */
					BLI_remlink(&eblist, curbone);
					BLI_addtail(&ebbase, curbone);
					
					BLI_remlink(&opose->chanbase, pchan);
					BLI_addtail(&pose->chanbase, pchan);
				}
				
				free_and_unlink_base(base);
			}
		}
	}
	
	DAG_scene_sort(G.scene);	// because we removed object(s)
	
	editbones_to_armature(&ebbase, ob);
	if (ebbase.first) BLI_freelistN(&ebbase);
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWOOPS, 0);
	return 1;
}

/* Helper function for armature separating - link fixing */
static void separated_armature_fix_links(Object *origArm, Object *newArm)
{
	Object *ob;
	bPoseChannel *pchan, *pcha, *pchb;
	bConstraint *con;
	ListBase *opchans, *npchans;
	
	/* get reference to list of bones in original and new armatures  */
	opchans= &origArm->pose->chanbase;
	npchans= &newArm->pose->chanbase;
	
	/* let's go through all objects in database */
	for (ob= G.main->object.first; ob; ob= ob->id.next) {
		/* do some object-type specific things */
		if (ob->type == OB_ARMATURE) {
			for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
				for (con= pchan->constraints.first; con; con= con->next) {
					bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					/* constraint targets */
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct= targets.first; ct; ct= ct->next) {
							/* any targets which point to original armature are redirected to the new one only if:
							 *	- the target isn't origArm/newArm itself
							 *	- the target is one that can be found in newArm/origArm
							 */
							if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
								for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
									/* check if either one matches */
									if ( (strcmp(pcha->name, ct->subtarget)==0) ||
										 (strcmp(pchb->name, ct->subtarget)==0) )
									{
										ct->tar= newArm;
										break;
									}
									
									/* check if both ends have met (to stop checking) */
									if (pcha == pchb) break;
								}								
							}
							else if ((ct->tar == newArm) && (ct->subtarget[0] != 0)) {
								for (pcha=opchans->first, pchb=opchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
									/* check if either one matches */
									if ( (strcmp(pcha->name, ct->subtarget)==0) ||
										 (strcmp(pchb->name, ct->subtarget)==0) )
									{
										ct->tar= origArm;
										break;
									}
									
									/* check if both ends have met (to stop checking) */
									if (pcha == pchb) break;
								}								
							}
						}
						
						if (cti->flush_constraint_targets)
							cti->flush_constraint_targets(con, &targets, 0);
					}
				}
			}
		}
			
		/* fix object-level constraints */
		if (ob != origArm) {
			for (con= ob->constraints.first; con; con= con->next) {
				bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				/* constraint targets */
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(con, &targets);
					
					for (ct= targets.first; ct; ct= ct->next) {
						/* any targets which point to original armature are redirected to the new one only if:
						 *	- the target isn't origArm/newArm itself
						 *	- the target is one that can be found in newArm/origArm
						 */
						if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
							for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
								/* check if either one matches */
								if ( (strcmp(pcha->name, ct->subtarget)==0) ||
									 (strcmp(pchb->name, ct->subtarget)==0) )
								{
									ct->tar= newArm;
									break;
								}
								
								/* check if both ends have met (to stop checking) */
								if (pcha == pchb) break;
							}								
						}
						else if ((ct->tar == newArm) && (ct->subtarget[0] != 0)) {
							for (pcha=opchans->first, pchb=opchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
								/* check if either one matches */
								if ( (strcmp(pcha->name, ct->subtarget)==0) ||
									 (strcmp(pchb->name, ct->subtarget)==0) )
								{
									ct->tar= origArm;
									break;
								}
								
								/* check if both ends have met (to stop checking) */
								if (pcha == pchb) break;
							}								
						}
					}
					
					if (cti->flush_constraint_targets)
						cti->flush_constraint_targets(con, &targets, 0);
				}
			}
		}
		
		/* See if an object is parented to this armature */
		if ((ob->parent) && (ob->parent == origArm)) {
			/* Is object parented to a bone of this src armature? */
			if (ob->partype==PARBONE) {
				/* bone name in object */
				for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
					/* check if either one matches */
					if ( (strcmp(pcha->name, ob->parsubstr)==0) ||
						 (strcmp(pchb->name, ob->parsubstr)==0) )
					{
						ob->parent= newArm;
						break;
					}
					
					/* check if both ends have met (to stop checking) */
					if (pcha == pchb) break;
				}
			}
		}
	}	
}

/* Helper function for armature separating - remove certain bones from the given armature 
 *	sel: remove selected bones from the armature, otherwise the unselected bones are removed
 */
static void separate_armature_bones (Object *ob, short sel) 
{
	ListBase edbo = {NULL, NULL};
	bArmature *arm= (bArmature *)ob->data;
	bPoseChannel *pchan, *pchann;
	EditBone *curbone;
	
	/* make local set of editbones to manipulate here */
	make_boneList(&edbo, &arm->bonebase, NULL);
	
	/* go through pose-channels, checking if a bone should be removed */
	for (pchan=ob->pose->chanbase.first; pchan; pchan=pchann) {
		pchann= pchan->next;
		curbone= editbone_name_exists(&edbo, pchan->name);
		
		/* check if bone needs to be removed */
		if ( (sel && (curbone->flag & BONE_SELECTED)) ||
			 (!sel && !(curbone->flag & BONE_SELECTED)) )
		{
			EditBone *ebo;
			bPoseChannel *pchn;
			
			/* clear the bone->parent var of any bone that had this as its parent  */
			for (ebo= edbo.first; ebo; ebo= ebo->next) {
				if (ebo->parent == curbone) {
					ebo->parent= NULL;
					ebo->temp= NULL; /* this is needed to prevent random crashes with in editbones_to_armature */
					ebo->flag &= ~BONE_CONNECTED;
				}
			}
			
			/* clear the pchan->parent var of any pchan that had this as its parent */
			for (pchn= ob->pose->chanbase.first; pchn; pchn=pchn->next) {
				if (pchn->parent == pchan)
					pchn->parent= NULL;
			}
			
			/* free any of the extra-data this pchan might have */
			if (pchan->path) MEM_freeN(pchan->path);
			free_constraints(&pchan->constraints);
			
			/* get rid of unneeded bone */
			BLI_freelinkN(&edbo, curbone);
			BLI_freelinkN(&ob->pose->chanbase, pchan);
		}
	}
	
	/* exit editmode (recalculates pchans too) */
	editbones_to_armature(&edbo, ob);
	BLI_freelistN(&edbo);
}

/* separate selected bones into their armature */
void separate_armature (void)
{
	Object *oldob, *newob;
	Base *base, *oldbase, *newbase;
	bArmature *arm;
	
	if ( G.vd==0 || (G.vd->lay & G.obedit->lay)==0 ) return;
	if ( okee("Separate")==0 ) return;

	waitcursor(1);
	
	arm= G.obedit->data;
	
	/* we are going to do this as follows (unlike every other instance of separate):
	 *	1. exit editmode +posemode for active armature/base. Take note of what this is.
	 *	2. duplicate base - BASACT is the new one now
	 *	3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc
	 *	4. fix constraint links
	 *	5. make original armature active and enter editmode
	 */
	
	/* 1) only edit-base selected */
	base= FIRSTBASE;
	for (base= FIRSTBASE; base; base= base->next) {
		if (base->lay & G.vd->lay) {
			if (base->object==G.obedit) base->flag |= 1;
			else base->flag &= ~1;
		}
	}
	
	/* 1) store starting settings and exit editmode */
	oldob= G.obedit;
	oldbase= BASACT;
	oldob->flag &= ~OB_POSEMODE;
	oldbase->flag &= ~OB_POSEMODE;
	
	load_editArmature();
	free_editArmature();
	
	/* 2) duplicate base */
	adduplicate(1, USER_DUP_ARM); /* no transform and zero so do get a linked dupli */
	
	newbase= BASACT; /* basact is set in adduplicate() */
	newob= newbase->object;		
	newbase->flag &= ~SELECT;
	
	
	/* 3) remove bones that shouldn't still be around on both armatures */
	separate_armature_bones(oldob, 1);
	separate_armature_bones(newob, 0);
	
	
	/* 4) fix links before depsgraph flushes */ // err... or after?
	separated_armature_fix_links(oldob, newob);
	
	DAG_object_flush_update(G.scene, oldob, OB_RECALC_DATA);	/* this is the original one */
	DAG_object_flush_update(G.scene, newob, OB_RECALC_DATA);	/* this is the separated one */
	
	
	/* 5) restore original conditions */
	G.obedit= oldob;
	BASACT= oldbase;
	BASACT->flag |= SELECT;
	
	make_editArmature();
	
	/* recalc/redraw + cleanup */
	waitcursor(0);

	countall();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWOOPS, 0);
	
	BIF_undo_push("Separate Armature");
}

/* **************** END tools on Editmode Armature **************** */
/* **************** PoseMode & EditMode *************************** */

/* only for opengl selection indices */
Bone *get_indexed_bone (Object *ob, int index)
{
	bPoseChannel *pchan;
	int a= 0;
	
	if(ob->pose==NULL) return NULL;
	index>>=16;		// bone selection codes use left 2 bytes
	
	for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
		if(a==index) return pchan->bone;
	}
	return NULL;
}

/* See if there are any selected bones in this buffer */
static void *get_bone_from_selectbuffer(Base *base, unsigned int *buffer, short hits, short findunsel)
{
	Object *ob= base->object;
	Bone *bone;
	EditBone *ebone;
	void *firstunSel=NULL, *firstSel=NULL, *data;
	unsigned int hitresult;
	short i, takeNext=0, sel;
	
	for (i=0; i< hits; i++){
		hitresult = buffer[3+(i*4)];
		
		if (!(hitresult & BONESEL_NOSEL)) {	// -1
			if(hitresult & BONESEL_ANY) {	// to avoid including objects in selection
				
				hitresult &= ~(BONESEL_ANY);
				/* Determine what the current bone is */
				if (G.obedit==NULL || base->object!=G.obedit) {
					/* no singular posemode, so check for correct object */
					if(base->selcol == (hitresult & 0xFFFF)) {
						bone = get_indexed_bone(ob, hitresult);

						if (findunsel)
							sel = (bone->flag & BONE_SELECTED);
						else
							sel = !(bone->flag & BONE_SELECTED);
						
						data = bone;
					}
					else {
						data= NULL;
						sel= 0;
					}
				}
				else{
					ebone = BLI_findlink(&G.edbo, hitresult);
					if (findunsel)
						sel = (ebone->flag & BONE_SELECTED);
					else
						sel = !(ebone->flag & BONE_SELECTED);
					
					data = ebone;
				}
				
				if(data) {
					if (sel) {
						if(!firstSel) firstSel= data;
						takeNext=1;
					}
					else {
						if (!firstunSel)
							firstunSel=data;
						if (takeNext)
							return data;
					}
				}
			}
		}
	}
	
	if (firstunSel)
		return firstunSel;
	else 
		return firstSel;
}

/* used by posemode as well editmode */
static void *get_nearest_bone (short findunsel)
{
	unsigned int buffer[MAXPICKBUF];
	short hits;
	
	persp(PERSP_VIEW);
	
	glInitNames();
	hits= view3d_opengl_select(buffer, MAXPICKBUF, 0, 0, 0, 0);

	if (hits>0)
		return get_bone_from_selectbuffer(BASACT, buffer, hits, findunsel);
	
	return NULL;
}

/* helper for setflag_sel_bone() */
static void bone_setflag (int *bone, int flag, short mode)
{
	if (bone && flag) {
		/* exception for inverse flags */
		if (flag == BONE_NO_DEFORM) {
			if (mode == 2)
				*bone |= flag;
			else if (mode == 1)
				*bone &= ~flag;
			else
				*bone ^= flag;

		}
		else {
			if (mode == 2)
				*bone &= ~flag;
			else if (mode == 1)
				*bone |= flag;
			else
				*bone ^= flag;
		}
	}
}

/* Get the first available child of an editbone */
static EditBone *editbone_get_child(EditBone *pabone, short use_visibility)
{
	Object *ob;
	bArmature *arm;
	EditBone *curbone, *chbone=NULL;
	
	if (!G.obedit) return NULL;
	else ob= G.obedit;
	arm= (bArmature *)ob->data;

	for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
		if (curbone->parent == pabone) {
			if (use_visibility) {
				if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A))
					chbone = curbone;
			}
			else
				chbone = curbone;
		}
	}
	
	return chbone;
}

void armature_select_hierarchy(short direction, short add_to_sel)
{
	Object *ob;
	bArmature *arm;
	EditBone *curbone, *pabone, *chbone;

	if (!G.obedit) return;
	else ob= G.obedit;
	arm= (bArmature *)ob->data;
	
	for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
		if (EBONE_VISIBLE(arm, curbone)) {
			if (curbone->flag & (BONE_ACTIVE)) {
				if (direction == BONE_SELECT_PARENT) {
					if (curbone->parent == NULL) continue;
					else pabone = curbone->parent;
					
					if (EBONE_VISIBLE(arm, pabone)) {
						pabone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
						if (pabone->parent)	pabone->parent->flag |= BONE_TIPSEL;
						
						if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
						curbone->flag &= ~BONE_ACTIVE;
						break;
					}
					
				} 
				else { // BONE_SELECT_CHILD
					chbone = editbone_get_child(curbone, 1);
					if (chbone == NULL) continue;
					
					if (EBONE_VISIBLE(arm, chbone)) {
						chbone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
						
						if (!add_to_sel) {
							curbone->flag &= ~(BONE_SELECTED|BONE_ROOTSEL);
							if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
						}
						curbone->flag &= ~BONE_ACTIVE;
						break;
					}
				}
			}
		}
	}

	countall(); // flushes selection!
	
	allqueue (REDRAWVIEW3D, 0);
	allqueue (REDRAWBUTSEDIT, 0);
	allqueue (REDRAWBUTSOBJECT, 0);
	allqueue (REDRAWOOPS, 0);
	
	if (direction==BONE_SELECT_PARENT)
		BIF_undo_push("Select edit bone parent");
	if (direction==BONE_SELECT_CHILD)
		BIF_undo_push("Select edit bone child");
}

/* used by posemode and editmode */
void setflag_armature (short mode)
{
	Object *ob;
	bArmature *arm;	
	int flag;
	
	/* get data */
	if (G.obedit)
		ob= G.obedit;
	else if (OBACT)
		ob= OBACT;
	else
		return;
	arm= (bArmature *)ob->data;
	
	/* get flag to set (sync these with the ones used in eBone_Flag */
	if (mode == 2)
		flag= pupmenu("Disable Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
	else if (mode == 1)
		flag= pupmenu("Enable Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
	else
		flag= pupmenu("Toggle Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
	switch (flag) {
		case 1: 	flag = BONE_DRAWWIRE; 	break;
		case 2:		flag = BONE_NO_DEFORM; break;
		case 3: 	flag = BONE_MULT_VG_ENV; break;
		case 4:		flag = BONE_HINGE; break;
		case 5:		flag = BONE_NO_SCALE; break;
		case 6: 	flag = BONE_EDITMODE_LOCKED; break;
		default:	return;
	}
	
	/* determine which mode armature is in */
	if ((!G.obedit) && (ob->flag & OB_POSEMODE)) {
		/* deal with pose channels */
		bPoseChannel *pchan;
		
		/* set setting */
		for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
			if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
				if (pchan->bone->flag & BONE_SELECTED) {
					bone_setflag(&pchan->bone->flag, flag, mode);
				}
			}
		}
	}
	else if (G.obedit) {
		/* deal with editbones */
		EditBone *curbone;
		
		/* set setting */
		for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
			if (arm->layer & curbone->layer) {
				if (curbone->flag & BONE_SELECTED) {
					bone_setflag(&curbone->flag, flag, mode);
				}
			}
		}
	}
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
	
	BIF_undo_push("Change Bone Setting");
}

/* **************** END PoseMode & EditMode *************************** */
/* **************** Posemode stuff ********************** */


static void selectconnected_posebonechildren (Object *ob, Bone *bone)
{
	Bone *curBone;
	
	if (!(bone->flag & BONE_CONNECTED))
		return;
	
	select_actionchannel_by_name (ob->action, bone->name, !(G.qual & LR_SHIFTKEY));
	
	if (G.qual & LR_SHIFTKEY)
		bone->flag &= ~BONE_SELECTED;
	else
		bone->flag |= BONE_SELECTED;
	
	for (curBone=bone->childbase.first; curBone; curBone=curBone->next){
		selectconnected_posebonechildren (ob, curBone);
	}
}

/* within active object context */
void selectconnected_posearmature(void)
{
	Bone *bone, *curBone, *next;
	Object *ob= OBACT;
	
	if(!ob || !ob->pose) return;
	
	if (G.qual & LR_SHIFTKEY)
		bone= get_nearest_bone(0);
	else
		bone = get_nearest_bone(1);
	
	if (!bone)
		return;
	
	/* Select parents */
	for (curBone=bone; curBone; curBone=next){
		select_actionchannel_by_name (ob->action, curBone->name, !(G.qual & LR_SHIFTKEY));
		if (G.qual & LR_SHIFTKEY)
			curBone->flag &= ~BONE_SELECTED;
		else
			curBone->flag |= BONE_SELECTED;
		
		if (curBone->flag & BONE_CONNECTED)
			next=curBone->parent;
		else
			next=NULL;
	}
	
	/* Select children */
	for (curBone=bone->childbase.first; curBone; curBone=next){
		selectconnected_posebonechildren (ob, curBone);
	}
	
	countall(); // flushes selection!

	allqueue (REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue (REDRAWACTION, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Select connected");

}

/* **************** END Posemode stuff ********************** */
/* **************** EditMode stuff ********************** */

/* called in space.c */
void selectconnected_armature(void)
{
	EditBone *bone, *curBone, *next;

	if (G.qual & LR_SHIFTKEY)
		bone= get_nearest_bone(0);
	else
		bone= get_nearest_bone(1);

	if (!bone)
		return;

	/* Select parents */
	for (curBone=bone; curBone; curBone=next){
		if (G.qual & LR_SHIFTKEY){
			curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
		}
		else{
			curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
		}

		if (curBone->flag & BONE_CONNECTED)
			next=curBone->parent;
		else
			next=NULL;
	}

	/* Select children */
	while (bone){
		for (curBone=G.edbo.first; curBone; curBone=next){
			next = curBone->next;
			if (curBone->parent == bone){
				if (curBone->flag & BONE_CONNECTED){
					if (G.qual & LR_SHIFTKEY)
						curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
					else
						curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
					bone=curBone;
					break;
				}
				else{ 
					bone=NULL;
					break;
				}
			}
		}
		if (!curBone)
			bone=NULL;

	}

	countall(); // flushes selection!

	allqueue (REDRAWVIEW3D, 0);
	allqueue (REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
	
	BIF_undo_push("Select connected");

}

/* does bones and points */
/* note that BONE ROOT only gets drawn for root bones (or without IK) */
static EditBone * get_nearest_editbonepoint (int findunsel, int *selmask)
{
	EditBone *ebone;
	unsigned int buffer[MAXPICKBUF];
	unsigned int hitresult, besthitresult=BONESEL_NOSEL;
	int i, mindep= 4;
	short hits, mval[2];

	persp(PERSP_VIEW);

	glInitNames();
	
	getmouseco_areawin(mval);
	hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-5, mval[1]-5, mval[0]+5, mval[1]+5);
	if(hits==0)
		hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-12, mval[1]-12, mval[0]+12, mval[1]+12);
		
	/* See if there are any selected bones in this group */
	if (hits>0) {
		
		if(hits==1) {
			if (!(buffer[3] & BONESEL_NOSEL)) 
				besthitresult= buffer[3];
		}
		else {
			for (i=0; i< hits; i++) {
				hitresult= buffer[3+(i*4)];
				if (!(hitresult & BONESEL_NOSEL)) {
					int dep;
					
					ebone = BLI_findlink(&G.edbo, hitresult & ~BONESEL_ANY);
					
					/* clicks on bone points get advantage */
					if( hitresult & (BONESEL_ROOT|BONESEL_TIP)) {
						/* but also the unselected one */
						if(findunsel) {
							if( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL)==0) 
								dep= 1;
							else if( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL)==0) 
								dep= 1;
							else 
								dep= 2;
						}
						else dep= 2;
					}
					else {
						/* bone found */
						if(findunsel) {
							if((ebone->flag & BONE_SELECTED)==0)
								dep= 2;
							else
								dep= 3;
						}
						else dep= 3;
					}
					if(dep < mindep) {
						mindep= dep;
						besthitresult= hitresult;
					}
				}
			}
		}
		
		if (!(besthitresult & BONESEL_NOSEL)) {
			
			ebone= BLI_findlink(&G.edbo, besthitresult & ~BONESEL_ANY);
			
			*selmask = 0;
			if (besthitresult & BONESEL_ROOT)
				*selmask |= BONE_ROOTSEL;
			if (besthitresult & BONESEL_TIP)
				*selmask |= BONE_TIPSEL;
			if (besthitresult & BONESEL_BONE)
				*selmask |= BONE_SELECTED;
			return ebone;
		}
	}
	*selmask = 0;
	return NULL;
}

static void delete_bone(EditBone* exBone)
{
	EditBone *curBone;
	
	/* Find any bones that refer to this bone */
	for (curBone=G.edbo.first;curBone;curBone=curBone->next) {
		if (curBone->parent==exBone) {
			curBone->parent=exBone->parent;
			curBone->flag &= ~BONE_CONNECTED;
		}
	}
	
	BLI_freelinkN(&G.edbo,exBone);
}

/* only editmode! */
void delete_armature(void)
{
	bArmature *arm= G.obedit->data;
	EditBone	*curBone, *next;
	bConstraint *con;
	
	TEST_EDITARMATURE;
	if (okee("Erase selected bone(s)")==0) return;

	/* Select mirrored bones */
	if (arm->flag & ARM_MIRROR_EDIT) {
		for (curBone=G.edbo.first; curBone; curBone=curBone->next) {
			if (arm->layer & curBone->layer) {
				if (curBone->flag & BONE_SELECTED) {
					next = armature_bone_get_mirrored(curBone);
					if (next)
						next->flag |= BONE_SELECTED;
				}
			}
		}
	}
	
	/*  First erase any associated pose channel */
	if (G.obedit->pose) {
		bPoseChannel *chan, *next;
		for (chan=G.obedit->pose->chanbase.first; chan; chan=next) {
			next= chan->next;
			curBone = editbone_name_exists(&G.edbo, chan->name);
			
			if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
				free_constraints(&chan->constraints);
				BLI_freelinkN (&G.obedit->pose->chanbase, chan);
			}
			else {
				for (con= chan->constraints.first; con; con= con->next) {
					bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
					ListBase targets = {NULL, NULL};
					bConstraintTarget *ct;
					
					if (cti && cti->get_constraint_targets) {
						cti->get_constraint_targets(con, &targets);
						
						for (ct= targets.first; ct; ct= ct->next) {
							if (ct->tar == G.obedit) {
								if (ct->subtarget[0]) {
									curBone = editbone_name_exists(&G.edbo, ct->subtarget);
									if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
										con->flag |= CONSTRAINT_DISABLE;
										ct->subtarget[0]= 0;
									}
								}
							}
						}
						
						if (cti->flush_constraint_targets)
							cti->flush_constraint_targets(con, &targets, 0);
					}
				}
			}
		}
	}
	
	
	for (curBone=G.edbo.first;curBone;curBone=next) {
		next=curBone->next;
		if (arm->layer & curBone->layer) {
			if (curBone->flag & BONE_SELECTED)
				delete_bone(curBone);
		}
	}
	
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
	countall(); // flushes selection!
	
	BIF_undo_push("Delete bone(s)");
}

/* context: editmode armature */
void mouse_armature(void)
{
	EditBone *nearBone = NULL, *ebone;
	int	selmask;

	nearBone= get_nearest_editbonepoint(1, &selmask);
	if (nearBone) {
		
		if (!(G.qual & LR_SHIFTKEY)) {
			deselectall_armature(0, 0);
		}
		
		/* by definition the non-root connected bones have no root point drawn,
	       so a root selection needs to be delivered to the parent tip,
	       countall() (bad location) flushes these flags */
		
		if(selmask & BONE_SELECTED) {
			if(nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
				/* click in a chain */
				if(G.qual & LR_SHIFTKEY) {
					/* hold shift inverts this bone's selection */
					if(nearBone->flag & BONE_SELECTED) {
						/* deselect this bone */
						nearBone->flag &= ~(BONE_TIPSEL|BONE_SELECTED);
						/* only deselect parent tip if it is not selected */
						if(!(nearBone->parent->flag & BONE_SELECTED))
							nearBone->parent->flag &= ~BONE_TIPSEL;
					}
					else {
						/* select this bone */
						nearBone->flag |= BONE_TIPSEL;
						nearBone->parent->flag |= BONE_TIPSEL;
					}
				}
				else {
					/* select this bone */
					nearBone->flag |= BONE_TIPSEL;
					nearBone->parent->flag |= BONE_TIPSEL;
				}
			}
			else {
				if(G.qual & LR_SHIFTKEY) {
					/* hold shift inverts this bone's selection */
					if(nearBone->flag & BONE_SELECTED)
					   nearBone->flag &= ~(BONE_TIPSEL|BONE_ROOTSEL);
					else
						nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
				}
				else nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
			}
		}
		else {
			if ((G.qual & LR_SHIFTKEY) && (nearBone->flag & selmask))
				nearBone->flag &= ~selmask;
			else
				nearBone->flag |= selmask;
		}

		countall(); // flushes selection!
		
		if(nearBone) {
			/* then now check for active status */
			for (ebone=G.edbo.first;ebone;ebone=ebone->next) ebone->flag &= ~BONE_ACTIVE;
			if(nearBone->flag & BONE_SELECTED) nearBone->flag |= BONE_ACTIVE;
		}
		
		allqueue(REDRAWVIEW3D, 0);
		allqueue(REDRAWBUTSEDIT, 0);
		allqueue(REDRAWBUTSOBJECT, 0);
		allqueue(REDRAWOOPS, 0);
	}

	rightmouse_transform();
}

void free_editArmature(void)
{
	/*	Clear the editbones list */
	if (G.edbo.first)
		BLI_freelistN(&G.edbo);
}

void remake_editArmature(void)
{
	if(okee("Reload original data")==0) return;
	
	make_editArmature();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWOOPS, 0);
	allqueue(REDRAWBUTSHEAD, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	
//	BIF_undo_push("Delete bone");
}

/* Put object in EditMode */
void make_editArmature(void)
{
	bArmature *arm;
	
	if (G.obedit==0) return;
	
	free_editArmature();
	
	arm= get_armature(G.obedit);
	if (!arm) return;
	
	make_boneList(&G.edbo, &arm->bonebase,NULL);
}

/* put EditMode back in Object */
void load_editArmature(void)
{
	bArmature *arm;

	arm= get_armature(G.obedit);
	if (!arm) return;
	
	editbones_to_armature(&G.edbo, G.obedit);
}

/* toggle==0: deselect
   toggle==1: swap (based on test)
   toggle==2: only active tag
   toggle==3: swap (no test)
*/
void deselectall_armature(int toggle, int doundo)
{
	bArmature *arm= G.obedit->data;
	EditBone	*eBone;
	int			sel=1;

	if(toggle==1) {
		/*	Determine if there are any selected bones
			And therefore whether we are selecting or deselecting */
		for (eBone=G.edbo.first;eBone;eBone=eBone->next){
//			if(arm->layer & eBone->layer) {
				if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)){
					sel=0;
					break;
				}
//			}
		}
	}
	else sel= toggle;
	
	/*	Set the flags */
	for (eBone=G.edbo.first;eBone;eBone=eBone->next) {
		if (sel==3) {
			/* invert selection of bone */
			if ((arm->layer & eBone->layer) && (eBone->flag & BONE_HIDDEN_A)==0) {
				eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				eBone->flag &= ~BONE_ACTIVE;
			}
		}
		else if (sel==1) {
			/* select bone */
			if(arm->layer & eBone->layer && (eBone->flag & BONE_HIDDEN_A)==0) {
				eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
				if(eBone->parent)
					eBone->parent->flag |= (BONE_TIPSEL);
			}
		}
		else if (sel==2) {
			/* clear active flag */
			eBone->flag &= ~(BONE_ACTIVE);
		}
		else {
			/* deselect bone */
			eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
		}
	}
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
	
	countall(); // flushes selection!
	if (doundo) {
		if (sel==1) BIF_undo_push("Select All");
		else BIF_undo_push("Deselect All");
	}
}

/* Sets the roll value of selected bones, depending on the mode
 * 	mode == 0: their z-axes point upwards 
 * 	mode == 1: their z-axes point towards 3d-cursor
 */
void auto_align_armature(short mode)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone;
	EditBone *flipbone = NULL;
	float	delta[3];
	float	curmat[3][3];
	float  	*cursor= give_cursor();
		
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (arm->flag & ARM_MIRROR_EDIT)
				flipbone = armature_bone_get_mirrored(ebone);
			
			if ((ebone->flag & BONE_SELECTED) || 
				(flipbone && (flipbone->flag & BONE_SELECTED))) 
			{
				/* specific method used to calculate roll depends on mode */
				if (mode == 1) {
					/* Z-Axis point towards cursor */
					float	mat[4][4], tmat[4][4], imat[4][4];
					float 	rmat[4][4], rot[3];
					float	vec[3];
					
					/* find the current bone matrix as a 4x4 matrix (in Armature Space) */
					VecSubf(delta, ebone->tail, ebone->head);
					vec_roll_to_mat3(delta, ebone->roll, curmat);
					Mat4CpyMat3(mat, curmat);
					VECCOPY(mat[3], ebone->head);
					
					/* multiply bone-matrix by object matrix (so that bone-matrix is in WorldSpace) */
					Mat4MulMat4(tmat, mat, G.obedit->obmat);
					Mat4Invert(imat, tmat);
					
					/* find position of cursor relative to bone */
					VecMat4MulVecfl(vec, imat, cursor);
					
					/* check that cursor is in usable position */
					if ((IS_EQ(vec[0], 0)==0) && (IS_EQ(vec[2], 0)==0)) {
						/* Compute a rotation matrix around y */
						rot[1] = atan2(vec[0], vec[2]);
						rot[0] = rot[2] = 0.0f;
						EulToMat4(rot, rmat);
						
						/* Multiply the bone matrix by rotation matrix. This should be new bone-matrix */
						Mat4MulMat4(tmat, rmat, mat);
						Mat3CpyMat4(curmat, tmat);
						
						/* Now convert from new bone-matrix, back to a roll value (in radians) */
						mat3_to_vec_roll(curmat, delta, &ebone->roll);
					}
				}
				else { 
					/* Z-Axis Point Up */
					float	xaxis[3]={1.0, 0.0, 0.0}, yaxis[3], zaxis[3]={0.0, 0.0, 1.0};
					float	targetmat[3][3], imat[3][3], diffmat[3][3];
					
					/* Find the current bone matrix */
					VecSubf(delta, ebone->tail, ebone->head);
					vec_roll_to_mat3(delta, 0.0, curmat);
					
					/* Make new matrix based on y axis & z-up */
					VECCOPY (yaxis, curmat[1]);
					
					Mat3One(targetmat);
					VECCOPY (targetmat[0], xaxis);
					VECCOPY (targetmat[1], yaxis);
					VECCOPY (targetmat[2], zaxis);
					Mat3Ortho(targetmat);
					
					/* Find the difference between the two matrices */
					Mat3Inv(imat, targetmat);
					Mat3MulMat3(diffmat, imat, curmat);
					
					ebone->roll = atan2(diffmat[2][0], diffmat[2][2]);
				}				
			}
		}
	}
}

/* **************** undo for armatures ************** */

static void undoBones_to_editBones(void *lbv)
{
	ListBase *lb= lbv;
	EditBone *ebo, *newebo;
	
	BLI_freelistN(&G.edbo);
	
	/* copy  */
	for(ebo= lb->first; ebo; ebo= ebo->next) {
		newebo= MEM_dupallocN(ebo);
		ebo->temp= newebo;
		BLI_addtail(&G.edbo, newebo);
	}
	
	/* set pointers */
	for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
		if(newebo->parent) newebo->parent= newebo->parent->temp;
	}
	/* be sure they dont hang ever */
	for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
		newebo->temp= NULL;
	}
}

static void *editBones_to_undoBones(void)
{
	ListBase *lb;
	EditBone *ebo, *newebo;
	
	lb= MEM_callocN(sizeof(ListBase), "listbase undo");
	
	/* copy */
	for(ebo= G.edbo.first; ebo; ebo= ebo->next) {
		newebo= MEM_dupallocN(ebo);
		ebo->temp= newebo;
		BLI_addtail(lb, newebo);
	}
	
	/* set pointers */
	for(newebo= lb->first; newebo; newebo= newebo->next) {
		if(newebo->parent) newebo->parent= newebo->parent->temp;
	}
	
	return lb;
}

static void free_undoBones(void *lbv)
{
	ListBase *lb= lbv;
	
	BLI_freelistN(lb);
	MEM_freeN(lb);
}

/* and this is all the undo system needs to know */
void undo_push_armature(char *name)
{
	undo_editmode_push(name, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
}



/* **************** END EditMode stuff ********************** */
/* *************** Adding stuff in editmode *************** */

/* default bone add, returns it selected, but without tail set */
static EditBone *add_editbone(char *name)
{
	bArmature *arm= G.obedit->data;
	
	EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
	
	BLI_strncpy(bone->name, name, 32);
	unique_editbone_name(&G.edbo, bone->name);
	
	BLI_addtail(&G.edbo, bone);
	
	bone->flag |= BONE_TIPSEL;
	bone->weight= 1.0F;
	bone->dist= 0.25F;
	bone->xwidth= 0.1;
	bone->zwidth= 0.1;
	bone->ease1= 1.0;
	bone->ease2= 1.0;
	bone->rad_head= 0.10;
	bone->rad_tail= 0.05;
	bone->segments= 1;
	bone->layer= arm->layer;
	
	return bone;
}

static void add_primitive_bone(Object *ob, short newob)
{
	float		obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
	EditBone	*bone;
	
	VECCOPY(curs, give_cursor());	

	/* Get inverse point for head and orientation for tail */
	Mat4Invert(G.obedit->imat, G.obedit->obmat);
	Mat4MulVecfl(G.obedit->imat, curs);

	if ( !(newob) || (U.flag & USER_ADD_VIEWALIGNED) ) Mat3CpyMat4(obmat, G.vd->viewmat);
	else Mat3One(obmat);
	
	Mat3CpyMat4(viewmat, G.obedit->obmat);
	Mat3MulMat3(totmat, obmat, viewmat);
	Mat3Inv(imat, totmat);
	
	deselectall_armature(0, 0);
	
	/*	Create a bone	*/
	bone= add_editbone("Bone");

	VECCOPY(bone->head, curs);
	
	if ( !(newob) || (U.flag & USER_ADD_VIEWALIGNED) )
		VecAddf(bone->tail, bone->head, imat[1]);	// bone with unit length 1
	else
		VecAddf(bone->tail, bone->head, imat[2]);	// bone with unit length 1, pointing up Z
	
}

void add_primitiveArmature(int type)
{
	short newob=0;
	
	if(G.scene->id.lib) return;
	
	/* this function also comes from an info window */
	if ELEM(curarea->spacetype, SPACE_VIEW3D, SPACE_INFO); else return;
	if (G.vd==NULL) return;
	
	G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT+G_WEIGHTPAINT+G_SCULPTMODE);
	setcursor_space(SPACE_VIEW3D, CURSOR_STD);

	check_editmode(OB_ARMATURE);
	
	/* If we're not the "obedit", make a new object and enter editmode */
	if (G.obedit==NULL) {
		add_object(OB_ARMATURE);
		base_init_from_view3d(BASACT, G.vd);
		G.obedit= BASACT->object;
		
		where_is_object(G.obedit);
		
		make_editArmature();
		setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
		newob=1;
	}
	
	/* no primitive support yet */
	add_primitive_bone(G.obedit, newob);
	
	countall(); // flushes selection!

	if ((newob) && !(U.flag & USER_ADD_EDITMODE)) {
		exit_editmode(2);
	}
	
	allqueue(REDRAWALL, 0);
	BIF_undo_push("Add primitive");
}

/* the ctrl-click method */
void addvert_armature(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone, *newbone, *flipbone;
	float *curs, mat[3][3],imat[3][3];
	int a, to_root= 0;
	
	TEST_EDITARMATURE;
	
	/* find the active or selected bone */
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & (BONE_ACTIVE|BONE_TIPSEL)) 
				break;
		}
	}
	
	if (ebone==NULL) {
		for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
			if (EBONE_VISIBLE(arm, ebone)) {
				if (ebone->flag & (BONE_ACTIVE|BONE_ROOTSEL)) 
					break;
			}
		}
		if (ebone == NULL) 
			return;
		
		to_root= 1;
	}
	
	deselectall_armature(0, 0);
	
	/* we re-use code for mirror editing... */
	flipbone= NULL;
	if (arm->flag & ARM_MIRROR_EDIT)
		flipbone= armature_bone_get_mirrored(ebone);

	for (a=0; a<2; a++) {
		if (a==1) {
			if (flipbone==NULL)
				break;
			else {
				SWAP(EditBone *, flipbone, ebone);
			}
		}
		
		newbone= add_editbone(ebone->name);
		newbone->flag |= BONE_ACTIVE;
		
		if (to_root) {
			VECCOPY(newbone->head, ebone->head);
			newbone->rad_head= ebone->rad_tail;
			newbone->parent= ebone->parent;
		}
		else {
			VECCOPY(newbone->head, ebone->tail);
			newbone->rad_head= ebone->rad_tail;
			newbone->parent= ebone;
			newbone->flag |= BONE_CONNECTED;
		}
		
		curs= give_cursor();
		VECCOPY(newbone->tail, curs);
		VecSubf(newbone->tail, newbone->tail, G.obedit->obmat[3]);
		
		if (a==1) 
			newbone->tail[0]= -newbone->tail[0];
		
		Mat3CpyMat4(mat, G.obedit->obmat);
		Mat3Inv(imat, mat);
		Mat3MulVecfl(imat, newbone->tail);
		
		newbone->length= VecLenf(newbone->head, newbone->tail);
		newbone->rad_tail= newbone->length*0.05f;
		newbone->dist= newbone->length*0.25f;
		
	}
	
	countall();
	
	BIF_undo_push("Add Bone");
	allqueue(REDRAWVIEW3D, 0);
}

/* adds an EditBone between the nominated locations (should be in the right space) */
static EditBone *add_points_bone (float head[], float tail[]) 
{
	EditBone *ebo;
	
	ebo= add_editbone("Bone");
	
	VECCOPY(ebo->head, head);
	VECCOPY(ebo->tail, tail);
	
	return ebo;
}


static EditBone *get_named_editbone(char *name)
{
	EditBone  *eBone;

	if (name) {
		for (eBone=G.edbo.first; eBone; eBone=eBone->next) {
			if (!strcmp(name, eBone->name))
				return eBone;
		}
	}

	return NULL;
}

static void update_dup_subtarget(EditBone *dupBone)
{
	/* If an edit bone has been duplicated, lets
	 * update it's constraints if the subtarget
	 * they point to has also been duplicated
	 */
	EditBone     *oldtarget, *newtarget;
	bPoseChannel *chan;
	bConstraint  *curcon;
	ListBase     *conlist;
	
	if ( (chan = verify_pose_channel(OBACT->pose, dupBone->name)) ) {
		if ( (conlist = &chan->constraints) ) {
			for (curcon = conlist->first; curcon; curcon=curcon->next) {
				/* does this constraint have a subtarget in
				 * this armature?
				 */
				bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
				ListBase targets = {NULL, NULL};
				bConstraintTarget *ct;
				
				if (cti && cti->get_constraint_targets) {
					cti->get_constraint_targets(curcon, &targets);
					
					for (ct= targets.first; ct; ct= ct->next) {
						if ((ct->tar == G.obedit) && (ct->subtarget[0])) {
							oldtarget = get_named_editbone(ct->subtarget);
							if (oldtarget) {
								/* was the subtarget bone duplicated too? If
								 * so, update the constraint to point at the 
								 * duplicate of the old subtarget.
								 */
								if (oldtarget->flag & BONE_SELECTED){
									newtarget = (EditBone *) oldtarget->temp;
									strcpy(ct->subtarget, newtarget->name);
								}
							}
						}
					}
					
					if (cti->flush_constraint_targets)
						cti->flush_constraint_targets(curcon, &targets, 0);
				}
			}
		}
	}
}


void adduplicate_armature(void)
{
	bArmature *arm= G.obedit->data;
	EditBone	*eBone = NULL;
	EditBone	*curBone;
	EditBone	*firstDup=NULL;	/*	The beginning of the duplicated bones in the edbo list */
	
	countall(); // flushes selection!

	/* Select mirrored bones */
	if (arm->flag & ARM_MIRROR_EDIT) {
		for (curBone=G.edbo.first; curBone; curBone=curBone->next) {
			if (EBONE_VISIBLE(arm, curBone)) {
				if (curBone->flag & BONE_SELECTED) {
					eBone = armature_bone_get_mirrored(curBone);
					if (eBone)
						eBone->flag |= BONE_SELECTED;
				}
			}
		}
	}
	
	/*	Find the selected bones and duplicate them as needed */
	for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next) {
		if (EBONE_VISIBLE(arm, curBone)) {
			if (curBone->flag & BONE_SELECTED) {
				eBone=MEM_callocN(sizeof(EditBone), "addup_editbone");
				eBone->flag |= BONE_SELECTED;
				
				/*	Copy data from old bone to new bone */
				memcpy(eBone, curBone, sizeof(EditBone));
				
				curBone->temp = eBone;
				eBone->temp = curBone;
				
				unique_editbone_name(&G.edbo, eBone->name);
				BLI_addtail(&G.edbo, eBone);
				if (!firstDup)
					firstDup=eBone;
				
				/* Lets duplicate the list of constraints that the
				 * current bone has.
				 */
				if (OBACT->pose) {
					bPoseChannel *chanold, *channew;
					ListBase     *listold, *listnew;
					
					chanold = verify_pose_channel(OBACT->pose, curBone->name);
					if (chanold) {
						listold = &chanold->constraints;
						if (listold) {
							/* WARNING: this creates a new posechannel, but there will not be an attached bone 
							 *		yet as the new bones created here are still 'EditBones' not 'Bones'. 
							 */
							channew = 
								verify_pose_channel(OBACT->pose, eBone->name);
							if (channew) {
								/* copy transform locks */
								channew->protectflag = chanold->protectflag;
								
								/* copy bone group */
								channew->agrp_index= chanold->agrp_index;
								
								/* ik (dof) settings */
								channew->ikflag = chanold->ikflag;
								VECCOPY(channew->limitmin, chanold->limitmin);
								VECCOPY(channew->limitmax, chanold->limitmax);
								VECCOPY(channew->stiffness, chanold->stiffness);
								channew->ikstretch= chanold->ikstretch;
								
								/* constraints */
								listnew = &channew->constraints;
								copy_constraints(listnew, listold);
								
								/* custom shape */
								channew->custom= chanold->custom;
							}
						}
					}
				}
			}
		}
	}

	/*	Run though the list and fix the pointers */
	for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next) {
		if (EBONE_VISIBLE(arm, curBone)) {
			if (curBone->flag & BONE_SELECTED) {
				eBone=(EditBone*) curBone->temp;
				
				/*	If this bone has no parent,
				Set the duplicate->parent to NULL
				*/
				if (!curBone->parent)
					eBone->parent = NULL;
				/*	If this bone has a parent that IS selected,
					Set the duplicate->parent to the curBone->parent->duplicate
					*/
				else if (curBone->parent->flag & BONE_SELECTED)
					eBone->parent= (EditBone *)curBone->parent->temp;
				/*	If this bone has a parent that IS not selected,
					Set the duplicate->parent to the curBone->parent
					*/
				else {
					eBone->parent=(EditBone*) curBone->parent; 
					eBone->flag &= ~BONE_CONNECTED;
				}
				
				/* Lets try to fix any constraint subtargets that might
					have been duplicated */
				update_dup_subtarget(eBone);
			}
		}
	} 
	
	/*	Deselect the old bones and select the new ones */
	
	for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next) {
		if (EBONE_VISIBLE(arm, curBone))
			curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
	}
	
	BIF_TransformSetUndo("Add Duplicate");
	initTransform(TFM_TRANSLATION, CTX_NO_PET);
	Transform();
	
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
}



/* *************** END Adding stuff in editmode *************** */
/* ************** Add/Remove stuff in editmode **************** */

/* temporary data-structure for merge/fill bones */
typedef struct EditBonePoint {
	struct EditBonePoint *next, *prev;
	
	EditBone *head_owner;		/* EditBone which uses this point as a 'head' point */
	EditBone *tail_owner;		/* EditBone which uses this point as a 'tail' point */
	
	float vec[3];				/* the actual location of the point in local/EditMode space */
} EditBonePoint;

/* find chain-tips (i.e. bones without children) */
static void chains_find_tips (ListBase *list)
{
	EditBone *curBone, *ebo;
	LinkData *ld;
	
	/* note: this is potentially very slow ... there's got to be a better way */
	for (curBone= G.edbo.first; curBone; curBone= curBone->next) {
		short stop= 0;
		
		/* is this bone contained within any existing chain? (skip if so) */
		for (ld= list->first; ld; ld= ld->next) {
			for (ebo= ld->data; ebo; ebo= ebo->parent) {
				if (ebo == curBone) {
					stop= 1;
					break;
				}
			}
			
			if (stop) break;
		}
		/* skip current bone if it is part of an existing chain */
		if (stop) continue;
		
		/* is any existing chain part of the chain formed by this bone? */
		stop= 0;
		for (ebo= curBone->parent; ebo; ebo= ebo->parent) {
			for (ld= list->first; ld; ld= ld->next) {
				if (ld->data == ebo) {
					ld->data= curBone;
					stop= 1;
					break;
				}
			}
			
			if (stop) break;
		}
		/* current bone has already been added to a chain? */
		if (stop) continue;
		
		/* add current bone to a new chain */
		ld= MEM_callocN(sizeof(LinkData), "BoneChain");
		ld->data= curBone;
		BLI_addtail(list, ld);
	}
}


static void fill_add_joint (EditBone *ebo, short eb_tail, ListBase *points)
{
	EditBonePoint *ebp;
	float vec[3];
	short found= 0;
	
	if (eb_tail) {
		VECCOPY(vec, ebo->tail);
	}
	else {
		VECCOPY(vec, ebo->head);
	}
	
	for (ebp= points->first; ebp; ebp= ebp->next) {
		if (VecEqual(ebp->vec, vec)) {			
			if (eb_tail) {
				if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) {
					/* so this bone's tail owner is this bone */
					ebp->tail_owner= ebo;
					found= 1;
					break;
				}
			}
			else {
				if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) {
					/* so this bone's head owner is this bone */
					ebp->head_owner= ebo;
					found = 1;
					break;
				}
			}
		}
	}
	
	/* allocate a new point if no existing point was related */
	if (found == 0) {
		ebp= MEM_callocN(sizeof(EditBonePoint), "EditBonePoint");
		
		if (eb_tail) {
			VECCOPY(ebp->vec, ebo->tail);
			ebp->tail_owner= ebo;
		}
		else {
			VECCOPY(ebp->vec, ebo->head);
			ebp->head_owner= ebo;
		}
		
		BLI_addtail(points, ebp);
	}
}

/* bone adding between selected joints */
void fill_bones_armature(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebo, *newbone=NULL;
	ListBase points = {NULL, NULL};
	int count;
	
	/* loop over all bones, and only consider if visible */
	for (ebo= G.edbo.first; ebo; ebo= ebo->next) {
		if (EBONE_VISIBLE(arm, ebo)) {
			if (!(ebo->flag & BONE_CONNECTED) && (ebo->flag & BONE_ROOTSEL))
				fill_add_joint(ebo, 0, &points);
			if (ebo->flag & BONE_TIPSEL) 
				fill_add_joint(ebo, 1, &points);
		}
	}
	
	/* the number of joints determines how we fill:
	 *	1) between joint and cursor (joint=head, cursor=tail)
	 *	2) between the two joints (order is dependent on active-bone/hierachy)
	 * 	3+) error (a smarter method involving finding chains needs to be worked out
	 */
	count= BLI_countlist(&points);
	
	if (count == 0) {
		error("No joints selected");
		return;
	}
	else if (count == 1) {
		EditBonePoint *ebp;
		float curs[3];
		
		/* Get Points - selected joint */
		ebp= (EditBonePoint *)points.first;
		
		/* Get points - cursor (tail) */
		VECCOPY (curs, give_cursor());	
		
		Mat4Invert(G.obedit->imat, G.obedit->obmat);
		Mat4MulVecfl(G.obedit->imat, curs);
		
		/* Create a bone */
		newbone= add_points_bone(ebp->vec, curs);
	}
	else if (count == 2) {
		EditBonePoint *ebp, *ebp2;
		float head[3], tail[3];
		short headtail = 0;
		
		/* check that the points don't belong to the same bone */
		ebp= (EditBonePoint *)points.first;
		ebp2= ebp->next;
		
		if ((ebp->head_owner==ebp2->tail_owner) && (ebp->head_owner!=NULL)) {
			error("Same bone selected...");
			BLI_freelistN(&points);
			return;
		}
		if ((ebp->tail_owner==ebp2->head_owner) && (ebp->tail_owner!=NULL)) {
			error("Same bone selected...");
			BLI_freelistN(&points);
			return;
		}
		
		/* find which one should be the 'head' */
		if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) {
			/* rule: whichever one is closer to 3d-cursor */
			float curs[3];
			float vecA[3], vecB[3];
			float distA, distB;
			
			/* get cursor location */
			VECCOPY(curs, give_cursor());	
			
			Mat4Invert(G.obedit->imat, G.obedit->obmat);
			Mat4MulVecfl(G.obedit->imat, curs);
			
			/* get distances */
			VecSubf(vecA, ebp->vec, curs);
			VecSubf(vecB, ebp2->vec, curs);
			distA= VecLength(vecA);
			distB= VecLength(vecB);
			
			/* compare distances - closer one therefore acts as direction for bone to go */
			headtail= (distA < distB) ? 2 : 1;
		}
		else if (ebp->head_owner) {
			headtail = 1;
		}
		else if (ebp2->head_owner) {
			headtail = 2;
		}
		
		/* assign head/tail combinations */
		if (headtail == 2) {
			VECCOPY(head, ebp->vec);
			VECCOPY(tail, ebp2->vec);
		}
		else if (headtail == 1) {
			VECCOPY(head, ebp2->vec);
			VECCOPY(tail, ebp->vec);
		}
		
		/* add new bone and parent it to the appropriate end */
		if (headtail) {
			newbone= add_points_bone(head, tail);
			
			/* do parenting (will need to set connected flag too) */
			if (headtail == 2) {
				/* ebp tail or head - tail gets priority */
				if (ebp->tail_owner)
					newbone->parent= ebp->tail_owner;
				else
					newbone->parent= ebp->head_owner;
			}
			else {
				/* ebp2 tail or head - tail gets priority */
				if (ebp2->tail_owner)
					newbone->parent= ebp2->tail_owner;
				else
					newbone->parent= ebp2->head_owner;
			}
			
			newbone->flag |= BONE_CONNECTED;
		}
	}
	else {
		// FIXME.. figure out a method for multiple bones
		error("Too many points selected"); 
		printf("Points selected: %d \n", count);
		BLI_freelistN(&points);
		return;
	}
	
	/* free points */
	BLI_freelistN(&points);
	
	/* undo + updates */
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	BIF_undo_push("Fill Bones");
}

/* this function merges between two bones, removes them and those in-between, 
 * and adjusts the parent relationships for those in-between
 */
static void bones_merge(EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains)
{
	EditBone *ebo, *ebone, *newbone;
	LinkData *chain;
	float head[3], tail[3];
	
	/* check if same bone */
	if (start == end) {
		printf("Error: same bone! \n");
		printf("\tstart = %s, end = %s \n", start->name, end->name);
	}
	
	/* step 1: add a new bone
	 *	- head = head/tail of start (default head)
	 *	- tail = head/tail of end (default tail)
	 *	- parent = parent of start
	 */
	if ((start->flag & BONE_TIPSEL) && !(start->flag & (BONE_SELECTED|BONE_ACTIVE))) {
		VECCOPY(head, start->tail);
	}
	else {
		VECCOPY(head, start->head);
	}
	if ((end->flag & BONE_ROOTSEL) && !(end->flag & (BONE_SELECTED|BONE_ACTIVE))) {
		VECCOPY(tail, end->head);
	}
	else {
		VECCOPY(tail, end->tail);
	}
	newbone= add_points_bone(head, tail);
	newbone->parent = start->parent;
	
	/* step 2a: parent children of in-between bones to newbone */
	for (chain= chains->first; chain; chain= chain->next) {
		/* ick: we need to check if parent of each bone in chain is one of the bones in the */
		for (ebo= chain->data; ebo; ebo= ebo->parent) {
			short found= 0;
			
			/* try to find which bone from the list to be removed, is the parent */
			for (ebone= end; ebone; ebone= ebone->parent) {
				if (ebo->parent == ebone) {
					found= 1;
					break;
				}
			}
			
			/* adjust this bone's parent to newbone then */
			if (found) {
				ebo->parent= newbone;
				break;
			}
		}
	}
	
	/* step 2b: parent child of end to newbone (child from this chain) */
	if (endchild)
		endchild->parent= newbone;
	
	/* step 3: delete all bones between and including start and end */
	for (ebo= end; ebo; ebo= ebone) {
		ebone= (ebo == start) ? (NULL) : (ebo->parent);
		BLI_freelinkN(&G.edbo, ebo);
	}
}

/* bone merging - has a menu! */
void merge_armature(void)
{
	bArmature *arm= G.obedit->data;
	short val= 0;
	
	/* process a menu to determine how to merge */
	// TODO: there's room for more modes of merging stuff...
	val= pupmenu("Merge Selected Bones%t|Within Chains%x1");
	if (val <= 0) return;
	
	if (val == 1) {
		/* go down chains, merging bones */
		ListBase chains = {NULL, NULL};
		LinkData *chain, *nchain;
		EditBone *ebo;
		
		/* get chains (ends on chains) */
		chains_find_tips(&chains);
		if (chains.first == NULL) return;
		
		/* each 'chain' is the last bone in the chain (with no children) */
		for (chain= chains.first; chain; chain= nchain) {
			EditBone *bstart= NULL, *bend= NULL;
			EditBone *bchild= NULL, *child=NULL;
			
			/* temporarily remove chain from list of chains */
			nchain= chain->next;
			BLI_remlink(&chains, chain);
			
			/* only consider bones that are visible and selected */
			for (ebo=chain->data; ebo; child=ebo, ebo=ebo->parent) {
				/* check if visible + selected */
				if ( EBONE_VISIBLE(arm, ebo) &&
					 ((ebo->flag & BONE_CONNECTED) || (ebo->parent==NULL)) &&
					 (ebo->flag & (BONE_SELECTED|BONE_ACTIVE)) )
				{
					/* set either end or start (end gets priority, unless it is already set) */
					if (bend == NULL)  {
						bend= ebo;
						bchild= child;
					}
					else 
						bstart= ebo;
				}
				else {
					/* chain is broken... merge any continous segments then clear */
					if (bstart && bend)
						bones_merge(bstart, bend, bchild, &chains);
					
					bstart = NULL;
					bend = NULL;
					bchild = NULL;
				}
			}
			
			/* merge from bstart to bend if something not merged */
			if (bstart && bend)
				bones_merge(bstart, bend, bchild, &chains);
			
			/* put back link */
			BLI_insertlinkbefore(&chains, nchain, chain);
		}		
		
		BLI_freelistN(&chains);
	}
	
	/* undo + updates */
	countall();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	BIF_undo_push("Merge Bones");
}

/* ************** END Add/Remove stuff in editmode ************ */
/* *************** Tools in editmode *********** */


void hide_selected_armature_bones(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone;
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & (BONE_SELECTED)) {
				ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
				ebone->flag |= BONE_HIDDEN_A;
			}
		}
	}
	countall();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	BIF_undo_push("Hide Bones");
}

void hide_unselected_armature_bones(void)
{
	EditBone *ebone;
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		bArmature *arm= G.obedit->data;
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL));
			else {
				ebone->flag &= ~BONE_ACTIVE;
				ebone->flag |= BONE_HIDDEN_A;
			}
		}
	}
	countall();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	BIF_undo_push("Hide Unselected Bones");
}

void show_all_armature_bones(void)
{
	EditBone *ebone;
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		bArmature *arm= G.obedit->data;
		if(arm->layer & ebone->layer) {
			if (ebone->flag & BONE_HIDDEN_A) {
				ebone->flag |= (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL);
				ebone->flag &= ~BONE_HIDDEN_A;
			}
		}
	}
	countall();
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	BIF_undo_push("Reveal Bones");
}

/* check for null, before calling! */
static void bone_connect_to_existing_parent(EditBone *bone)
{
	bone->flag |= BONE_CONNECTED;
	VECCOPY(bone->head, bone->parent->tail);
	bone->rad_head = bone->parent->rad_tail;
}

static void bone_connect_to_new_parent(EditBone *selbone, EditBone *actbone, short mode)
{
	EditBone *ebone;
	float offset[3];
	
	if ((selbone->parent) && (selbone->flag & BONE_CONNECTED))
		selbone->parent->flag &= ~(BONE_TIPSEL);
	
	/* make actbone the parent of selbone */
	selbone->parent= actbone;
	
	/* in actbone tree we cannot have a loop */
	for (ebone= actbone->parent; ebone; ebone= ebone->parent) {
		if (ebone->parent==selbone) {
			ebone->parent= NULL;
			ebone->flag &= ~BONE_CONNECTED;
		}
	}
	
	if (mode == 1) {	
		/* Connected: Child bones will be moved to the parent tip */
		selbone->flag |= BONE_CONNECTED;
		VecSubf(offset, actbone->tail, selbone->head);
		
		VECCOPY(selbone->head, actbone->tail);
		selbone->rad_head= actbone->rad_tail;
		
		VecAddf(selbone->tail, selbone->tail, offset);
		
		/* offset for all its children */
		for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
			EditBone *par;
			
			for (par= ebone->parent; par; par= par->parent) {
				if (par==selbone) {
					VecAddf(ebone->head, ebone->head, offset);
					VecAddf(ebone->tail, ebone->tail, offset);
					break;
				}
			}
		}
	}
	else {
		/* Offset: Child bones will retain their distance from the parent tip */
		selbone->flag &= ~BONE_CONNECTED;
	}
}

void make_bone_parent(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *actbone, *ebone, *selbone;
	EditBone *flipbone, *flippar;
	short allchildbones= 0, foundselbone= 0;
	short val;
	
	/* find active bone to parent to */
	for (actbone = G.edbo.first; actbone; actbone=actbone->next) {
		if (EBONE_VISIBLE(arm, actbone)) {
			if (actbone->flag & BONE_ACTIVE)
				break;
		}
	}
	if (actbone == NULL) {
		error("Needs an active bone");
		return; 
	}

	/* find selected bones */
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if ((ebone->flag & BONE_SELECTED) && (ebone != actbone)) {
				foundselbone++;
				if (ebone->parent != actbone) allchildbones= 1; 
			}	
		}
	}
	/* abort if no selected bones, and active bone doesn't have a parent to work with instead */
	if (foundselbone==0 && actbone->parent==NULL) {
		error("Need selected bone(s)");
		return;
	}
	
	/* 'Keep Offset' option is only displayed if it's likely to be useful */
	if (allchildbones)
		val= pupmenu("Make Parent%t|Connected%x1|Keep Offset%x2");
	else
		val= pupmenu("Make Parent%t|Connected%x1");
	
	if (val < 1) return;

	if (foundselbone==0 && actbone->parent) {
		/* When only the active bone is selected, and it has a parent,
		 * connect it to the parent, as that is the only possible outcome. 
		 */
		bone_connect_to_existing_parent(actbone);
		
		if (arm->flag & ARM_MIRROR_EDIT) {
			flipbone = armature_bone_get_mirrored(actbone);
			if (flipbone)
				bone_connect_to_existing_parent(flipbone);
		}
	}
	else {
		/* loop through all editbones, parenting all selected bones to the active bone */
		for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
			if (EBONE_VISIBLE(arm, selbone)) {
				if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
					/* parent selbone to actbone */
					bone_connect_to_new_parent(selbone, actbone, val);
					
					if (arm->flag & ARM_MIRROR_EDIT) {
						/* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone 
						 *	(i.e.  selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
						 *	This is useful for arm-chains, for example parenting lower arm to upper arm
						 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
						 *	then just use actbone. Useful when doing upper arm to spine.
						 */
						flipbone = armature_bone_get_mirrored(selbone);
						flippar = armature_bone_get_mirrored(actbone);
						
						if (flipbone) {
							if (flippar)
								bone_connect_to_new_parent(flipbone, flippar, val);
							else
								bone_connect_to_new_parent(flipbone, actbone, val);
						}
					}
				}
			}
		}
	}

	countall(); /* checks selection */
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Make Parent");

	return;
}

static void editbone_clear_parent(EditBone *ebone, int mode)
{
	if (ebone->parent) {
		/* for nice selection */
		ebone->parent->flag &= ~(BONE_TIPSEL);
	}
	
	if (mode==1) ebone->parent= NULL;
	ebone->flag &= ~BONE_CONNECTED;
}

void clear_bone_parent(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone;
	EditBone *flipbone = NULL;
	short val;
	
	val= pupmenu("Clear Parent%t|Clear Parent%x1|Disconnect Bone%x2");
	if (val<1) return;
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & BONE_SELECTED) {
				if (arm->flag & ARM_MIRROR_EDIT)
					flipbone = armature_bone_get_mirrored(ebone);
					
				if (flipbone)
					editbone_clear_parent(flipbone, val);
				editbone_clear_parent(ebone, val);
			}
		}
	}
	
	countall(); // checks selection
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Clear Parent");
}
	

static EditBone *editbone_name_exists (ListBase *ebones, char *name)
{
	EditBone	*eBone;
	
	if (ebones == NULL) ebones = &G.edbo;
	
	for (eBone=ebones->first; eBone; eBone=eBone->next) {
		if (!strcmp(name, eBone->name))
			return eBone;
	}
	return NULL;
}

/* note: there's a unique_bone_name() too! */
void unique_editbone_name (ListBase *ebones, char *name)
{
	char		tempname[64];
	int			number;
	char		*dot;
	
	if (editbone_name_exists(ebones, name)) {
		/*	Strip off the suffix, if it's a number */
		number= strlen(name);
		if (number && isdigit(name[number-1])) {
			dot= strrchr(name, '.');	// last occurrance
			if (dot)
				*dot=0;
		}
		
		for (number = 1; number <=999; number++) {
			sprintf(tempname, "%s.%03d", name, number);
			if (!editbone_name_exists(ebones, tempname)) {
				BLI_strncpy(name, tempname, 32);
				return;
			}
		}
	}
}

/* context; editmode armature */
/* if forked && mirror-edit: makes two bones with flipped names */
void extrude_armature(int forked)
{
	bArmature *arm= G.obedit->data;
	EditBone *newbone, *ebone, *flipbone, *first=NULL;
	int a, totbone= 0, do_extrude;
	
	TEST_EDITARMATURE;
	
	/* since we allow root extrude too, we have to make sure selection is OK */
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			if (ebone->flag & BONE_ROOTSEL) {
				if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
					if (ebone->parent->flag & BONE_TIPSEL)
						ebone->flag &= ~BONE_ROOTSEL;
				}
			}
		}
	}
	
	/* Duplicate the necessary bones */
	for (ebone = G.edbo.first; ((ebone) && (ebone!=first)); ebone=ebone->next) {
		if (EBONE_VISIBLE(arm, ebone)) {
			/* we extrude per definition the tip */
			do_extrude= 0;
			if (ebone->flag & (BONE_TIPSEL|BONE_SELECTED))
				do_extrude= 1;
			else if (ebone->flag & BONE_ROOTSEL) {
				/* but, a bone with parent deselected we do the root... */
				if (ebone->parent && (ebone->parent->flag & BONE_TIPSEL));
				else do_extrude= 2;
			}
			
			if (do_extrude) {
				/* we re-use code for mirror editing... */
				flipbone= NULL;
				if (arm->flag & ARM_MIRROR_EDIT) {
					flipbone= armature_bone_get_mirrored(ebone);
					if (flipbone) {
						forked= 0;	// we extrude 2 different bones
						if (flipbone->flag & (BONE_TIPSEL|BONE_ROOTSEL|BONE_SELECTED))
							/* don't want this bone to be selected... */
							flipbone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
					}
					if ((flipbone==NULL) && (forked))
						flipbone= ebone;
				}
				
				for (a=0; a<2; a++) {
					if (a==1) {
						if (flipbone==NULL)
							break;
						else {
							SWAP(EditBone *, flipbone, ebone);
						}
					}
					
					totbone++;
					newbone = MEM_callocN(sizeof(EditBone), "extrudebone");
					
					if (do_extrude==1) {
						VECCOPY (newbone->head, ebone->tail);
						VECCOPY (newbone->tail, newbone->head);
						newbone->parent = ebone;
						
						newbone->flag = ebone->flag & BONE_TIPSEL;	// copies it, in case mirrored bone
						
						if (newbone->parent) newbone->flag |= BONE_CONNECTED;
					}
					else {
						VECCOPY(newbone->head, ebone->head);
						VECCOPY(newbone->tail, ebone->head);
						newbone->parent= ebone->parent;
						
						newbone->flag= BONE_TIPSEL;
						
						if (newbone->parent && (ebone->flag & BONE_CONNECTED)) {
							newbone->flag |= BONE_CONNECTED;
						}
					}
					
					newbone->weight= ebone->weight;
					newbone->dist= ebone->dist;
					newbone->xwidth= ebone->xwidth;
					newbone->zwidth= ebone->zwidth;
					newbone->ease1= ebone->ease1;
					newbone->ease2= ebone->ease2;
					newbone->rad_head= ebone->rad_tail;	// dont copy entire bone...
					newbone->rad_tail= ebone->rad_tail;
					newbone->segments= 1;
					newbone->layer= ebone->layer;
					
					BLI_strncpy (newbone->name, ebone->name, 32);
					
					if (flipbone && forked) {	// only set if mirror edit
						if (strlen(newbone->name)<30) {
							if (a==0) strcat(newbone->name, "_L");
							else strcat(newbone->name, "_R");
						}
					}
					unique_editbone_name(&G.edbo, newbone->name);
					
					/* Add the new bone to the list */
					BLI_addtail(&G.edbo, newbone);
					if (!first)
						first = newbone;
					
					/* restore ebone if we were flipping */
					if (a==1 && flipbone) 
						SWAP(EditBone *, flipbone, ebone);
				}
			}
			
			/* Deselect the old bone */
			ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
		}		
	}
	/* if only one bone, make this one active */
	if (totbone==1 && first) first->flag |= BONE_ACTIVE;
	
	/* Transform the endpoints */
	countall(); // flushes selection!
	BIF_TransformSetUndo("Extrude");
	initTransform(TFM_TRANSLATION, CTX_NO_PET);
	Transform();
	
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWOOPS, 0);
}

/* context; editmode armature */
void subdivide_armature(int numcuts)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone, *newbone, *tbone, *mbone;
	int a, i;
	
	if (numcuts < 1) return;

	for (mbone = G.edbo.last; mbone; mbone= mbone->prev) {
		if (EBONE_VISIBLE(arm, mbone)) {
			if (mbone->flag & BONE_SELECTED) {
				for (i=numcuts+1; i>1; i--) {
					/* compute cut ratio first */
					float cutratio= 1/(float)i;
					float cutratioI= 1-cutratio;
					
					/* take care of mirrored stuff */
					for (a=0; a<2; a++) {
						float val1[3];
						float val2[3];
						float val3[3];
						
						/* try to find mirrored bone on a != 0 */
						if (a) {
							if (arm->flag & ARM_MIRROR_EDIT)
								ebone= armature_bone_get_mirrored(mbone);
							else 
								ebone= NULL;
						}
						else
							ebone= mbone;
							
						if (ebone) {
							newbone= MEM_mallocN(sizeof(EditBone), "ebone subdiv");
							*newbone = *ebone;
							BLI_addtail(&G.edbo, newbone);
							
							/* calculate location of newbone->head */
							VECCOPY(val1, ebone->head);
							VECCOPY(val2, ebone->tail);
							VECCOPY(val3, newbone->head);
							
							val3[0]= val1[0]*cutratio+val2[0]*cutratioI;
							val3[1]= val1[1]*cutratio+val2[1]*cutratioI;
							val3[2]= val1[2]*cutratio+val2[2]*cutratioI;
							
							VECCOPY(newbone->head, val3);
							VECCOPY(newbone->tail, ebone->tail);
							VECCOPY(ebone->tail, newbone->head);
							
							newbone->rad_head= 0.5*(ebone->rad_head+ebone->rad_tail);
							ebone->rad_tail= newbone->rad_head;
							
							newbone->flag |= BONE_CONNECTED;
							
							unique_editbone_name (&G.edbo, newbone->name);
							
							/* correct parent bones */
							for (tbone = G.edbo.first; tbone; tbone=tbone->next) {
								if (tbone->parent==ebone)
									tbone->parent= newbone;
							}
							newbone->parent= ebone;
						}
					}
				}
			}
		}
	}
	
	if (numcuts==1) BIF_undo_push("Subdivide");
	else BIF_undo_push("Subdivide multi");
}

/* switch direction of bone chains */
void switch_direction_armature (void)
{
	bArmature *arm= (G.obedit) ? G.obedit->data : NULL;
	ListBase chains = {NULL, NULL};
	LinkData *chain;
	
	/* error checking paranoia */
	if (arm == NULL)
		return;
	
	/* get chains of bones (ends on chains) */
	chains_find_tips(&chains);
	if (chains.first == NULL) return;
	
	/* loop over chains, only considering selected and visible bones */
	for (chain= chains.first; chain; chain= chain->next) {
		EditBone *ebo, *child=NULL, *parent=NULL;
		
		/* loop over bones in chain */
		for (ebo= chain->data; ebo; ebo= parent) {
			/* parent is this bone's original parent
			 *	- we store this, as the next bone that is checked is this one
			 *	  but the value of ebo->parent may change here...
			 */
			parent= ebo->parent;
			
			/* only if selected and editable */
			if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) {				
				/* swap head and tail coordinates */
				SWAP(float, ebo->head[0], ebo->tail[0]);
				SWAP(float, ebo->head[1], ebo->tail[1]);
				SWAP(float, ebo->head[2], ebo->tail[2]);
				
				/* do parent swapping:
				 *	- use 'child' as new parent
				 *	- connected flag is only set if points are coincidental
				 */
				ebo->parent= child;
				if ((child) && VecEqual(ebo->head, child->tail))
					ebo->flag |= BONE_CONNECTED;
				else	
					ebo->flag &= ~BONE_CONNECTED;
				
				/* get next bones 
				 *	- child will become the new parent of next bone
				 */
				child= ebo;
			}
			else {
				/* not swapping this bone, however, if its 'parent' got swapped, unparent us from it 
				 * as it will be facing in opposite direction
				 */
				if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) {
					ebo->parent= NULL;
					ebo->flag &= ~BONE_CONNECTED;
				}
				
				/* get next bones
				 *	- child will become new parent of next bone (not swapping occurred, 
				 *	  so set to NULL to prevent infinite-loop)
				 */
				child= NULL;
			}
		}
	}
	
	/* free chains */
	BLI_freelistN(&chains);
	
	BIF_undo_push("Switch Direction");
}

/* editbone alignment */

/* helper to fix a ebone position if its parent has moved due to alignment*/
static void fix_connected_bone(EditBone *ebone)
{
	float diff[3];
	
	if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || VecEqual(ebone->parent->tail, ebone->head))
		return;
	
	/* if the parent has moved we translate child's head and tail accordingly*/
	VecSubf(diff, ebone->parent->tail, ebone->head);
	VecAddf(ebone->head, ebone->head, diff);
	VecAddf(ebone->tail, ebone->tail, diff);
	return;
}

/* helper to recursively find chains of connected bones starting at ebone and fix their position */
static void fix_editbone_connected_children(EditBone *ebone)
{
	EditBone *selbone;
	
	for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
		if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
			fix_connected_bone(selbone);
			fix_editbone_connected_children(selbone);
		}
	}
	return;
}			

static void bone_align_to_bone(EditBone *selbone, EditBone *actbone)
{
	float selboneaxis[3], actboneaxis[3], length;

	VecSubf(actboneaxis, actbone->tail, actbone->head);
	Normalize(actboneaxis);

	VecSubf(selboneaxis, selbone->tail, selbone->head);
	length =  VecLength(selboneaxis);

	VecMulf(actboneaxis, length);
	VecAddf(selbone->tail, selbone->head, actboneaxis);
	selbone->roll = actbone->roll;
	
	/* if the bone being aligned has connected descendants they must be moved
	according to their parent new position, otherwise they would be left
	in an unconsistent state: connected but away from the parent*/
	fix_editbone_connected_children(selbone);
	return;
}

void align_selected_bones(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *actbone, *ebone, *selbone;
	EditBone *flipbone, *flippar;
	short allchildbones= 0, foundselbone= 0;
	
	/* find active bone to align to */
	for (actbone = G.edbo.first; actbone; actbone=actbone->next) {
		if (arm->layer & actbone->layer) {
			if (actbone->flag & BONE_ACTIVE)
				break;
		}
	}
	if (actbone == NULL) {
		error("Needs an active bone");
		return; 
	}

	/* find selected bones */
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (arm->layer & ebone->layer) {
			if ((ebone->flag & BONE_SELECTED) && (ebone != actbone)) {
				foundselbone++;
				if (ebone->parent != actbone) allchildbones= 1; 
			}	
		}
	}
	/* abort if no selected bones, and active bone doesn't have a parent to work with instead */
	if (foundselbone==0 && actbone->parent==NULL) {
		error("Need selected bone(s)");
		return;
	}
	
	if (foundselbone==0 && actbone->parent) {
		/* When only the active bone is selected, and it has a parent,
		 * align it to the parent, as that is the only possible outcome. 
		 */
		bone_align_to_bone(actbone, actbone->parent);
		
		if (arm->flag & ARM_MIRROR_EDIT) {
			flipbone = armature_bone_get_mirrored(actbone);
			if (flipbone)
				bone_align_to_bone(flipbone, flipbone->parent);
		}
	}
	else {
		/* loop through all editbones, aligning all selected bones to the active bone */
		for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
			if (arm->layer & selbone->layer) {
				if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
					/* align selbone to actbone */
					bone_align_to_bone(selbone, actbone);
					
					if (arm->flag & ARM_MIRROR_EDIT) {
						/* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone 
						 *	(i.e.  selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
						 *	This is useful for arm-chains, for example parenting lower arm to upper arm
						 * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
						 *	then just use actbone. Useful when doing upper arm to spine.
						 */
						flipbone = armature_bone_get_mirrored(selbone);
						flippar = armature_bone_get_mirrored(actbone);
						
						if (flipbone) {
							if (flippar)
								bone_align_to_bone(flipbone, flippar);
							else
								bone_align_to_bone(flipbone, actbone);
						}
					}
				}
			}
		}
	}

	countall(); /* checks selection */
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Align bones");

	return;
}

/* ***************** Pose tools ********************* */

void clear_armature(Object *ob, char mode)
{
	bPoseChannel *pchan;
	bArmature	*arm;

	arm= get_armature(ob);
	if (arm == NULL)
		return;
	
	/* only clear those channels that are not locked */
	for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
		if (pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
			if (arm->layer & pchan->bone->layer) {
				switch (mode) {
					case 'r':
						if (pchan->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ)) {
							float eul[3], oldeul[3], quat1[4];
							
							QUATCOPY(quat1, pchan->quat);
							QuatToEul(pchan->quat, oldeul);
							eul[0]= eul[1]= eul[2]= 0.0f;
							
							if (pchan->protectflag & OB_LOCK_ROTX)
								eul[0]= oldeul[0];
							if (pchan->protectflag & OB_LOCK_ROTY)
								eul[1]= oldeul[1];
							if (pchan->protectflag & OB_LOCK_ROTZ)
								eul[2]= oldeul[2];
							
							EulToQuat(eul, pchan->quat);
							/* quaternions flip w sign to accumulate rotations correctly */
							if ((quat1[0]<0.0f && pchan->quat[0]>0.0f) || (quat1[0]>0.0f && pchan->quat[0]<0.0f)) {
								QuatMulf(pchan->quat, -1.0f);
							}
						}						
						else { 
							pchan->quat[1]=pchan->quat[2]=pchan->quat[3]=0.0F; 
							pchan->quat[0]=1.0F;
						}
						break;
					case 'g':
						if ((pchan->protectflag & OB_LOCK_LOCX)==0)
							pchan->loc[0]= 0.0f;
						if ((pchan->protectflag & OB_LOCK_LOCY)==0)
							pchan->loc[1]= 0.0f;
						if ((pchan->protectflag & OB_LOCK_LOCZ)==0)
							pchan->loc[2]= 0.0f;
						break;
					case 's':
						if ((pchan->protectflag & OB_LOCK_SCALEX)==0)
							pchan->size[0]= 1.0f;
						if ((pchan->protectflag & OB_LOCK_SCALEY)==0)
							pchan->size[1]= 1.0f;
						if ((pchan->protectflag & OB_LOCK_SCALEZ)==0)
							pchan->size[2]= 1.0f;
						break;
						
				}
				
				/* the current values from IPO's may not be zero, so tag as unkeyed */
				pchan->bone->flag |= BONE_UNKEYED;
			}
		}
	}
	
	DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
	/* no update for this object, this will execute the action again */
	/* is weak... like for ipo editing which uses ctime now... */
	where_is_pose (ob);
	ob->recalc= 0;
}

/* helper for function below */
static int clear_active_flag(Object *ob, Bone *bone, void *data) 
{
	bone->flag &= ~BONE_ACTIVE;
	return 0;
}


/* called from editview.c, for mode-less pose selection */
int do_pose_selectbuffer(Base *base, unsigned int *buffer, short hits)
{
	Object *ob= base->object;
	Bone *nearBone;
	
	if (!ob || !ob->pose) return 0;

	nearBone= get_bone_from_selectbuffer(base, buffer, hits, 1);

	if (nearBone) {
		bArmature *arm= ob->data;
		
		/* since we do unified select, we don't shift+select a bone if the armature object was not active yet */
		if (!(G.qual & LR_SHIFTKEY) || (base != BASACT)) {
			deselectall_posearmature(ob, 0, 0);
			nearBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
			select_actionchannel_by_name(ob->action, nearBone->name, 1);
		}
		else {
			if (nearBone->flag & BONE_SELECTED) {
				/* if not active, we make it active */
				if((nearBone->flag & BONE_ACTIVE)==0) {
					bone_looper(ob, arm->bonebase.first, NULL, clear_active_flag);
					nearBone->flag |= BONE_ACTIVE;
				}
				else {
					nearBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
					select_actionchannel_by_name(ob->action, nearBone->name, 0);
				}
			}
			else {
				bone_looper(ob, arm->bonebase.first, NULL, clear_active_flag);
				
				nearBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
				select_actionchannel_by_name(ob->action, nearBone->name, 1);
			}
		}
		
		/* in weightpaint we select the associated vertex group too */
		if (G.f & G_WEIGHTPAINT) {
			if (nearBone->flag & BONE_ACTIVE) {
				vertexgroup_select_by_name(OBACT, nearBone->name);
				DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
			}
		}
		
		allqueue(REDRAWVIEW3D, 0);
		allqueue(REDRAWACTION, 0);
		allqueue(REDRAWIPO, 0);		/* To force action/constraint ipo update */
		allqueue(REDRAWBUTSEDIT, 0);
		allqueue(REDRAWBUTSOBJECT, 0);
		allqueue(REDRAWOOPS, 0);
	}
	
	return nearBone!=NULL;
}

/* test==0: deselect all
   test==1: swap select (apply to all the opposite of current situation) 
   test==2: only clear active tag
   test==3: swap select (no test / inverse selection status of all independently)
*/
void deselectall_posearmature (Object *ob, int test, int doundo)
{
	bArmature *arm;
	bPoseChannel *pchan;
	int	selectmode= 0;
	
	/* we call this from outliner too, but with OBACT set OK */
	if (ELEM(NULL, ob, ob->pose)) return;
	arm= get_armature(ob);
	
	/*	Determine if we're selecting or deselecting	*/
	if (test==1) {
		for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
			if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & BONE_HIDDEN_P)) {
				if (pchan->bone->flag & BONE_SELECTED)
					break;
			}
		}
		
		if (pchan == NULL)
			selectmode= 1;
	}
	else if (test == 2)
		selectmode= 2;
	
	/*	Set the flags accordingly	*/
	for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
		if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & BONE_HIDDEN_P)) {
			if (test==3) {
				pchan->bone->flag ^= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
				pchan->bone->flag &= ~BONE_ACTIVE;
			}
			else {
				if (selectmode==0) pchan->bone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
				else if (selectmode==1) pchan->bone->flag |= BONE_SELECTED;
				else pchan->bone->flag &= ~BONE_ACTIVE;
			}
		}
	}
	
	/* action editor */
	if (test == 3) {
		deselect_actionchannels(ob->action, 2); /* inverts selection */
	}
	else {
		deselect_actionchannels(ob->action, 0);	/* deselects for sure */
		if (selectmode == 1)
			deselect_actionchannels(ob->action, 1);	/* swaps */
	}
	
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWOOPS, 0);
	allqueue(REDRAWACTION, 0);
	
	countall();
	
	if (doundo) {
		if (selectmode==1) BIF_undo_push("Select All");
		else BIF_undo_push("Deselect All");
	}
}


int bone_looper(Object *ob, Bone *bone, void *data,
                        int (*bone_func)(Object *, Bone *, void *)) 
{
    /* We want to apply the function bone_func to every bone 
     * in an armature -- feed bone_looper the first bone and 
     * a pointer to the bone_func and watch it go!. The int count 
     * can be useful for counting bones with a certain property
     * (e.g. skinnable)
     */
    int count = 0;

    if (bone) {
		/* only do bone_func if the bone is non null */
        count += bone_func(ob, bone, data);
		
		/* try to execute bone_func for the first child */
        count += bone_looper(ob, bone->childbase.first, data, bone_func);
		
		/* try to execute bone_func for the next bone at this
		 * depth of the recursion.
		 */
        count += bone_looper(ob, bone->next, data, bone_func);
    }

    return count;
}


static int bone_skinnable(Object *ob, Bone *bone, void *datap)
{
    /* Bones that are deforming
     * are regarded to be "skinnable" and are eligible for
     * auto-skinning.
     *
     * This function performs 2 functions:
     *
     *   a) It returns 1 if the bone is skinnable.
     *      If we loop over all bones with this 
     *      function, we can count the number of
     *      skinnable bones.
     *   b) If the pointer data is non null,
     *      it is treated like a handle to a
     *      bone pointer -- the bone pointer
     *      is set to point at this bone, and
     *      the pointer the handle points to
     *      is incremented to point to the
     *      next member of an array of pointers
     *      to bones. This way we can loop using
     *      this function to construct an array of
     *      pointers to bones that point to all
     *      skinnable bones.
     */
	Bone ***hbone;
	int a, segments;
	struct { Object *armob; void *list; int heat; } *data = datap;

	if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
		if (!(bone->flag & BONE_NO_DEFORM)) {
			if (data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
				segments = bone->segments;
			else
				segments = 1;
			
			if (data->list != NULL) {
				hbone = (Bone ***) &data->list;
				
				for (a=0; a<segments; a++) {
					**hbone = bone;
					++*hbone;
				}
			}
			return segments;
		}
	}
    return 0;
}

static int add_defgroup_unique_bone(Object *ob, Bone *bone, void *data) 
{
    /* This group creates a vertex group to ob that has the
      * same name as bone (provided the bone is skinnable). 
	 * If such a vertex group aleady exist the routine exits.
      */
	if (!(bone->flag & BONE_NO_DEFORM)) {
		if (!get_named_vertexgroup(ob,bone->name)) {
			add_defgroup_name(ob, bone->name);
			return 1;
		}
    }
    return 0;
}

static int dgroup_skinnable(Object *ob, Bone *bone, void *datap) 
{
    /* Bones that are deforming
     * are regarded to be "skinnable" and are eligible for
     * auto-skinning.
     *
     * This function performs 2 functions:
     *
     *   a) If the bone is skinnable, it creates 
     *      a vertex group for ob that has
     *      the name of the skinnable bone
     *      (if one doesn't exist already).
     *   b) If the pointer data is non null,
     *      it is treated like a handle to a
     *      bDeformGroup pointer -- the 
     *      bDeformGroup pointer is set to point
     *      to the deform group with the bone's
     *      name, and the pointer the handle 
     *      points to is incremented to point to the
     *      next member of an array of pointers
     *      to bDeformGroups. This way we can loop using
     *      this function to construct an array of
     *      pointers to bDeformGroups, all with names
     *      of skinnable bones.
     */
    bDeformGroup ***hgroup, *defgroup;
	int a, segments;
	struct { Object *armob; void *list; int heat; } *data= datap;

	if (!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
	   if (!(bone->flag & BONE_NO_DEFORM)) {
			if (data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
				segments = bone->segments;
			else
				segments = 1;
			
			if (!(defgroup = get_named_vertexgroup(ob, bone->name)))
				defgroup = add_defgroup_name(ob, bone->name);
			
			if (data->list != NULL) {
				hgroup = (bDeformGroup ***) &data->list;
				
				for (a=0; a<segments; a++) {
					**hgroup = defgroup;
					++*hgroup;
				}
			}
			return segments;
		}
	}
    return 0;
}

static void add_vgroups__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s)
{
	/* DerivedMesh mapFunc for getting final coords in weight paint mode */

	float (*verts)[3] = userData;
	VECCOPY(verts[index], co);
}

static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], int *selected, float scale)
{
	/* Create vertex group weights from envelopes */

	Bone *bone;
	bDeformGroup *dgroup;
	float distance;
	int i, iflip, j;

	/* for each vertex in the mesh */
	for (i=0; i < mesh->totvert; i++) {
		iflip = (dgroupflip)? mesh_get_x_mirror_vert(ob, i): 0;
		
		/* for each skinnable bone */
		for (j=0; j < numbones; ++j) {
			if (!selected[j])
				continue;
			
			bone = bonelist[j];
			dgroup = dgrouplist[j];
			
			/* store the distance-factor from the vertex to the bone */
			distance = distfactor_to_bone (verts[i], root[j], tip[j],
				bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale);
			
			/* add the vert to the deform group if weight!=0.0 */
			if (distance!=0.0)
				add_vert_to_defgroup (ob, dgroup, i, distance, WEIGHT_REPLACE);
			else
				remove_vert_defgroup (ob, dgroup, i);
			
			/* do same for mirror */
			if (dgroupflip && dgroupflip[j] && iflip >= 0) {
				if (distance!=0.0)
					add_vert_to_defgroup (ob, dgroupflip[j], iflip, distance,
						WEIGHT_REPLACE);
				else
					remove_vert_defgroup (ob, dgroupflip[j], iflip);
			}
		}
	}
}

void add_verts_to_dgroups(Object *ob, Object *par, int heat, int mirror)
{
	/* This functions implements the automatic computation of vertex group
	 * weights, either through envelopes or using a heat equilibrium.
	 *
	 * This function can be called both when parenting a mesh to an armature,
	 * or in weightpaint + posemode. In the latter case selection is taken
	 * into account and vertex weights can be mirrored.
	 *
	 * The mesh vertex positions used are either the final deformed coords
	 * from the derivedmesh in weightpaint mode, the final subsurf coords
	 * when parenting, or simply the original mesh coords.
	 */

	bArmature *arm;
	Bone **bonelist, *bone;
	bDeformGroup **dgrouplist, **dgroupflip;
	bDeformGroup *dgroup, *curdg;
	bPoseChannel *pchan;
	Mesh *mesh;
	Mat4 *bbone = NULL;
	float (*root)[3], (*tip)[3], (*verts)[3];
	int *selected;
	int numbones, vertsfilled = 0, i, j, segments = 0;
	int wpmode = (G.f & G_WEIGHTPAINT);
	struct { Object *armob; void *list; int heat; } looper_data;

	/* If the parent object is not an armature exit */
	arm = get_armature(par);
	if (!arm)
		return;
	
	looper_data.armob = par;
	looper_data.heat= heat;
	looper_data.list= NULL;

	/* count the number of skinnable bones */
	numbones = bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable);
	
	if (numbones == 0)
		return;
	
	/* create an array of pointer to bones that are skinnable
	 * and fill it with all of the skinnable bones */
	bonelist = MEM_callocN(numbones*sizeof(Bone *), "bonelist");
	looper_data.list= bonelist;
	bone_looper(ob, arm->bonebase.first, &looper_data, bone_skinnable);

	/* create an array of pointers to the deform groups that
	 * coorespond to the skinnable bones (creating them
	 * as necessary. */
	dgrouplist = MEM_callocN(numbones*sizeof(bDeformGroup *), "dgrouplist");
	dgroupflip = MEM_callocN(numbones*sizeof(bDeformGroup *), "dgroupflip");

	looper_data.list= dgrouplist;
	bone_looper(ob, arm->bonebase.first, &looper_data, dgroup_skinnable);

	/* create an array of root and tip positions transformed into
	 * global coords */
	root = MEM_callocN(numbones*sizeof(float)*3, "root");
	tip = MEM_callocN(numbones*sizeof(float)*3, "tip");
	selected = MEM_callocN(numbones*sizeof(int), "selected");

	for (j=0; j < numbones; ++j) {
   		bone = bonelist[j];
		dgroup = dgrouplist[j];
		
		/* handle bbone */
		if (heat) {
			if (segments == 0) {
				segments = 1;
				bbone = NULL;
				
				if ((par->pose) && (pchan=get_pose_channel(par->pose, bone->name))) {
					if (bone->segments > 1) {
						segments = bone->segments;
						bbone = b_bone_spline_setup(pchan, 1);
					}
				}
			}
			
			segments--;
		}
		
		/* compute root and tip */
		if (bbone) {
			VECCOPY(root[j], bbone[segments].mat[3]);
			Mat4MulVecfl(bone->arm_mat, root[j]);
			if ((segments+1) < bone->segments) {
				VECCOPY(tip[j], bbone[segments+1].mat[3])
				Mat4MulVecfl(bone->arm_mat, tip[j]);
			}
			else
				VECCOPY(tip[j], bone->arm_tail)
		}
		else {
			VECCOPY(root[j], bone->arm_head);
			VECCOPY(tip[j], bone->arm_tail);
		}
		
		Mat4MulVecfl(par->obmat, root[j]);
		Mat4MulVecfl(par->obmat, tip[j]);
		
		/* set selected */
		if (wpmode) {
			if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))
				selected[j] = 1;
		}
		else
			selected[j] = 1;
		
		/* find flipped group */
		if (mirror) {
			char name[32];
			
			BLI_strncpy(name, dgroup->name, 32);
			// 0 = don't strip off number extensions
			bone_flip_name(name, 0);
			
			for (curdg = ob->defbase.first; curdg; curdg=curdg->next) {
				if (!strcmp(curdg->name, name))
					break;
			}
			
			dgroupflip[j] = curdg;
		}
	}

	/* create verts */
    mesh = (Mesh*)ob->data;
	verts = MEM_callocN(mesh->totvert*sizeof(*verts), "closestboneverts");

	if (wpmode) {
		/* if in weight paint mode, use final verts from derivedmesh */
		DerivedMesh *dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH);
		
		if (dm->foreachMappedVert) {
			dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void*)verts);
			vertsfilled = 1;
		}
		
		dm->release(dm);
	}
	else if (modifiers_findByType(ob, eModifierType_Subsurf)) {
		/* is subsurf on? Lets use the verts on the limit surface then.
	 	 * = same amount of vertices as mesh, but vertices  moved to the
		 * subsurfed position, like for 'optimal'. */
		subsurf_calculate_limit_positions(mesh, verts);
		vertsfilled = 1;
	}

	/* transform verts to global space */
	for (i=0; i < mesh->totvert; i++) {
		if (!vertsfilled)
			VECCOPY(verts[i], mesh->mvert[i].co)
		Mat4MulVecfl(ob->obmat, verts[i]);
	}

	/* compute the weights based on gathered vertices and bones */
	if (heat) {
		heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip,
			root, tip, selected);
	}
	else {
		envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist,
			dgroupflip, root, tip, selected, Mat4ToScalef(par->obmat));
	}
	
    /* free the memory allocated */
    MEM_freeN(bonelist);
    MEM_freeN(dgrouplist);
	MEM_freeN(dgroupflip);
	MEM_freeN(root);
	MEM_freeN(tip);
	MEM_freeN(selected);
	MEM_freeN(verts);
}

void create_vgroups_from_armature(Object *ob, Object *par)
{
	/* Lets try to create some vertex groups 
	 * based on the bones of the parent armature.
	 */

	bArmature *arm;
	short mode;

	/* If the parent object is not an armature exit */
	arm = get_armature(par);
	if (!arm)
		return;

	/* Prompt the user on whether/how they want the vertex groups
	 * added to the child mesh */
    mode= pupmenu("Create Vertex Groups? %t|"
				  "Don't Create Groups %x1|"
				  "Name Groups %x2|"
                  "Create From Envelopes %x3|"
				  "Create From Bone Heat %x4|");
	switch (mode) {
	case 2:
		/* Traverse the bone list, trying to create empty vertex 
		 * groups cooresponding to the bone.
		 */
		bone_looper(ob, arm->bonebase.first, NULL,
					add_defgroup_unique_bone);
		if (ob->type == OB_MESH)
			create_dverts(ob->data);
		
		break;
	
	case 3:
	case 4:
		/* Traverse the bone list, trying to create vertex groups 
		 * that are populated with the vertices for which the
		 * bone is closest.
		 */
		add_verts_to_dgroups(ob, par, (mode == 4), 0);
		break;
	}
} 

static int hide_selected_pose_bone(Object *ob, Bone *bone, void *ptr) 
{
	bArmature *arm= ob->data;
	
	if (arm->layer & bone->layer) {
		if (bone->flag & BONE_SELECTED) {
			bone->flag |= BONE_HIDDEN_P;
			bone->flag &= ~(BONE_SELECTED|BONE_ACTIVE);
		}
	}
	return 0;
}

/* active object is armature */
void hide_selected_pose_bones(void) 
{
	bArmature *arm= OBACT->data;

	if (!arm)
		return;

	bone_looper(OBACT, arm->bonebase.first, NULL, 
				hide_selected_pose_bone);
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWACTION, 0);
	BIF_undo_push("Hide Bones");
}

static int hide_unselected_pose_bone(Object *ob, Bone *bone, void *ptr) 
{
	bArmature *arm= ob->data;
	
	if (arm->layer & bone->layer) {
		// hrm... typo here?
		if (~bone->flag & BONE_SELECTED) {
			bone->flag |= BONE_HIDDEN_P;
			bone->flag &= ~BONE_ACTIVE;
		}
	}
	return 0;
}

/* active object is armature */
void hide_unselected_pose_bones(void) 
{
	bArmature		*arm;

	arm=get_armature(OBACT);

	if (!arm)
		return;

	bone_looper(OBACT, arm->bonebase.first, NULL, 
				hide_unselected_pose_bone);

	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWACTION, 0);
	BIF_undo_push("Hide Unselected Bone");
}

static int show_pose_bone(Object *ob, Bone *bone, void *ptr) 
{
	bArmature *arm= ob->data;
	
	if (arm->layer & bone->layer) {
		if (bone->flag & BONE_HIDDEN_P) {
			bone->flag &= ~BONE_HIDDEN_P;
			bone->flag |= BONE_SELECTED;
		}
	}

	return 0;
}

/* active object is armature in posemode */
void show_all_pose_bones(void) 
{
	bArmature		*arm;

	arm=get_armature(OBACT);

	if (!arm)
		return;

	bone_looper(OBACT, arm->bonebase.first, NULL, 
				show_pose_bone);

	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWACTION, 0);
	BIF_undo_push("Reveal Bones");
}


/* ************* RENAMING DISASTERS ************ */

/* note: there's a unique_editbone_name() too! */
void unique_bone_name (bArmature *arm, char *name)
{
	char		tempname[64];
	int			number;
	char		*dot;
	
	if (get_named_bone(arm, name)) {
		
		/*	Strip off the suffix, if it's a number */
		number= strlen(name);
		if(number && isdigit(name[number-1])) {
			dot= strrchr(name, '.');	// last occurrance
			if (dot)
				*dot=0;
		}
		
		for (number = 1; number <=999; number++) {
			sprintf (tempname, "%s.%03d", name, number);
			if (!get_named_bone(arm, tempname)) {
				BLI_strncpy (name, tempname, 32);
				return;
			}
		}
	}
}

#define MAXBONENAME 32
/* helper call for armature_bone_rename */
static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
{
	bConstraint *curcon;
	bConstraintTarget *ct;
	
	for (curcon = conlist->first; curcon; curcon=curcon->next) {
		bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
		ListBase targets = {NULL, NULL};
		
		if (cti && cti->get_constraint_targets) {
			cti->get_constraint_targets(curcon, &targets);
			
			for (ct= targets.first; ct; ct= ct->next) {
				if (ct->tar == ob) {
					if (!strcmp(ct->subtarget, oldname) )
						BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
				}
			}
			
			if (cti->flush_constraint_targets)
				cti->flush_constraint_targets(curcon, &targets, 0);
		}	
	}
}

/* called by UI for renaming a bone */
/* warning: make sure the original bone was not renamed yet! */
/* seems messy, but thats what you get with not using pointers but channel names :) */
void armature_bone_rename(bArmature *arm, char *oldnamep, char *newnamep)
{
	Object *ob;
	Ipo *ipo;
	char newname[MAXBONENAME];
	char oldname[MAXBONENAME];
	
	/* names better differ! */
	if(strncmp(oldnamep, newnamep, MAXBONENAME)) {
		
		/* we alter newname string... so make copy */
		BLI_strncpy(newname, newnamep, MAXBONENAME);
		/* we use oldname for search... so make copy */
		BLI_strncpy(oldname, oldnamep, MAXBONENAME);
		
		/* now check if we're in editmode, we need to find the unique name */
		if ((G.obedit) && (G.obedit->data==arm)) {
			EditBone	*eBone;
			
			eBone= editbone_name_exists(&G.edbo, oldname);
			if (eBone) {
				unique_editbone_name(&G.edbo, newname);
				BLI_strncpy(eBone->name, newname, MAXBONENAME);
			}
			else return;
		}
		else {
			Bone *bone= get_named_bone(arm, oldname);
			
			if (bone) {
				unique_bone_name (arm, newname);
				BLI_strncpy(bone->name, newname, MAXBONENAME);
			}
			else return;
		}
		
		/* do entire dbase - objects */
		for (ob= G.main->object.first; ob; ob= ob->id.next) {
			/* we have the object using the armature */
			if (arm==ob->data) {
				Object *cob;
				bAction  *act;
				bActionChannel *achan;
				bActionStrip *strip;
				
				/* Rename action channel if necessary */
				act = ob->action;
				if (act && !act->id.lib) {
					/*	Find the appropriate channel */
					achan= get_action_channel(act, oldname);
					if (achan) 
						BLI_strncpy(achan->name, newname, MAXBONENAME);
				}
		
				/* Rename the pose channel, if it exists */
				if (ob->pose) {
					bPoseChannel *pchan = get_pose_channel(ob->pose, oldname);
					if (pchan)
						BLI_strncpy(pchan->name, newname, MAXBONENAME);
				}
				
				/* check all nla-strips too */
				for (strip= ob->nlastrips.first; strip; strip= strip->next) {
					/* Rename action channel if necessary */
					act = strip->act;
					if (act && !act->id.lib) {
						/*	Find the appropriate channel */
						achan= get_action_channel(act, oldname);
						if (achan) 
							BLI_strncpy(achan->name, newname, MAXBONENAME);
					}
				}
				
				/* Update any object constraints to use the new bone name */
				for (cob= G.main->object.first; cob; cob= cob->id.next) {
					if (cob->constraints.first)
						constraint_bone_name_fix(ob, &cob->constraints, oldname, newname);
					if (cob->pose) {
						bPoseChannel *pchan;
						for (pchan = cob->pose->chanbase.first; pchan; pchan=pchan->next) {
							constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname);
						}
					}
				}
			}
					
			/* See if an object is parented to this armature */
			if (ob->parent && (ob->parent->data == arm)) {
				if (ob->partype==PARBONE) {
					/* bone name in object */
					if (!strcmp(ob->parsubstr, oldname))
						BLI_strncpy(ob->parsubstr, newname, MAXBONENAME);
				}
			}
			
			if (modifiers_usesArmature(ob, arm)) { 
				bDeformGroup *dg;
				/* bone name in defgroup */
				for (dg=ob->defbase.first; dg; dg=dg->next) {
					if (!strcmp(dg->name, oldname))
					   BLI_strncpy(dg->name, newname, MAXBONENAME);
				}
			}
		}
		
		/* do entire db - ipo's for the drivers */
		for (ipo= G.main->ipo.first; ipo; ipo= ipo->id.next) {
			IpoCurve *icu;
			
			/* check each curve's driver */
			for (icu= ipo->curve.first; icu; icu= icu->next) {
				IpoDriver *icd= icu->driver;
				
				if ((icd) && (icd->ob)) {
					ob= icd->ob;
					
					if (icu->driver->type == IPO_DRIVER_TYPE_NORMAL) {
						if (!strcmp(oldname, icd->name))
							BLI_strncpy(icd->name, newname, MAXBONENAME);
					}
					else {
						/* TODO: pydrivers need to be treated differently */
					}
				}
			}			
		}
	}
}

/* context editmode object */
void armature_flip_names(void)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone;
	char newname[32];
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (arm->layer & ebone->layer) {
			if (ebone->flag & BONE_SELECTED) {
				BLI_strncpy(newname, ebone->name, sizeof(newname));
				bone_flip_name(newname, 1);		// 1 = do strip off number extensions
				armature_bone_rename(G.obedit->data, ebone->name, newname);
			}
		}
	}
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Flip names");
}

/* context: edtimode armature */
void armature_autoside_names(short axis)
{
	bArmature *arm= G.obedit->data;
	EditBone *ebone;
	char newname[32];
	
	for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
		if (arm->layer & ebone->layer) {
			if (ebone->flag & BONE_SELECTED) {
				BLI_strncpy(newname, ebone->name, sizeof(newname));
				bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]);
				armature_bone_rename(G.obedit->data, ebone->name, newname);
			}
		}
	}
	
	allqueue(REDRAWVIEW3D, 0);
	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWBUTSOBJECT, 0);
	allqueue(REDRAWACTION, 0);
	allqueue(REDRAWOOPS, 0);
	BIF_undo_push("Auto-side name");
}

/* context: editmode armature */
EditBone *armature_bone_get_mirrored(EditBone *ebo)
{
	EditBone *eboflip= NULL;
	char name[32];
	
	BLI_strncpy(name, ebo->name, sizeof(name));
	bone_flip_name(name, 0);		// 0 = don't strip off number extensions

	for (eboflip=G.edbo.first; eboflip; eboflip=eboflip->next) {
		if (ebo != eboflip) {
			if (!strcmp (name, eboflip->name)) 
				break;
		}
	}
	
	return eboflip;
}

/* if editbone (partial) selected, copy data */
/* context; editmode armature, with mirror editing enabled */
void transform_armature_mirror_update(void)
{
	EditBone *ebo, *eboflip;
	
	for (ebo=G.edbo.first; ebo; ebo=ebo->next) {
		/* no layer check, correct mirror is more important */
		if (ebo->flag & (BONE_TIPSEL|BONE_ROOTSEL)) {
			eboflip= armature_bone_get_mirrored(ebo);
			
			if (eboflip) {
				/* we assume X-axis flipping for now */
				if (ebo->flag & BONE_TIPSEL) {
					EditBone *children;
					
					eboflip->tail[0]= -ebo->tail[0];
					eboflip->tail[1]= ebo->tail[1];
					eboflip->tail[2]= ebo->tail[2];
					eboflip->rad_tail= ebo->rad_tail;

					/* Also move connected children, in case children's name aren't mirrored properly */
					for (children=G.edbo.first; children; children=children->next) {
						if (children->parent == eboflip && children->flag & BONE_CONNECTED) {
							VECCOPY(children->head, eboflip->tail);
							children->rad_head = ebo->rad_tail;
						}
					}
				}
				if (ebo->flag & BONE_ROOTSEL) {
					eboflip->head[0]= -ebo->head[0];
					eboflip->head[1]= ebo->head[1];
					eboflip->head[2]= ebo->head[2];
					eboflip->rad_head= ebo->rad_head;
					
					/* Also move connected parent, in case parent's name isn't mirrored properly */
					if (eboflip->parent && eboflip->flag & BONE_CONNECTED)
					{
						EditBone *parent = eboflip->parent;
						VECCOPY(parent->tail, eboflip->head);
						parent->rad_tail = ebo->rad_head;
					}
				}
				if (ebo->flag & BONE_SELECTED) {
					eboflip->dist= ebo->dist;
					eboflip->roll= -ebo->roll;
					eboflip->xwidth= ebo->xwidth;
					eboflip->zwidth= ebo->zwidth;
				}
			}
		}
	}
}


/*****************************************************************************************************/
/*************************************** SKELETON GENERATOR ******************************************/
/*****************************************************************************************************/

/**************************************** SYMMETRY HANDLING ******************************************/

void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level);

void mirrorAlongAxis(float v[3], float center[3], float axis[3])
{
	float dv[3], pv[3];
	
	VecSubf(dv, v, center);
	Projf(pv, dv, axis);
	VecMulf(pv, -2);
	VecAddf(v, v, pv);
}

/* Helper structure for radial symmetry */
typedef struct RadialArc
{
	ReebArc *arc; 
	float n[3]; /* normalized vector joining the nodes of the arc */
} RadialArc;

void reestablishRadialSymmetry(ReebNode *node, int depth, float axis[3])
{
	RadialArc *ring = NULL;
	RadialArc *unit;
	float limit = G.scene->toolsettings->skgen_symmetry_limit;
	int symmetric = 1;
	int count = 0;
	int i;

	/* count the number of arcs in the symmetry ring */
	for (i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		/* depth is store as a negative in flag. symmetry level is positive */
		if (connectedArc->flags == -depth)
		{
			count++;
		}
	}

	ring = MEM_callocN(sizeof(RadialArc) * count, "radial symmetry ring");
	unit = ring;

	/* fill in the ring */
	for (unit = ring, i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		/* depth is store as a negative in flag. symmetry level is positive */
		if (connectedArc->flags == -depth)
		{
			ReebNode *otherNode = OTHER_NODE(connectedArc, node);
			float vec[3];

			unit->arc = connectedArc;

			/* project the node to node vector on the symmetry plane */
			VecSubf(unit->n, otherNode->p, node->p);
			Projf(vec, unit->n, axis);
			VecSubf(unit->n, unit->n, vec);

			Normalize(unit->n);

			unit++;
		}
	}

	/* sort ring */
	for (i = 0; i < count - 1; i++)
	{
		float minAngle = 3; /* arbitrary high value, higher than 2, at least */
		int minIndex = -1;
		int j;

		for (j = i + 1; j < count; j++)
		{
			float angle = Inpf(ring[i].n, ring[j].n);

			/* map negative values to 1..2 */
			if (angle < 0)
			{
				angle = 1 - angle;
			}

			if (angle < minAngle)
			{
				minIndex = j;
				minAngle = angle;
			}
		}

		/* swap if needed */
		if (minIndex != i + 1)
		{
			RadialArc tmp;
			tmp = ring[i + 1];
			ring[i + 1] = ring[minIndex];
			ring[minIndex] = tmp;
		}
	}

	for (i = 0; i < count && symmetric; i++)
	{
		ReebNode *node1, *node2;
		float tangent[3];
		float normal[3];
		float p[3];
		int j = (i + 1) % count; /* next arc in the circular list */

		VecAddf(tangent, ring[i].n, ring[j].n);
		Crossf(normal, tangent, axis);
		
		node1 = OTHER_NODE(ring[i].arc, node);
		node2 = OTHER_NODE(ring[j].arc, node);

		VECCOPY(p, node2->p);
		mirrorAlongAxis(p, node->p, normal);
		
		/* check if it's within limit before continuing */
		if (VecLenf(node1->p, p) > limit)
		{
			symmetric = 0;
		}

	}

	if (symmetric)
	{
		/* first pass, merge incrementally */
		for (i = 0; i < count - 1; i++)
		{
			ReebNode *node1, *node2;
			float tangent[3];
			float normal[3];
			int j = i + 1;
	
			VecAddf(tangent, ring[i].n, ring[j].n);
			Crossf(normal, tangent, axis);
			
			node1 = OTHER_NODE(ring[i].arc, node);
			node2 = OTHER_NODE(ring[j].arc, node);
	
			/* mirror first node and mix with the second */
			mirrorAlongAxis(node1->p, node->p, normal);
			VecLerpf(node2->p, node2->p, node1->p, 1.0f / (j + 1));
			
			/* Merge buckets
			 * there shouldn't be any null arcs here, but just to be safe 
			 * */
			if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0)
			{
				ReebArcIterator iter1, iter2;
				EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
				
				initArcIterator(&iter1, ring[i].arc, node);
				initArcIterator(&iter2, ring[j].arc, node);
				
				bucket1 = nextBucket(&iter1);
				bucket2 = nextBucket(&iter2);
			
				/* Make sure they both start at the same value */	
				while(bucket1 && bucket1->val < bucket2->val)
				{
					bucket1 = nextBucket(&iter1);
				}
				
				while(bucket2 && bucket2->val < bucket1->val)
				{
					bucket2 = nextBucket(&iter2);
				}
		
		
				for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
				{
					bucket2->nv += bucket1->nv; /* add counts */
					
					/* mirror on axis */
					mirrorAlongAxis(bucket1->p, node->p, normal);
					/* add bucket2 in bucket1 */
					VecLerpf(bucket2->p, bucket2->p, bucket1->p, (float)bucket1->nv / (float)(bucket2->nv));
				}
			}
		}
		
		/* second pass, mirror back on previous arcs */
		for (i = count - 1; i > 0; i--)
		{
			ReebNode *node1, *node2;
			float tangent[3];
			float normal[3];
			int j = i - 1;
	
			VecAddf(tangent, ring[i].n, ring[j].n);
			Crossf(normal, tangent, axis);
			
			node1 = OTHER_NODE(ring[i].arc, node);
			node2 = OTHER_NODE(ring[j].arc, node);
	
			/* copy first node than mirror */
			VECCOPY(node2->p, node1->p);
			mirrorAlongAxis(node2->p, node->p, normal);
			
			/* Copy buckets
			 * there shouldn't be any null arcs here, but just to be safe 
			 * */
			if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0)
			{
				ReebArcIterator iter1, iter2;
				EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
				
				initArcIterator(&iter1, ring[i].arc, node);
				initArcIterator(&iter2, ring[j].arc, node);
				
				bucket1 = nextBucket(&iter1);
				bucket2 = nextBucket(&iter2);
			
				/* Make sure they both start at the same value */	
				while(bucket1 && bucket1->val < bucket2->val)
				{
					bucket1 = nextBucket(&iter1);
				}
				
				while(bucket2 && bucket2->val < bucket1->val)
				{
					bucket2 = nextBucket(&iter2);
				}
		
		
				for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
				{
					/* copy and mirror back to bucket2 */			
					bucket2->nv = bucket1->nv;
					VECCOPY(bucket2->p, bucket1->p);
					mirrorAlongAxis(bucket2->p, node->p, normal);
				}
			}
		}
	}

	MEM_freeN(ring);
}

void reestablishAxialSymmetry(ReebNode *node, int depth, float axis[3])
{
	ReebArc *arc1 = NULL;
	ReebArc *arc2 = NULL;
	ReebNode *node1 = NULL, *node2 = NULL;
	float limit = G.scene->toolsettings->skgen_symmetry_limit;
	float nor[3], vec[3], p[3];
	int i;
	
	for (i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		/* depth is store as a negative in flag. symmetry level is positive */
		if (connectedArc->flags == -depth)
		{
			if (arc1 == NULL)
			{
				arc1 = connectedArc;
				node1 = OTHER_NODE(arc1, node);
			}
			else
			{
				arc2 = connectedArc;
				node2 = OTHER_NODE(arc2, node);
				break; /* Can stop now, the two arcs have been found */
			}
		}
	}
	
	/* shouldn't happen, but just to be sure */
	if (node1 == NULL || node2 == NULL)
	{
		return;
	}
	
	VecSubf(p, node1->p, node->p);
	Crossf(vec, p, axis);
	Crossf(nor, vec, axis);
	
	/* mirror node2 along axis */
	VECCOPY(p, node2->p);
	mirrorAlongAxis(p, node->p, nor);
	
	/* check if it's within limit before continuing */
	if (VecLenf(node1->p, p) <= limit)
	{
	
		/* average with node1 */
		VecAddf(node1->p, node1->p, p);
		VecMulf(node1->p, 0.5f);
		
		/* mirror back on node2 */
		VECCOPY(node2->p, node1->p);
		mirrorAlongAxis(node2->p, node->p, nor);
		
		/* Merge buckets
		 * there shouldn't be any null arcs here, but just to be safe 
		 * */
		if (arc1->bcount > 0 && arc2->bcount > 0)
		{
			ReebArcIterator iter1, iter2;
			EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
			
			initArcIterator(&iter1, arc1, node);
			initArcIterator(&iter2, arc2, node);
			
			bucket1 = nextBucket(&iter1);
			bucket2 = nextBucket(&iter2);
		
			/* Make sure they both start at the same value */	
			while(bucket1 && bucket1->val < bucket2->val)
			{
				bucket1 = nextBucket(&iter1);
			}
			
			while(bucket2 && bucket2->val < bucket1->val)
			{
				bucket2 = nextBucket(&iter2);
			}
	
	
			for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
			{
				bucket1->nv += bucket2->nv; /* add counts */
				
				/* mirror on axis */
				mirrorAlongAxis(bucket2->p, node->p, nor);
				/* add bucket2 in bucket1 */
				VecLerpf(bucket1->p, bucket1->p, bucket2->p, (float)bucket2->nv / (float)(bucket1->nv));
	
				/* copy and mirror back to bucket2 */			
				bucket2->nv = bucket1->nv;
				VECCOPY(bucket2->p, bucket1->p);
				mirrorAlongAxis(bucket2->p, node->p, nor);
			}
		}
	}
}

void markdownSecondarySymmetry(ReebNode *node, int depth, int level)
{
	float axis[3] = {0, 0, 0};
	int count = 0;
	int i;

	/* Only reestablish spatial symmetry if needed */
	if (G.scene->toolsettings->skgen_options & SKGEN_SYMMETRY)
	{
		/* count the number of branches in this symmetry group
		 * and determinte the axis of symmetry
		 *  */	
		for (i = 0; node->arcs[i] != NULL; i++)
		{
			ReebArc *connectedArc = node->arcs[i];
			
			/* depth is store as a negative in flag. symmetry level is positive */
			if (connectedArc->flags == -depth)
			{
				count++;
			}
			/* If arc is on the axis */
			else if (connectedArc->flags == level)
			{
				VecAddf(axis, axis, connectedArc->v1->p);
				VecSubf(axis, axis, connectedArc->v2->p);
			}
		}
	
		Normalize(axis);
	
		/* Split between axial and radial symmetry */
		if (count == 2)
		{
			reestablishAxialSymmetry(node, depth, axis);
		}
		else
		{
			reestablishRadialSymmetry(node, depth, axis);
		}
	}

	/* markdown secondary symetries */	
	for (i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		if (connectedArc->flags == -depth)
		{
			/* markdown symmetry for branches corresponding to the depth */
			markdownSymmetryArc(connectedArc, node, level + 1);
		}
	}
}

void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level)
{
	int i;
	arc->flags = level;
	
	node = OTHER_NODE(arc, node);
	
	for (i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		if (connectedArc != arc)
		{
			ReebNode *connectedNode = OTHER_NODE(connectedArc, node);
			
			/* symmetry level is positive value, negative values is subtree depth */
			connectedArc->flags = -subtreeDepth(connectedNode, connectedArc);
		}
	}

	arc = NULL;

	for (i = 0; node->arcs[i] != NULL; i++)
	{
		int issymmetryAxis = 0;
		ReebArc *connectedArc = node->arcs[i];
		
		/* only arcs not already marked as symetric */
		if (connectedArc->flags < 0)
		{
			int j;
			
			/* true by default */
			issymmetryAxis = 1;
			
			for (j = 0; node->arcs[j] != NULL && issymmetryAxis == 1; j++)
			{
				ReebArc *otherArc = node->arcs[j];
				
				/* different arc, same depth */
				if (otherArc != connectedArc && otherArc->flags == connectedArc->flags)
				{
					/* not on the symmetry axis */
					issymmetryAxis = 0;
				} 
			}
		}
		
		/* arc could be on the symmetry axis */
		if (issymmetryAxis == 1)
		{
			/* no arc as been marked previously, keep this one */
			if (arc == NULL)
			{
				arc = connectedArc;
			}
			else
			{
				/* there can't be more than one symmetry arc */
				arc = NULL;
				break;
			}
		}
	}
	
	/* go down the arc continuing the symmetry axis */
	if (arc)
	{
		markdownSymmetryArc(arc, node, level);
	}

	
	/* secondary symmetry */
	for (i = 0; node->arcs[i] != NULL; i++)
	{
		ReebArc *connectedArc = node->arcs[i];
		
		/* only arcs not already marked as symetric and is not the next arc on the symmetry axis */
		if (connectedArc->flags < 0)
		{
			/* subtree depth is store as a negative value in the flag */
			markdownSecondarySymmetry(node, -connectedArc->flags, level);
		}
	}
}

void markdownSymmetry(ReebGraph *rg)
{
	ReebNode *node;
	ReebArc *arc;
	/* only for Acyclic graphs */
	int cyclic = isGraphCyclic(rg);
	
	/* mark down all arcs as non-symetric */
	for (arc = rg->arcs.first; arc; arc = arc->next)
	{
		arc->flags = 0;
	}
	
	/* mark down all nodes as not on the symmetry axis */
	for (node = rg->nodes.first; node; node = node->next)
	{
		node->flags = 0;
	}

	/* node list is sorted, so lowest node is always the head (by design) */
	node = rg->nodes.first;
	
	/* only work on acyclic graphs and if only one arc is incident on the first node */
	if (cyclic == 0 && countConnectedArcs(rg, node) == 1)
	{
		arc = node->arcs[0];
		
		markdownSymmetryArc(arc, node, 1);

		/* mark down non-symetric arcs */
		for (arc = rg->arcs.first; arc; arc = arc->next)
		{
			if (arc->flags < 0)
			{
				arc->flags = 0;
			}
			else
			{
				/* mark down nodes with the lowest level symmetry axis */
				if (arc->v1->flags == 0 || arc->v1->flags > arc->flags)
				{
					arc->v1->flags = arc->flags;
				}
				if (arc->v2->flags == 0 || arc->v2->flags > arc->flags)
				{
					arc->v2->flags = arc->flags;
				}
			}
		}
	}
}

/**************************************** SUBDIVISION ALGOS ******************************************/

EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	EditBone *lastBone = NULL;
	if (G.scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE)
	{
		ReebArcIterator iter;
		EmbedBucket *current = NULL;
		EmbedBucket *previous = NULL;
		EditBone *child = NULL;
		EditBone *parent = NULL;
		EditBone *root = NULL;
		float angleLimit = (float)cos(G.scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
		
		parent = add_editbone("Bone");
		parent->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
		VECCOPY(parent->head, head->p);
		
		root = parent;
		
		for (initArcIterator(&iter, arc, head), previous = nextBucket(&iter), current = nextBucket(&iter);
			current;
			previous = current, current = nextBucket(&iter))
		{
			float vec1[3], vec2[3];
			float len1, len2;

			VecSubf(vec1, previous->p, parent->head);
			VecSubf(vec2, current->p, previous->p);

			len1 = Normalize(vec1);
			len2 = Normalize(vec2);

			if (len1 > 0.0f && len2 > 0.0f && Inpf(vec1, vec2) < angleLimit)
			{
				VECCOPY(parent->tail, previous->p);

				child = add_editbone("Bone");
				VECCOPY(child->head, parent->tail);
				child->parent = parent;
				child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
				
				parent = child; /* new child is next parent */
			}
		}
		VECCOPY(parent->tail, tail->p);
		
		/* If the bone wasn't subdivided, delete it and return NULL
		 * to let subsequent subdivision methods do their thing. 
		 * */
		if (parent == root)
		{
			delete_bone(parent);
			parent = NULL;
		}
		
		lastBone = parent; /* set last bone in the chain */
	}
	
	return lastBone;
}

float calcCorrelation(ReebArc *arc, int start, int end, float v0[3], float n[3])
{
	int len = 2 + abs(end - start);
	
	if (len > 2)
	{
		ReebArcIterator iter;
		EmbedBucket *bucket = NULL;
		float avg_t = 0.0f;
		float s_t = 0.0f;
		float s_xyz = 0.0f;
		
		/* First pass, calculate average */
		for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter);
			bucket;
			bucket = nextBucket(&iter))
		{
			float v[3];
			
			VecSubf(v, bucket->p, v0);
			avg_t += Inpf(v, n);
		}
		
		avg_t /= Inpf(n, n);
		avg_t += 1.0f; /* adding start (0) and end (1) values */
		avg_t /= len;
		
		/* Second pass, calculate s_xyz and s_t */
		for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter);
			bucket;
			bucket = nextBucket(&iter))
		{
			float v[3], d[3];
			float dt;
			
			VecSubf(v, bucket->p, v0);
			Projf(d, v, n);
			VecSubf(v, v, d);
			
			dt = VecLength(d) - avg_t;
			
			s_t += dt * dt;
			s_xyz += Inpf(v, v);
		}
		
		/* adding start(0) and end(1) values to s_t */
		s_t += (avg_t * avg_t) + (1 - avg_t) * (1 - avg_t);
		
		return 1.0f - s_xyz / s_t; 
	}
	else
	{
		return 1.0f;
	}
}

EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	ReebArcIterator iter;
	float n[3];
	float CORRELATION_THRESHOLD = G.scene->toolsettings->skgen_correlation_limit;
	EditBone *lastBone = NULL;
	
	/* init iterator to get start and end from head */
	initArcIterator(&iter, arc, head);
	
	/* Calculate overall */
	VecSubf(n, arc->buckets[iter.end].p, head->p);
	
	if (G.scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION && 
		calcCorrelation(arc, iter.start, iter.end, head->p, n) < CORRELATION_THRESHOLD)
	{
		EmbedBucket *bucket = NULL;
		EmbedBucket *previous = NULL;
		EditBone *child = NULL;
		EditBone *parent = NULL;
		int boneStart = iter.start;

		parent = add_editbone("Bone");
		parent->flag = BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
		VECCOPY(parent->head, head->p);
		
		for (previous = nextBucket(&iter), bucket = nextBucket(&iter);
			bucket;
			previous = bucket, bucket = nextBucket(&iter))
		{
			/* Calculate normal */
			VecSubf(n, bucket->p, parent->head);

			if (calcCorrelation(arc, boneStart, iter.index, parent->head, n) < CORRELATION_THRESHOLD)
			{
				VECCOPY(parent->tail, previous->p);

				child = add_editbone("Bone");
				VECCOPY(child->head, parent->tail);
				child->parent = parent;
				child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
				
				parent = child; // new child is next parent
				boneStart = iter.index; // start from end
			}
		}

		VECCOPY(parent->tail, tail->p);
		
		lastBone = parent; /* set last bone in the chain */
	}
	
	return lastBone;
}

float arcLengthRatio(ReebArc *arc)
{
	float arcLength = 0.0f;
	float embedLength = 0.0f;
	int i;
	
	arcLength = VecLenf(arc->v1->p, arc->v2->p);
	
	if (arc->bcount > 0)
	{
		/* Add the embedding */
		for ( i = 1; i < arc->bcount; i++)
		{
			embedLength += VecLenf(arc->buckets[i - 1].p, arc->buckets[i].p);
		}
		/* Add head and tail -> embedding vectors */
		embedLength += VecLenf(arc->v1->p, arc->buckets[0].p);
		embedLength += VecLenf(arc->v2->p, arc->buckets[arc->bcount - 1].p);
	}
	else
	{
		embedLength = arcLength;
	}
	
	return embedLength / arcLength;	
}

EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
{
	EditBone *lastBone = NULL;
	if ((G.scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
		arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio)
	{
		ReebArcIterator iter;
		EmbedBucket *bucket = NULL;
		EmbedBucket *previous = NULL;
		EditBone *child = NULL;
		EditBone *parent = NULL;
		float lengthLimit = G.scene->toolsettings->skgen_length_limit;
		int same = 0;
		
		parent = add_editbone("Bone");
		parent->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
		VECCOPY(parent->head, head->p);

		initArcIterator(&iter, arc, head);

		bucket = nextBucket(&iter);
		
		while (bucket != NULL)
		{
			float *vec0 = NULL;
			float *vec1 = bucket->p;

			/* first bucket. Previous is head */
			if (previous == NULL)
			{
				vec0 = head->p;
			}
			/* Previous is a valid bucket */
			else
			{
				vec0 = previous->p;
			}
			
			/* If lengthLimit hits the current segment */
			if (VecLenf(vec1, parent->head) > lengthLimit)
			{
				if (same == 0)
				{
					float dv[3], off[3];
					float a, b, c, f;
					
					/* Solve quadratic distance equation */
					VecSubf(dv, vec1, vec0);
					a = Inpf(dv, dv);
					
					VecSubf(off, vec0, parent->head);
					b = 2 * Inpf(dv, off);
					
					c = Inpf(off, off) - (lengthLimit * lengthLimit);
					
					f = (-b + (float)sqrt(b * b - 4 * a * c)) / (2 * a);
					
					//printf("a %f, b %f, c %f, f %f\n", a, b, c, f);
					
					if (isnan(f) == 0 && f < 1.0f)
					{
						VECCOPY(parent->tail, dv);
						VecMulf(parent->tail, f);
						VecAddf(parent->tail, parent->tail, vec0);
					}
					else
					{
						VECCOPY(parent->tail, vec1);
					}
				}
				else
				{
					float dv[3];
					
					VecSubf(dv, vec1, vec0);
					Normalize(dv);
					 
					VECCOPY(parent->tail, dv);
					VecMulf(parent->tail, lengthLimit);
					VecAddf(parent->tail, parent->tail, parent->head);
				}
				
				child = add_editbone("Bone");
				VECCOPY(child->head, parent->tail);
				child->parent = parent;
				child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
				
				parent = child; // new child is next parent
				
				same = 1; // mark as same
			}
			else
			{
				previous = bucket;
				bucket = nextBucket(&iter);
				same = 0; // Reset same
			}
		}
		VECCOPY(parent->tail, tail->p);
		
		lastBone = parent; /* set last bone in the chain */
	}
	
	return lastBone;
}

/***************************************** MAIN ALGORITHM ********************************************/

void generateSkeletonFromReebGraph(ReebGraph *rg)
{
	GHash *arcBoneMap = NULL;
	ReebArc *arc = NULL;
	ReebNode *node = NULL;
	Object *src = NULL;
	Object *dst = NULL;
	
	src = BASACT->object;
	
	if (G.obedit != NULL)
	{
		exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR); // freedata, and undo
	}

	setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
	
	dst = add_object(OB_ARMATURE);
	base_init_from_view3d(BASACT, G.vd);
	G.obedit= BASACT->object;
	
	/* Copy orientation from source */
	VECCOPY(dst->loc, src->obmat[3]);
	Mat4ToEul(src->obmat, dst->rot);
	Mat4ToSize(src->obmat, dst->size);
	
	where_is_object(G.obedit);
	
	make_editArmature();

	arcBoneMap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
	
	markdownSymmetry(rg);
	
	for (arc = rg->arcs.first; arc; arc = arc->next) 
	{
		EditBone *lastBone = NULL;
		ReebNode *head, *tail;
		int i;

		/* Find out the direction of the arc through simple heuristics (in order of priority) :
		 * 
		 * 1- Arcs on primary symmetry axis (flags == 1) point up (head: high weight -> tail: low weight)
		 * 2- Arcs starting on a primary axis point away from it (head: node on primary axis)
		 * 3- Arcs point down (head: low weight -> tail: high weight)
		 *
		 * Finally, the arc direction is stored in its flags: 1 (low -> high), -1 (high -> low)
		 */

		/* if arc is a symmetry axis, internal bones go up the tree */		
		if (arc->flags == 1 && arc->v2->degree != 1)
		{
			head = arc->v2;
			tail = arc->v1;
			
			arc->flags = -1; /* mark arc direction */
		}
		/* Bones point AWAY from the symmetry axis */
		else if (arc->v1->flags == 1)
		{
			head = arc->v1;
			tail = arc->v2;
			
			arc->flags = 1; /* mark arc direction */
		}
		else if (arc->v2->flags == 1)
		{
			head = arc->v2;
			tail = arc->v1;
			
			arc->flags = -1; /* mark arc direction */
		}
		/* otherwise, always go from low weight to high weight */
		else
		{
			head = arc->v1;
			tail = arc->v2;
			
			arc->flags = 1; /* mark arc direction */
		}
		
		/* Loop over subdivision methods */	
		for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++)
		{
			switch(G.scene->toolsettings->skgen_subdivisions[i])
			{
				case SKGEN_SUB_LENGTH:
					lastBone = subdivideByLength(arc, head, tail);
					break;
				case SKGEN_SUB_ANGLE:
					lastBone = subdivideByAngle(arc, head, tail);
					break;
				case SKGEN_SUB_CORRELATION:
					lastBone = subdivideByCorrelation(arc, head, tail);
					break;
			}
		}
	
		if (lastBone == NULL)
		{
			EditBone	*bone;
			bone = add_editbone("Bone");
			bone->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
			
			VECCOPY(bone->head, head->p);
			VECCOPY(bone->tail, tail->p);
			
			/* set first and last bone, since there's only one */
			lastBone = bone;
		}
		
		BLI_ghash_insert(arcBoneMap, arc, lastBone);
	}

	/* Second pass, setup parent relationship between arcs */
	for (node = rg->nodes.first; node; node = node->next)
	{
		ReebArc *incomingArc = NULL;
		int i;

		for (i = 0; node->arcs[i] != NULL; i++)
		{
			arc = node->arcs[i];

			/* if arc is incoming into the node */
			if ((arc->v1 == node && arc->flags == -1) || (arc->v2 == node && arc->flags == 1))
			{
				if (incomingArc == NULL)
				{
					incomingArc = arc;
					/* loop further to make sure there's only one incoming arc */
				}
				else
				{
					/* skip this node if more than one incomingArc */
					incomingArc = NULL;
					break; /* No need to look further, we are skipping already */
				}
			}
		}

		if (incomingArc != NULL)
		{
			EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc);

			/* Look for outgoing arcs and parent their bones */
			for (i = 0; node->arcs[i] != NULL; i++)
			{
				arc = node->arcs[i];

				/* if arc is outgoing from the node */
				if ((arc->v1 == node && arc->flags == 1) || (arc->v2 == node && arc->flags == -1))
				{
					EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc);

					/* find the root bone */
					while(childBone->parent != NULL)
					{
						childBone = childBone->parent;
					}

					childBone->parent = parentBone;
					childBone->flag |= BONE_CONNECTED;
				}
			}
		}
	}
	
	BLI_ghash_free(arcBoneMap, NULL, NULL);

	setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
	
	BIF_undo_push("Generate Skeleton");
}

void generateSkeleton(void)
{
	EditMesh *em = G.editMesh;
	ReebGraph *rg = NULL;
	int i;
	
	if (em == NULL)
		return;

	setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);

	if (weightFromDistance(em) == 0)
	{
		error("No selected vertex\n");
		return;
	}
		
	renormalizeWeight(em, 1.0f);
	
	weightToHarmonic(em);
	
#ifdef DEBUG_REEB
	weightToVCol(em);
#endif
	
	rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution);

	verifyBuckets(rg);
	
	/* Remove arcs without embedding */
	filterNullReebGraph(rg);

	verifyBuckets(rg);


	i = 1;
	/* filter until there's nothing more to do */
	while (i == 1)
	{
		i = 0; /* no work done yet */
		
		if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_EXTERNAL)
		{
			i |= filterExternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_external * G.scene->toolsettings->skgen_resolution);
		}
	
		verifyBuckets(rg);
	
		if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_INTERNAL)
		{
			i |= filterInternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_internal * G.scene->toolsettings->skgen_resolution);
		}
	}

	verifyBuckets(rg);

	repositionNodes(rg);
	
	verifyBuckets(rg);

	/* Filtering might have created degree 2 nodes, so remove them */
	removeNormalNodes(rg);
	
	verifyBuckets(rg);

	for(i = 0; i <  G.scene->toolsettings->skgen_postpro_passes; i++)
	{
		postprocessGraph(rg, G.scene->toolsettings->skgen_postpro);
	}

	buildAdjacencyList(rg);
	
	sortNodes(rg);
	
	sortArcs(rg);
	
	generateSkeletonFromReebGraph(rg);

	freeGraph(rg);
}
