/* :ts=8 */
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "general.h"
#include "globals.h"
#include "intui.h"
#include "spl_gfx.h"
#include "spl_math.h"
#include "spl_util.h"

void Splines_Init()
/************************************************************************/
/*                                                                      */
/* Initialize the tables containing splines, knots and points.		*/
/*                                                                      */
/************************************************************************/
{
    Spline_T 	*Spline;
    short	Point_Id;

    while (Splines != NULL) Spline_Free(Splines);

    for (Point_Id = 0; Point_Id < Max_Nbr_Points; Point_Id++)
    				Points[Point_Id].Reference_Count = 0;

    Select_Knot		= NULL;
    Select_Spline	= NULL;
    Select_Point_Id	= -1;

    Group_Mode		= FALSE;
    Points_Hidden	= FALSE;

} /* Splines_Init */

static
Boolean_T Is_Segment_Selected(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Return TRUE if the segment defined by Spline and Knot is selected.	*/
/*                                                                      */
/* A segment is selected if BOTH endpoints are selected.		*/
/*                                                                      */
/************************************************************************/
{
    Knot_T 	*Next_Knot;
    Boolean_T	Result;

    if (Spline == NULL || Knot == NULL) return(FALSE);

    Next_Knot = Knot->Next;
    if (Next_Knot == NULL) return(FALSE);

    Result = 
	Is_VSelected(Points[Knot->Point_Id]) &&
	Is_VSelected(Points[Next_Knot->Point_Id]);

    return(Result);
	
} /* Is_Segment_Selected */

void Set_Origin(Vector_T New_Origin)
/************************************************************************/
/*                                                                      */
/* Move origin.								*/
/*                                                                      */
/************************************************************************/
{
    short	i;
    extern Vector_T 	Offset;

    if (Grid_Snap_Active) Snap_To_Grid(New_Origin);

    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count != 0) {

	    Vec2Op(Points[i].Pos, +=, Origin);
	    Vec2Op(Points[i].Pos, -=, New_Origin);

   	} /* if */

    } /* for */

    Vec2Op(Offset, +=, Origin);
    Vec2Op(Offset, -=, New_Origin);

} /* Set_Origin */

Boolean_T Get_Bounding_Box(Vector_T Min_Pos, Vector_T Max_Pos)
/************************************************************************/
/*                                                                      */
/* Find bounding box which contains all visible points.			*/
/* Return TRUE if no points were found.					*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    Vec3Scalar(Min_Pos, = , 1e9, 1e9, 1e9);
    Vec3Scalar(Max_Pos, = , -1e9, -1e9, -1e9);

    for (i = 0; i < Max_Nbr_Points; i++) 
      if (Points[i].Reference_Count > 0 && !Is_Hidden(Points[i])) {
	if (Min_Pos[0] > Points[i].Pos[0]) Min_Pos[0] = Points[i].Pos[0];
	if (Min_Pos[1] > Points[i].Pos[1]) Min_Pos[1] = Points[i].Pos[1];
	if (Min_Pos[2] > Points[i].Pos[2]) Min_Pos[2] = Points[i].Pos[2];
	if (Max_Pos[0] < Points[i].Pos[0]) Max_Pos[0] = Points[i].Pos[0];
	if (Max_Pos[1] < Points[i].Pos[1]) Max_Pos[1] = Points[i].Pos[1];
	if (Max_Pos[2] < Points[i].Pos[2]) Max_Pos[2] = Points[i].Pos[2];
    } /* if */

    if (Min_Pos[0] > Max_Pos[0]) return(TRUE);

    return(FALSE);

} /* Get_Bounding_Box */

Boolean_T Get_Select_Bounding_Box(Vector_T Min_Pos, Vector_T Max_Pos)
/************************************************************************/
/*                                                                      */
/* Find bounding box which contains all selected points.		*/
/* Return TRUE if no points were found.					*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    Vec3Scalar(Min_Pos, = , 1e9, 1e9, 1e9);
    Vec3Scalar(Max_Pos, = , -1e9, -1e9, -1e9);

    for (i = 0; i < Max_Nbr_Points; i++) 
      if (Points[i].Reference_Count > 0 && Is_Selected(Points[i])) {
	if (Min_Pos[0] > Points[i].Pos[0]) Min_Pos[0] = Points[i].Pos[0];
	if (Min_Pos[1] > Points[i].Pos[1]) Min_Pos[1] = Points[i].Pos[1];
	if (Min_Pos[2] > Points[i].Pos[2]) Min_Pos[2] = Points[i].Pos[2];
	if (Max_Pos[0] < Points[i].Pos[0]) Max_Pos[0] = Points[i].Pos[0];
	if (Max_Pos[1] < Points[i].Pos[1]) Max_Pos[1] = Points[i].Pos[1];
	if (Max_Pos[2] < Points[i].Pos[2]) Max_Pos[2] = Points[i].Pos[2];
    } /* if */

    if (Min_Pos[0] > Max_Pos[0]) return(TRUE);

    return(FALSE);

} /* Get_Select_Bounding_Box */

void Points_Save()
/************************************************************************/
/*                                                                      */
/* Save all points to Saved_Points table.				*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    for (i = 0; i < Max_Nbr_Points; i++) Saved_Points[i] = Points[i];

} /* Points_Save */

void Points_Restore()
/************************************************************************/
/*                                                                      */
/* Restore all points from Saved_Points table.				*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    for (i = 0; i < Max_Nbr_Points; i++) Points[i] = Saved_Points[i];

} /* Points_Restore */

void Points_Move(Vector_T Distance)
/************************************************************************/
/*                                                                      */
/* Move all selected points the distance 'Distance'.			*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && Is_Selected(Points[i])) {

	    Vec3Op(Points[i].Pos, =, Saved_Points[i].Pos, +, Distance);

   	} /* if */

    } /* for */

} /* Points_Move */

void Points_Scale(double Scale_Factor, 
	Boolean_T Do_Scale_X, Boolean_T Do_Scale_Y, Boolean_T Do_Scale_Z)
/************************************************************************/
/*                                                                      */
/* Scale all selected points.						*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && Is_Selected(Points[i])) {

    	    if (Do_Scale_X) Points[i].Pos[0] = Origin[0] + 
				Scale_Factor * (Saved_Points[i].Pos[0] - Origin[0]);
    	    if (Do_Scale_Y) Points[i].Pos[1] = Origin[1] + 
				Scale_Factor * (Saved_Points[i].Pos[1] - Origin[1]);
    	    if (Do_Scale_Z) Points[i].Pos[2] = Origin[2] + 
				Scale_Factor * (Saved_Points[i].Pos[2] - Origin[2]);

   	} /* if */

    } /* for */

} /* Points_Scale */

void Points_Rotate(double Angle, short Axis)
/************************************************************************/
/*                                                                      */
/* Rotate all selected points 'Angle' degrees about 'Axis'.		*/
/*                                                                      */
/************************************************************************/
{
    short	i;
    Vector_T	R_Vector, P, RP;
    Matrix_T	R_Matrix;

    if (Axis < 0 || Axis > 2) return;

    R_Vector[0] = R_Vector[1] = R_Vector[2] = 0.0;
    R_Vector[Axis] = Angle;

    Compute_Rotation_Matrix(R_Matrix, R_Vector);

    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && Is_Selected(Points[i])) {

	    Vec3Op(P, =, Saved_Points[i].Pos, -, Origin);

	    Matrix_MultV(RP, R_Matrix, P);

	    Vec3Op(Points[i].Pos, =, RP, +, Origin);

   	} /* if */

    } /* for */

} /* Points_Scale */

void Points_Set_Tension(double Tension)
/************************************************************************/
/*                                                                      */
/* Set tension for all selected points.					*/
/*                                                                      */
/************************************************************************/
{
    Spline_T 	*Spline;
    Knot_T	*Knot;
    int		i;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
					i++, Knot = Knot->Next) {
	
	    if (Is_Segment_Selected(Spline, Knot))
    		Knot->Tension = Tension;

   	} /* for */

    } /* for */

} /* Points_Set_Tension */

void Points_Set_Bias(double Bias)
/************************************************************************/
/*                                                                      */
/* Set bias for all selected points.					*/
/*                                                                      */
/************************************************************************/
{
    Spline_T 	*Spline;
    Knot_T	*Knot;
    int		i;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
					i++, Knot = Knot->Next) {
	
	    if (Is_Segment_Selected(Spline, Knot))
    		Knot->Bias = Bias;

   	} /* for */

    } /* for */

} /* Points_Set_Bias */

void Points_Set_Continuity(double Continuity)
/************************************************************************/
/*                                                                      */
/* Set continuity for all selected points.				*/
/*                                                                      */
/************************************************************************/
{
    Spline_T 	*Spline;
    Knot_T	*Knot;
    int		i;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
					i++, Knot = Knot->Next) {
	
	    if (Is_Segment_Selected(Spline, Knot))
    		Knot->Continuity = Continuity;

   	} /* for */

    } /* for */

} /* Points_Set_Continuity */

short Point_New(Vector_T Pos)
/************************************************************************/
/*                                                                      */
/* Allocate first free point in the point list, set its position to Pos.*/
/* Return id of the point.						*/
/*                                                                      */
/************************************************************************/
{
    short	i;

    for (i = 0; i < Max_Nbr_Points && Points[i].Reference_Count != 0; i++) ;

    if (i >= Max_Nbr_Points) return(-1);

    Points[i].Reference_Count 	= 1;
    Points[i].Flags           	= 0;
    /* DON'T Initialize Points[i].Work it might be in use already! */

    Vec2Op(Points[i].Pos, =, Pos);

    return(i);

} /* Point_New */


void Point_Move(short Point_Id, Vector_T Pos)
/************************************************************************/
/*                                                                      */
/* Move point Point_Id to Pos.						*/
/*                                                                      */
/************************************************************************/
{
    if (!Is_Point_Id_Ok(Point_Id)) return;

    Vec2Op(Points[Point_Id].Pos, =, Pos);

} /* Point_Move */

short Point_Find(Vector_T Pos, short View_Id)
/************************************************************************/
/*                                                                      */
/* Search for a point with  coordinates near Pos, in the view View_Id.	*/
/* Return its id, or -1 if not found.					*/
/*                                                                      */
/************************************************************************/
{
    int 	i;
    short	Found_Id;
    double 	D, Dist;

    Dist = 999999.0;
    switch (View_Id) {		

    case V_X: 					/* Y - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i])) {
              D = MAX(ABS(Pos[1] - Points[i].Pos[1]), ABS(Pos[2] - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Found_Id = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;    
    
    case V_Y: 					/* X - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i])) {
              D = MAX(ABS(Pos[0] - Points[i].Pos[0]), ABS(Pos[2] - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Found_Id = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;

    case V_Z: 					/* X - Y */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i])) {
              D = MAX(ABS(Pos[0] - Points[i].Pos[0]), ABS(Pos[1] - Points[i].Pos[1]));
	        if (D < Dist) {
	            Dist = D;
	            Found_Id = i;
	        } /* if */
	    } /* if */
        } /* for */
	break;
    
    case V_P:					/* Persp */
	/* Not implemented yet */
	break;

    } /* switch */

    if (Dist > Max_Dist) return(-1);

    return(Found_Id);

}  /* Point_Find */

void Knot_Insert(Spline_T *Spline, Knot_T *Knot, Knot_T *New_Knot)
/************************************************************************/
/*                                                                      */
/* Insert New_Knot after the knot Knot to the the spline Spline.	*/
/* If Knot is NULL, then insert it first.				*/
/*                                                                      */
/************************************************************************/
{

    if (Knot == NULL) {		/* Insert first	*/
	
        New_Knot->Next 	 = Spline->First;
        New_Knot->Previous = NULL;

	if (New_Knot->Next != NULL) New_Knot->Next->Previous = New_Knot;
        else                        Spline->Last             = New_Knot;

	Spline->First	 = New_Knot;

     } else {
	
	New_Knot->Next = Knot->Next;
	New_Knot->Previous = Knot;

	if (New_Knot->Next != NULL) New_Knot->Next->Previous = New_Knot;
	else                        Spline->Last             = New_Knot;

	Knot->Next = New_Knot;


    } /* if .. else .. */

		/* Check if Knot was last 	*/
    if (Spline->Last == Knot) Spline->Last = New_Knot;

    if (Spline->Loop) {

	Spline->First->Previous = Spline->Last;
	Spline->Last->Next      = Spline->First;

    } else {

	Spline->First->Previous = NULL;
	Spline->Last->Next      = NULL;

    } /* if .. else .. */

    Spline->Nbr_Knots++;


} /* Knot_Insert */

void Knot_Remove(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Remove the knot Knot from the spline Spline.				*/
/*                                                                      */
/************************************************************************/
{
    if (Spline == NULL || Knot == NULL) return;

    if (Spline->First == Spline->Last) {

	Spline->First = Spline->Last = NULL;

    } else {

        if (Spline->First == Knot) Spline->First = Knot->Next;
        if (Spline->Last == Knot) Spline->Last = Knot->Previous;

    } /* if .. else .. */

    if (Knot->Previous != NULL) Knot->Previous->Next = Knot->Next; 
    if (Knot->Next     != NULL) Knot->Next->Previous = Knot->Previous; 

    Spline->Nbr_Knots--;

    if (Spline->Nbr_Knots < 3) Spline_Loop(Spline, FALSE);

} /* Knot_Remove */

Knot_T *Knot_New(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Add a new knot after the knot Knot to the the spline Spline.		*/
/*                                                                      */
/* Return ptr to new knot, or NULL if it couldn't be created.		*/
/*                                                                      */
/************************************************************************/
{
    Knot_T	*New_Knot;

    New_Knot = (Knot_T *) malloc(sizeof(Knot_T));
    if (New_Knot == NULL) return(NULL);

    New_Knot->Point_Id 	 = -1;
    New_Knot->Flags 	 = 0;
    New_Knot->Tension 	 = 0.0;
    New_Knot->Bias 	 = 0.0;
    New_Knot->Continuity = 0.0;

    Knot_Insert(Spline, Knot, New_Knot);

    return(New_Knot);

} /* Knot_New */

void Knot_Free(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Free the knot Knot from the spline Spline.				*/
/*                                                                      */
/************************************************************************/
{
    if (Spline == NULL || Knot == NULL) return;

    Knot_Remove(Spline, Knot);

    if (Knot->Point_Id >= 0) Points[Knot->Point_Id].Reference_Count--;
    free(Knot);

} /* Knot_Free */

Knot_T *Knot_Add(Spline_T *Spline, Knot_T *Knot, Vector_T Point_Pos)
/************************************************************************/
/*                                                                      */
/* Add a new knot after the knot 'Knot' on spline 'Spline'.		*/
/* Place it at 'Point_Pos'.						*/
/*                                                                      */
/* Select it, and return ptr to new knot.				*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    short	New_Point_Id;
    Knot_T	*New_Knot;
    short	Point_Id, Next_Point_Id;
    Vector_T	V1, V2;
    double	Dot_Product;
    Boolean_T	Last_Knot;


    if (Spline == NULL || Knot == NULL) return(NULL);

    /* Now try to decide wheter the new knot should be placed before or	*/
    /* after 'Knot'.							*/

    Point_Id = Knot->Point_Id;

    Last_Knot = FALSE;

    if (Spline->Last != Knot) {

	Next_Point_Id = Knot->Next->Point_Id;

    } else {

	/* This is the last knot	*/
	if (!Spline->Loop) {
	
 	    /* Special case: 						*/
	    /* If last knot and not looping, then use previous point	*/
	    /* and invert the test.					*/

	    Last_Knot = TRUE;
	    Next_Point_Id = Knot->Previous->Point_Id;

        } else  Next_Point_Id = Spline->First->Point_Id;

    } /* if .. else .. */

	/* Vector from current point to next:			 	*/
    Vec3Op(V1, =, Points[Next_Point_Id].Pos, -, Points[Point_Id].Pos);

	/* Vector from current point to new point:			*/
    Vec3Op(V2, =, Point_Pos, -, Points[Point_Id].Pos);

    Dot_Product = VecDot(V1, V2);

    if (Last_Knot) Dot_Product *= -1.0;

	/* If the two vectors are pointing in the same direction, ie. 	*/
	/* the dot product > 0, then place new knot after current knot, */
	/* otherwise before						*/

    if (Dot_Product < 0.0) Knot = Knot->Previous;	/* Before	*/

    New_Point_Id = Point_New(Point_Pos);
    if (New_Point_Id < 0) return(NULL);

    New_Knot = Knot_New(Spline, Knot);
    if (New_Knot == NULL) {

	Points[New_Point_Id].Reference_Count--;
	return(NULL);

    } /* if */

    New_Knot->Point_Id 	= New_Point_Id;

    Compute_Spline(Spline);

    Select_Point(New_Point_Id);

    return(New_Knot);

} /* Knot_Add */

void Knot_Delete(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Delete knot with id Knot_Id from the spline 'Spline'.		*/
/* If there's no more knots in the spline, then delete the spline.	*/
/*                                                                      */
/************************************************************************/
{
    if (Spline == NULL || Knot == NULL) return;

    Knot_Free(Spline, Knot);

    if (Spline->Nbr_Knots <= 0) Spline_Free(Spline);

} /* Knot_Delete */

Spline_T *Spline_New()
/************************************************************************/
/*                                                                      */
/* Add a new empty spline.						*/
/*                                                                      */
/* Return Id of new spline, or NULL if it couldn't be created.		*/
/************************************************************************/
{
    Spline_T 	*Spline;

    Spline = (Spline_T *) malloc(sizeof(Spline_T));
    if (Spline == NULL) return(NULL);

    Spline->Nbr_Knots	= 0;
    Spline->First	= NULL;
    Spline->Last	= NULL;
    Spline->Loop	= FALSE;
    Spline->Flags	= 0;

    Spline->Next = Splines;
    Spline->Previous = NULL;

    if (Spline->Next != NULL) Spline->Next->Previous = Spline;
    Splines = Spline;

    return(Spline);

} /* Spline_New */

void Spline_Free(Spline_T *Spline)
/************************************************************************/
/*                                                                      */
/* Delete the spline Spline, DON'T change point reference counts.	*/	
/*                                                                      */
/************************************************************************/
{
    Knot_T	*Knot, *Next_Knot;
    int		i;

    if (Spline == NULL) return;

    if (Spline->Next != NULL) Spline->Next->Previous = Spline->Previous;

    if (Spline->Previous != NULL) Spline->Previous->Next = Spline->Next;
    else                          Splines = Splines->Next;

	/* Free all knots in this spline.	*/
    for (i = 0, Knot = Spline->First;
    	 i < Spline->Nbr_Knots && Knot != NULL; i++) {

	Next_Knot = Knot->Next;
        if (Knot->Point_Id >= 0) Points[Knot->Point_Id].Reference_Count--;
	free(Knot);
	Knot = Next_Knot;

    } /* while */

    free(Spline);

} /* Spline_Free */

Spline_T *Spline_Add(Vector_T Pos)
/************************************************************************/
/*                                                                      */
/* Add a new spline (with 2 knots) at position Pos. Select one endpoint */
/* Return pointer to new spline (or NULL).				*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    Spline_T	*Spline;
    Knot_T	*Knot1, *Knot2;
    short	Point_Id1, Point_Id2;

    Spline = Spline_New();
    if (Spline == NULL) return(NULL);

    Point_Id1 = Point_New(Pos);
    if (Point_Id1 < 0) return(NULL);

    Point_Id2 = Point_New(Pos);
    if (Point_Id2 < 0) {
	Points[Point_Id1].Reference_Count--;
	return(NULL);
    }

    Knot1 = Knot_New(Spline, NULL);
    if (Knot1 == NULL) {
	Points[Point_Id1].Reference_Count--;
	Points[Point_Id2].Reference_Count--;
	Spline_Free(Spline);
	return(NULL);
    }
    Knot2 = Knot_New(Spline, Knot1);
    if (Knot2 == NULL) {
	Points[Point_Id1].Reference_Count--;
	Points[Point_Id2].Reference_Count--;
	Spline_Free(Spline);
	return(NULL);
    }
    Knot1->Point_Id 	= Point_Id1;
    Knot2->Point_Id 	= Point_Id2;

    Compute_Spline(Spline);

    Select_Point(Point_Id2);

    return(Spline);

} /* Spline_Add */

void Spline_Loop(Spline_T *Spline, Boolean_T Loop)
/************************************************************************/
/*                                                                      */
/* 'Loop' or 'Unloop' a spline.						*/
/*                                                                      */
/************************************************************************/
{
    Spline->Loop = Loop;

    if (Spline->Last == NULL || Spline->First == NULL) return;

    if (Loop) {
	
	Spline->First->Previous = Spline->Last;
	Spline->Last->Next      = Spline->First;

    } else {

	Spline->First->Previous = NULL;
	Spline->Last->Next      = NULL;

    } /* if .. else .. */

} /* Spline_Loop */

Spline_T *Spline_Cut(Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Cut the spline 'Spline' at the knot 'Knot'.				*/
/*                                                                      */
/* If the spline is looping, then the result will be one non looping 	*/
/* spline. If the spline is non-looping, the result will be two splines.*/
/*                                                                      */
/* The knot 'Knot' will be deleted if the action is successfull.	*/
/*                                                                      */
/* In case of error, return NULL, otherwise return ptr to newly created	*/
/* spline, or if no new spline was created, ptr to original spline.	*/
/*                                                                      */
/************************************************************************/
{
    int		i,j;
    int		Nbr_Knots;
    short	Point_Id;
    Spline_T	*Return_Value;
    Spline_T	*New_Spline;
    Knot_T	*Knot1, *Knot2;

    Return_Value = NULL;

    if (Spline == NULL || Knot == NULL) return(Return_Value);

    Nbr_Knots = Spline->Nbr_Knots;
    Point_Id  = Knot->Point_Id;
    
    if (Spline->Loop) {
	if (Nbr_Knots < 3) return(Return_Value);
	
	Knot1 = Knot->Previous;
        Knot2 = Knot->Next;

	Knot_Free(Spline, Knot);

	Spline->Last = Knot1;
	Spline->First = Knot2;

	Spline_Loop(Spline, FALSE);

        Return_Value = Spline;

    } else {

	/* It is only possible to cut at an inner knot!			*/
	if (Nbr_Knots < 5 || 
	    Knot->Previous == NULL || Knot->Previous->Previous == NULL ||
	    Knot->Next     == NULL || Knot->Next->Next         == NULL)
			     				return(Return_Value);

        New_Spline = Spline_New();
        if (New_Spline == NULL) return(NULL);
	
	Knot1 = NULL;		/* Insert point	*/
	Knot2 = Knot->Next;	/* Knot to remove/insert */
	while (Knot2 != NULL) {

	    Knot_Remove(Spline, Knot2);
	    Knot_Insert(New_Spline, Knot1, Knot2);

	    Knot1 = Knot2; /* New insert point */
	    Knot2 = Knot->Next;	/* Knot to remove/insert */

        } /* while */

	Knot_Free(Spline, Knot);

   	Return_Value = New_Spline;

    } /* if .. else .. */
 
    return(Return_Value);

} /* Spline_Cut */

void Spline_Reverse_Knots(Spline_T *Spline)
/************************************************************************/
/*                                                                      */
/* Reverse the knots for Spline.					*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    Knot_T	*Knot, *Next_Knot, *Tmp_Knot;

    if (Spline == NULL) return;

    if (Spline->Nbr_Knots < 2) return;

    i = 0; 
    Knot = Spline->First;
    while (i < Spline->Nbr_Knots) {

	Next_Knot      = Knot->Next;

	/* switch Next & Previous */
	Tmp_Knot       = Knot->Next;
	Knot->Next     = Knot->Previous;
	Knot->Previous = Tmp_Knot;

	Knot = Next_Knot;
	i++;

    } /* while */

	/* Switch First & Last */
    Tmp_Knot      = Spline->Last;
    Spline->Last  = Spline->First;
    Spline->First = Tmp_Knot;

} /* Spline_Reverse_Knots */

Boolean_T Splines_Connect(short View_Id, Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Try to connect the given spline/knot with another knot near it.	*/
/* Return TRUE if connection is made.					*/
/*                                                                      */
/************************************************************************/
{
    int 	i;
    short	Point_Id1;
    short	Point_Id2;
    double 	D, Dist;
    double	X, Y, Z;

    if (Spline == NULL || Knot == NULL) return(FALSE);

    Point_Id1 = Knot->Point_Id;

    X = Points[Point_Id1].Pos[0];
    Y = Points[Point_Id1].Pos[1];
    Z = Points[Point_Id1].Pos[2];

    Dist = 999999.0;

    switch (View_Id) {		
    
    case V_X: 					/* Y - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(Y - Points[i].Pos[1]), ABS(Z - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;

    
    case V_Y: 					/* X - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(X - Points[i].Pos[0]), ABS(Z - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;
    
    case V_Z: 					/* X - Y */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(X - Points[i].Pos[0]), ABS(Y - Points[i].Pos[1]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */
        } /* for */
	break;

    case V_P:				/* Persp */
	break;

    } /* switch */

    if (Dist > Max_Dist) return(FALSE);

	/* We've found a point (Point_Id2) near the Point_Id1 */

	/* Now handle case where Point_Id1 is on start of spline and	*/
	/* Point_Id2 is on end of the same spline (or vice versa). 	*/
	/* In this case we will remove the last knot loop the spline 	*/
	/* instead.							*/

    if (!Spline->Loop) {

        if (Spline->First == Knot && 
	    Spline->Last->Point_Id == Point_Id2) {
	
            if (Spline->Nbr_Knots < 3) return(FALSE);

	    Knot_Free(Spline, Spline->Last);
	    Spline_Loop(Spline, TRUE);
    	    Deselect_All();
    	    Select_Point(Point_Id1);
	    return(TRUE);

	}  else if (Spline->Last == Knot && 
                    Spline->First->Point_Id == Point_Id2) {

            if (Spline->Nbr_Knots < 3) return(FALSE);
	    Knot_Free(Spline, Spline->Last);
	    Spline_Loop(Spline, TRUE);
    	    Deselect_All();
    	    Select_Point(Point_Id2);
	    return(TRUE);

	} /* if */

    } /* if */

    Points[Point_Id1].Reference_Count--;
    Knot->Point_Id = Point_Id2;
    Points[Point_Id2].Reference_Count++;

    Deselect_All();
    Select_Point(Point_Id2);

    return(TRUE);

} /* Splines_Connect */

Boolean_T Splines_Combine(short View_Id, Spline_T *Spline, Knot_T *Knot)
/************************************************************************/
/*                                                                      */
/* Try to combine the given spline/knot with another spline near it.	*/
/* This will only work if we are looking at two endpoints, and none of	*/
/* the splines are looping.						*/
/* Return TRUE if combination is made.					*/
/*                                                                      */
/************************************************************************/
{
    Boolean_T	Found;
    int 	i;
    short	Point_Id1;
    short	Point_Id2;
    Spline_T	*S, *Spline2;
    Knot_T	*K, *Knot2;
    double 	D, Dist;
    double	X, Y, Z;

    if (Spline == NULL || Knot == NULL) return(FALSE);

    if (Knot != Spline->First && Knot != Spline->Last) return(FALSE);
    if (Spline->Loop) return(FALSE);
	
    Point_Id1 = Knot->Point_Id;

    X = Points[Point_Id1].Pos[0];
    Y = Points[Point_Id1].Pos[1];
    Z = Points[Point_Id1].Pos[2];

    Dist = 999999.0;

    switch (View_Id) {		
    
    case V_X: 					/* Y - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(Y - Points[i].Pos[1]), ABS(Z - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;

    
    case V_Y: 					/* X - Z */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(X - Points[i].Pos[0]), ABS(Z - Points[i].Pos[2]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */

        } /* for */
	break;
    
    case V_Z: 					/* X - Y */
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (i != Point_Id1 && Points[i].Reference_Count > 0 &&
	        	Is_Visible(Points[i])) {
                D = MAX(ABS(X - Points[i].Pos[0]), ABS(Y - Points[i].Pos[1]));
	        if (D < Dist) {
	            Dist = D;
	            Point_Id2 = i;
	        } /* if */
	    } /* if */
        } /* for */
	break;

    case V_P:				/* Persp */
	break;

    } /* switch */

    if (Dist > Max_Dist) return(FALSE);

	/* We've found a point (Point_Id2) near the Point_Id1, find 	*/
	/* first knot using this point:					*/

    Found = FALSE;
    for (S = Splines; S != NULL && !Found; S = S->Next) {

    	for (i = 0, K = S->First; i < S->Nbr_Knots && !Found; 
							i++, K = K->Next) {

	    if (Point_Id2 == K->Point_Id) {

		Spline2 = S;
		Knot2   = K;
		Found   = TRUE;
	    } /* if */

	} /* for */

    } /* for */

	/* Spline2, Knot2 is on the second spline			*/

    if (!Found) return(FALSE);	/* Should never happen!			*/
    if (Spline2 == NULL || Knot2 == NULL) return(FALSE);


    if (Spline2 == Spline) return(FALSE);
    if (Knot2 != Spline2->First && Knot2 != Spline2->Last) return(FALSE);
    if (Spline2->Loop) return(FALSE);

	/* All conditions for combination is fulfilled, so do it*/	

    if (Knot  == Spline->First) Spline_Reverse_Knots(Spline);
    if (Knot2 == Spline2->Last) Spline_Reverse_Knots(Spline2); 

	/* Append spline 2 to spline 1:					*/

    Knot2 = Spline2->First->Next; /* Skip first knot	*/

    while (Knot2 != NULL) {

	K = Knot2->Next;

	Knot_Remove(Spline2, Knot2);
	Knot_Insert(Spline, Spline->Last, Knot2);

	Knot2 = K;

    } /* while */

    Spline_Free(Spline2);

    return(TRUE);

} /* Splines_Combine */

void Splines_Disconnect(short Point_Id)
/************************************************************************/
/*                                                                      */
/* Disconnect all splines which meet at the point given by Point_Id.	*/
/*                                                                      */
/************************************************************************/
{
    Spline_T    *Spline;
    Knot_T	*Knot;
    int		Point_Id2;
    int		i;

    if (!Is_Point_Id_Ok(Point_Id)) return;

    if (Points[Point_Id].Reference_Count < 2) return;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

    	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
						i++, Knot = Knot->Next) {

 	    if (Knot->Point_Id == Point_Id) {

		Point_Id2 = Point_New(Points[Point_Id].Pos);
		if (Point_Id2 < 0) return;
		Knot->Point_Id = Point_Id2;

		if (--Points[Point_Id].Reference_Count < 2) return;

	    } /* if */

        } /* for */

    } /* for */

    Deselect_All();
    Select_Point(Point_Id);

} /* Splines_Disconnect */

#ifdef WORK_IN_PROGRESS
Boolean_T Splines_Copy()
{
    Spline_T	*Spline;
    Knot_T	*Knot, *First_Knot;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {
	
	for (i = 0, First_Knot = NULL, Knot = Spline->First; 
	     i < Spline->Nbr_Knots && First_Knot == NULL;
						i++, Knot = Knot->Next) {
	    if (Is_Segment_Selected(Spline, Knot) First_Knot = Knot;
	} /* for */


    } /* for */    
} /* Splines_Copy */

#endif

void Deselect_All()
/************************************************************************/
/*                                                                      */
/* DeSelect all points.							*/
/*                                                                      */
/************************************************************************/
{
    int	i;


    Select_Point_Id = -1;
    Select_Spline   = NULL;
    Select_Knot     = NULL;

    for (i = 0; i < Max_Nbr_Points; i++) Do_Deselect(Points[i]);

    Group_Mode          = FALSE;

} /* Deselect_All */

void Toggle_Select_All()
/************************************************************************/
/*                                                                      */
/* Toggle selection of all non-hidden points and enter group mode.	*/
/*                                                                      */
/************************************************************************/
{
    int	P;

    for (P = 0; P < Max_Nbr_Points; P++) {

	if (Is_Visible(Points[P])) {

	    if (Is_Selected(Points[P])) Do_Deselect(Points[P]);
            else                        Do_Select(Points[P]);

	} /* if */

    } /* for */

	/* Currently selected point isn't toggled! */
    if (Select_Point_Id >= 0) Do_Select(Points[Select_Point_Id]);

    Group_Mode = TRUE;

} /* Toggle_Select_All */

Boolean_T Select_Knot_From_Group()
/************************************************************************/
/*                                                                      */
/* Select any knot from the current group.				*/
/* This is sometimes necessary, as some commands needs a selected knot	*/
/* and not only a selected group. 					*/
/* Just chose the first knot in the group.				*/
/*                                                                      */
/* Return TRUE if we find a selectable knot.				*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    short 	Point_Id;
    Spline_T 	*Spline;
    Knot_T	*Knot;

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

    	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
						i++, Knot = Knot->Next) {

	    Point_Id = Knot->Point_Id;

			/* This knot is selected	*/
	    if (Is_Selected(Points[Point_Id])) {

		Select_Spline   = Spline;
		Select_Knot     = Knot;
		Select_Point_Id = Point_Id;
		return(TRUE);

	    } /* if */

	} /* for */

    } /* for */

    return(FALSE);

} /* Select_Knot_From_Group */

void Select_Point(short Point_Id)
/************************************************************************/
/*                                                                      */
/* Select all knots which refer to the point 'Point_Id'.		*/
/*                                                                      */
/* The first knot found will be the primary selected knot.		*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    Spline_T 	*Spline;
    Knot_T	*Knot;

    if (!Is_Point_Id_Ok(Point_Id)) return;

    if (Points[Point_Id].Reference_Count <= 0) return;

    Select_Point_Id = Point_Id;
    Select_Spline   = NULL;
    Select_Knot     = NULL;

    Do_Select(Points[Select_Point_Id]);

    for (Spline = Splines; Spline != NULL; Spline = Spline->Next) {

    	for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
						i++, Knot = Knot->Next) {

			/* This knot is selected	*/
	    if (Knot->Point_Id == Point_Id) {

		if (Select_Spline == NULL) {

			/* This is the first selected knot	*/
			/* Make this knot the primary one.	*/

		    Select_Spline = Spline;
		    Select_Knot   = Knot;

		} /* if */

	    } /* if */

	} /* for */

    } /* for */

} /* Select_Point */

void Select_Next_Knot()
/************************************************************************/
/*                                                                      */
/* Select next knot at currently selected point.			*/
/*                                                                      */
/************************************************************************/
{
    Spline_T 	*Spline;
    Knot_T	*Knot;

    if (Select_Spline == NULL || Select_Knot == NULL) return;


    Spline  = Select_Spline;
    Knot    = Select_Knot;

    Select_Point_Id = Knot->Point_Id;

    do {
	Knot = Knot->Next;
        if (Knot == NULL || Knot->Previous == Spline->Last) {

		/* Loop back to first spline if necessary */
	    Spline = Spline->Next;
	    if (Spline == NULL) Spline = Splines;
            Knot = Spline->First;

	} /* if */

    } while (Knot->Point_Id != Select_Point_Id);

    Select_Knot   = Knot;
    Select_Spline = Spline;

} /* Select_Next_Knot */

void Toggle_Select_Spline(Spline_T *Spline)
/************************************************************************/
/*                                                                      */
/* Toggle selection of all knots on spline 'Spline'.			*/
/*                                                                      */
/************************************************************************/
{
    int		i;
    Knot_T	*Knot;
    short	Point_Id;

    if (Spline == NULL) return;

    for (i = 0, Knot = Spline->First; i < Spline->Nbr_Knots; 
						i++, Knot = Knot->Next) {

	Point_Id = Knot->Point_Id;

	if (Is_Visible(Points[Point_Id])) {

	    if (Is_Selected(Points[Point_Id])) Do_Deselect(Points[Point_Id]);
            else                               Do_Select(Points[Point_Id]);

	} /* if */

    } /* for */

	/* Currently selected point isn't toggled! */
    if (Select_Point_Id >= 0) Do_Select(Points[Select_Point_Id]);

    Group_Mode = TRUE;

} /* Toggle_Select_Spline */

void Select_Area(short View_Id, Vector_T Pos1, Vector_T Pos2)
/************************************************************************/
/*                                                                      */
/* Toggle selection of all non hidden points in the area between Pos1 	*/
/* and Pos2 in the view given by View_Id.					*/
/*                                                                      */
/************************************************************************/
{
    int	i;
    double Tmp;

    switch (View_Id) {		
    
    case V_X: 					/* Y - Z */
        if (Pos1[2] > Pos2[2]) {Tmp=Pos1[2];Pos1[2]=Pos2[2];Pos2[2]=Tmp;};
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i]) &&
	        Points[i].Pos[1] > Pos1[1] && Points[i].Pos[1] < Pos2[1] &&
	        Points[i].Pos[2] > Pos1[2] && Points[i].Pos[2] < Pos2[2]) {
		
		if (Is_Selected(Points[i])) Do_Deselect(Points[i]);
		else 			    Do_Select(Points[i]);

	    } /* if */
        } /* for */
	break;
    
    case V_Y: 					/* X - Z */
        if (Pos1[2] > Pos2[2]) {Tmp=Pos1[2];Pos1[2]=Pos2[2];Pos2[2]=Tmp;};
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i]) &&
	        Points[i].Pos[0] > Pos1[0] && Points[i].Pos[0] < Pos2[0] &&
	        Points[i].Pos[2] > Pos1[2] && Points[i].Pos[2] < Pos2[2]) {

		if (Is_Selected(Points[i])) Do_Deselect(Points[i]);
		else 			    Do_Select(Points[i]);

	    } /* if */
        } /* for */
	break;

    case V_Z: 					/* X - Y */
        if (Pos1[1] > Pos2[1]) {Tmp=Pos1[1];Pos1[1]=Pos2[1];Pos2[1]=Tmp;};
        for (i = 0; i < Max_Nbr_Points; i++) {
	    if (Points[i].Reference_Count > 0 && Is_Visible(Points[i]) &&
	        Points[i].Pos[0] > Pos1[0] && Points[i].Pos[0] < Pos2[0] &&
	        Points[i].Pos[1] > Pos1[1] && Points[i].Pos[1] < Pos2[1]) {

		if (Is_Selected(Points[i])) Do_Deselect(Points[i]);
		else 			    Do_Select(Points[i]);

	    } /* if */
        } /* for */
	break;

    case V_P:					/* Persp */
	break;

    } /* switch */

    Group_Mode = TRUE;

} /* Select_Area */

void Hide_Selected_Points()
/************************************************************************/
/*                                                                      */
/* Hide all selected points and deselect them.				*/
/*                                                                      */
/************************************************************************/
{
    int	i;
        
    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && Is_Selected(Points[i]) ) {

		Do_Hide(Points[i]);
		Points_Hidden = TRUE;
	} /* if */

    } /* for */

    Deselect_All();
    
} /* Hide_Selected_Points */

void Hide_Unselected_Points()
/************************************************************************/
/*                                                                      */
/* Hide all unselected points and deselect all.				*/
/*                                                                      */
/************************************************************************/
{
    int	i;
        
    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && !Is_Selected(Points[i]) ) {

		Do_Hide(Points[i]);
		Points_Hidden = TRUE;

	} /* if */

    } /* for */

    Deselect_All();
    
} /* Hide_Unselected_Points */

void Show_All_Points()
/************************************************************************/
/*                                                                      */
/* Redisplay all hidden points.						*/
/*                                                                      */
/************************************************************************/
{
    int	i;

    for (i = 0; i < Max_Nbr_Points; i++) {

	if (Points[i].Reference_Count > 0 && Is_Hidden(Points[i]) ) {

	    Do_Unhide(Points[i]);

	} /* if */

    } /* for */
    Points_Hidden = FALSE;
    
} /* Show_All_Points */
