/*
 * $Id: retopo.c 16914 2008-10-04 11:04:09Z ton $
 *
 * ***** 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) 2006 by Nicholas Bishop
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 *
 * Implements the Retopo tools
 *
 * BIF_retopo.h
 *
 */

#include "MEM_guardedalloc.h"

#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_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 "BDR_editobject.h"

#include "BIF_editmesh.h"
#include "BIF_editmode_undo.h"
#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_mywindow.h"
#include "BIF_retopo.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"

#include "BKE_curve.h"
#include "BKE_depsgraph.h"
#include "BKE_global.h"
#include "BKE_mesh.h"

#include "BLI_blenlib.h"
#include "BLI_editVert.h"

#include "BSE_drawview.h"
#include "BSE_edit.h"
#include "BSE_view.h"

#include "editmesh.h"
#include "mydevice.h"

#ifdef WIN32
#define _USE_MATH_DEFINES
#endif
#include <math.h>
#include <stdlib.h>
#include <string.h>

typedef struct RetopoPaintHit {
	struct RetopoPaintHit *next, *prev;
	RetopoPaintPoint *intersection;
	short index;
	float where;
} RetopoPaintHit;

static void retopo_do_2d(View3D *v3d, double proj[2], float *v, char adj);

#if 0
static void retopo_paint_debug_print(RetopoPaintData *rpd)
{
	RetopoPaintLine *l;
	RetopoPaintPoint *p;
	
	for(l= rpd->lines.first; l; l= l->next) {
		printf("Line:\n");
		for(p= l->points.first; p; p= p->next) {
			printf("   Point(%d: %d,%d)\n",p->index,p->loc.x,p->loc.y);
		}
	}
	
	fflush(stdout);
}
#endif

/* Painting */
RetopoPaintData *get_retopo_paint_data(void)
{
	if(!retopo_mesh_paint_check()) return NULL;
	if(!G.editMesh) return NULL;
	return G.editMesh->retopo_paint_data;
}

char retopo_mesh_paint_check(void)
{
	return retopo_mesh_check() && G.scene->toolsettings->retopo_mode & RETOPO_PAINT;
}

void retopo_free_paint_data(RetopoPaintData *rpd)
{
	if(rpd) {
		RetopoPaintLine *l;
		for(l= rpd->lines.first; l; l= l->next) {
			BLI_freelistN(&l->points);
			BLI_freelistN(&l->hitlist);
		}
		BLI_freelistN(&rpd->lines);
		
		BLI_freelistN(&rpd->intersections);

		MEM_freeN(rpd);
	}
}

void retopo_free_paint(void)
{
	retopo_free_paint_data(G.editMesh->retopo_paint_data);
	G.editMesh->retopo_paint_data= NULL;
}

char line_intersection_2d(const vec2s *a, const vec2s *b, const vec2s *c, const vec2s *d, vec2s *out,
			  float *r, float *s)
{
	float den;
	*r= (a->y - c->y) * (d->x - c->x) - (a->x - c->x) * (d->y - c->y);
	*s= (a->y - c->y) * (b->x - a->x) - (a->x - c->x) * (b->y - a->y);
	den= (b->x - a->x) * (d->y - c->y) - (b->y - a->y) * (d->x - c->x);

	if((a->x==b->x && a->y==b->y) || (c->x==d->x && c->y==d->y)) return 0;

	if(!den) return 0;

	*r/= den;
	*s/= den;

	if(*s<0 || *s>=1 || *r<0 || *r>=1) return 0;

	out->x= a->x + *r*(b->x - a->x);
	out->y= a->y + *r*(b->y - a->y);
	return 1;
}

void retopo_paint_add_line_hit(RetopoPaintLine *l, RetopoPaintPoint *p, RetopoPaintPoint *intersection, float w)
{
	RetopoPaintHit *prev, *hit= MEM_callocN(sizeof(RetopoPaintHit),"RetopoPaintHit");
	
	hit->intersection= intersection;
	hit->index= p->index;
	hit->where= w;

	prev= l->hitlist.first;
	if(!prev) {
		BLI_addtail(&l->hitlist,hit);
	}
	else if(prev->index>hit->index) {
		BLI_addhead(&l->hitlist,hit);
	}
	else {
		/* Move forward until we hit the next highest index */
		while(prev->next) {
			if(prev->next->index > hit->index) break;
			prev= prev->next;
		}
		/* Move backward until we hit the next lowest where */
		while(prev->prev && prev->prev->index==prev->index &&
		      prev->where > hit->where)
			prev=prev->prev;
		BLI_insertlink(&l->hitlist,prev,hit);
	}
	
	/* Removed duplicate intersections */
	if(hit->prev && hit->prev->intersection==hit->intersection) {
		BLI_freelinkN(&l->hitlist,hit);
	}
}

char retopo_paint_add_intersection(RetopoPaintData *rpd, RetopoPaintLine *l1, RetopoPaintPoint *p1,
				   RetopoPaintLine *l2, RetopoPaintPoint *p2, vec2s *out, float r, float s)
{
	RetopoPaintPoint *p, *hit;
	char found= 0;

	for(p=rpd->intersections.first; p; p= p->next) {
		if(sqrt(pow(p->loc.x-out->x,2)+pow(p->loc.y-out->y,2))<7) {
			found= 1;
			break;
		}
	}

	if(!found) {
		hit= MEM_callocN(sizeof(RetopoPaintPoint),"Retopo paint intersection");
		hit->loc.x= out->x;
		hit->loc.y= out->y;
		BLI_addtail(&rpd->intersections,hit);
	} else {
		hit= p;
	}

	retopo_paint_add_line_hit(l1,p1,hit,r);
	retopo_paint_add_line_hit(l2,p2,hit,s);

	return !found;
}


/* Returns 1 if a new intersection was added */
char do_line_intersection(RetopoPaintData *rpd, RetopoPaintLine *l1, RetopoPaintPoint *p1,
			  RetopoPaintLine *l2, RetopoPaintPoint *p2)
{
	vec2s out;
	float r,s;
	if(line_intersection_2d(&p1->loc, &p1->next->loc,
				&p2->loc, &p2->next->loc,
				&out,&r,&s)) {
		if(retopo_paint_add_intersection(rpd,l1,p1,l2,p2,&out,r,s))
			return 1;
	}
	return 0;
}

typedef struct FaceNode {
	struct FaceNode *next, *prev;
	MFace f;
} FaceNode;

char faces_equal(EditFace *f1, EditFace *f2)
{
	return editface_containsVert(f2,f1->v1) &&
	       editface_containsVert(f2,f1->v2) &&
	       editface_containsVert(f2,f1->v3) &&
	       (f1->v4 ? editface_containsVert(f2,f1->v4) : 1);
}

EditFace *addfaceif(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *v3, EditVert *v4)
{
	EditFace *efa;
	
	for(efa= em->faces.first; efa; efa= efa->next) {
		if(editface_containsVert(efa,v1) &&
		   editface_containsVert(efa,v2) &&
		   editface_containsVert(efa,v3) &&
		   (v4 ? editface_containsVert(efa,v4) : 1))
			return NULL;
	}

	return addfacelist(v1,v2,v3,v4,NULL,NULL);
}

void retopo_paint_apply(void)
{
	RetopoPaintData *rpd= G.editMesh->retopo_paint_data;
	EditVert *eve;

	if(rpd) {
		RetopoPaintLine *l1, *l2;
		RetopoPaintPoint *p1, *p2;
		unsigned hitcount= 0;
		unsigned i;
		RetopoPaintHit *h;
		float hitco[3];
		
		/* Find intersections */
		BLI_freelistN(&rpd->intersections);
		for(l1= rpd->lines.first; l1; l1= l1->next) {
			for(l2= rpd->lines.first; l2; l2= l2->next) {
				if(l1!=l2) {
					for(p1= l1->points.first; p1 && p1!=l1->points.last; p1= p1->next) {
						for(p2= l2->points.first; p2 && p2!=l2->points.last; p2= p2->next) {
							if(p1!=p2) {
								if(do_line_intersection(rpd,l1,p1,l2,p2))
									++hitcount;
							}
						}
					}
				}
			}
		}

		/*topoPaintHit *hit;
		l1= rpd->lines.first;
		for(hit= l1->hitlist.first; hit; hit= hit->next) {
			printf("\nhit(%p,%d) ",hit->intersection,hit->index);
		}
		fflush(stdout);*/

		/* Deselect */
		for(eve= G.editMesh->verts.first; eve; eve= eve->next)
			eve->f &= ~SELECT;
		EM_deselect_flush();

		for(i=0; i<hitcount; ++i) {
			RetopoPaintPoint *intersection= BLI_findlink(&rpd->intersections,i);
			double proj[2] = {intersection->loc.x, intersection->loc.y};
			retopo_do_2d(rpd->paint_v3d, proj, hitco, 1);
			intersection->eve= addvertlist(hitco, NULL);
			intersection->eve->f= SELECT;
		}
		
		for(l1= rpd->lines.first; l1; l1= l1->next) {
			unsigned etcount= BLI_countlist(&l1->hitlist);
			if(etcount>=2) {
				for(h= l1->hitlist.first; (h && h->next); h= h->next)
					addedgelist(h->intersection->eve,h->next->intersection->eve,NULL);
				if(etcount>=3 && l1->cyclic)
					addedgelist(((RetopoPaintHit*)l1->hitlist.first)->intersection->eve,
						    ((RetopoPaintHit*)l1->hitlist.last)->intersection->eve, NULL);
			}
		}
		
		addfaces_from_edgenet();
	}

	retopo_free_paint();
}

void add_rppoint(RetopoPaintLine *l, short x, short y)
{
	RetopoPaintPoint *p= MEM_callocN(sizeof(RetopoPaintPoint),"RetopoPaintPoint");
	double proj[2];
	p->loc.x= x;
	p->loc.y= y;
	BLI_addtail(&l->points,p);
	p->index= p->prev?p->prev->index+1:0;

	proj[0] = p->loc.x;
	proj[1] = p->loc.y;

	retopo_do_2d(G.editMesh->retopo_paint_data->paint_v3d, proj, p->co, 1);
}
RetopoPaintLine *add_rpline(RetopoPaintData *rpd)
{
	RetopoPaintLine *l= MEM_callocN(sizeof(RetopoPaintLine),"RetopoPaintLine");
	BLI_addtail(&rpd->lines,l);
	return l;
}

void retopo_paint_toggle_cyclic(RetopoPaintLine *l)
{
	if(l==NULL)
		return;
	if(!l->cyclic) {
		RetopoPaintPoint *pf= l->points.first;

		if(pf) {
			add_rppoint(l, pf->loc.x, pf->loc.y);
			l->cyclic= l->points.last;
		}
	} else {
		BLI_freelinkN(&l->points,l->cyclic);
		l->cyclic= NULL;
	}
}

void retopo_paint_add_line(RetopoPaintData *rpd, short mouse[2])
{
	RetopoPaintLine *l= add_rpline(rpd);
	float range[2]= {mouse[0]-rpd->sloc[0],mouse[1]-rpd->sloc[1]};
	int i;

	/* Add initial point */
	add_rppoint(l,rpd->sloc[0],rpd->sloc[1]);
	for(i=0; i<G.scene->toolsettings->line_div; ++i) {
		const float mul= (i+1.0f) / G.scene->toolsettings->line_div;
		add_rppoint(l,rpd->sloc[0] + range[0]*mul,rpd->sloc[1] + range[1]*mul);
	}

	allqueue(REDRAWVIEW3D,0);
}

void retopo_paint_add_ellipse(RetopoPaintData *rpd, short mouse[2])
{
	int i;

	add_rpline(rpd);
	for (i=0; i<G.scene->toolsettings->ellipse_div; i++) {
		float t= (float) i / G.scene->toolsettings->ellipse_div;
		float cur= t*(M_PI*2);
		
		float w= abs(mouse[0]-rpd->sloc[0]);
		float h= abs(mouse[1]-rpd->sloc[1]);

		add_rppoint(rpd->lines.last,cos(cur)*w+rpd->sloc[0],sin(cur)*h+rpd->sloc[1]);
	}

	retopo_paint_toggle_cyclic(rpd->lines.last);

	allqueue(REDRAWVIEW3D,0);
}

void retopo_end_okee(void)
{
	if(okee("Apply retopo paint?"))
		retopo_paint_apply();
	else
		retopo_free_paint();
	G.scene->toolsettings->retopo_mode &= ~RETOPO_PAINT;
}

void retopo_paint_toggle(void *a, void *b)
{
	/* Note that these operations are reversed because mode bit has already been set! */
	if(retopo_mesh_paint_check()) { /* Activate retopo paint */
		RetopoPaintData *rpd= MEM_callocN(sizeof(RetopoPaintData),"RetopoPaintData");
		
		G.editMesh->retopo_paint_data= rpd;
		G.scene->toolsettings->retopo_paint_tool= RETOPO_PEN;
		rpd->seldist= 15;
		rpd->nearest.line= NULL;
		G.scene->toolsettings->line_div= 25;
		G.scene->toolsettings->ellipse_div= 25;
		G.scene->toolsettings->retopo_hotspot= 1;
	} else retopo_end_okee();

	BIF_undo_push("Retopo toggle");

	allqueue(REDRAWVIEW3D, 1);
}

void retopo_paint_view_update(struct View3D *v3d)
{
	RetopoPaintData *rpd= get_retopo_paint_data();

	if(rpd && rpd->paint_v3d==v3d) {
		RetopoPaintLine *l;
		RetopoPaintPoint *p;
		double ux, uy, uz;
		
		for(l= rpd->lines.first; l; l= l->next) {
			for(p= l->points.first; p; p= p->next) {
				gluProject(p->co[0],p->co[1],p->co[2], v3d->retopo_view_data->mats.modelview,
					   v3d->retopo_view_data->mats.projection,
					   (GLint *)v3d->retopo_view_data->mats.viewport, &ux, &uy, &uz);
				p->loc.x= ux;
				p->loc.y= uy;
			}
		}
	}
}

void retopo_force_update(void)
{
	RetopoPaintData *rpd= get_retopo_paint_data();
	
	if(rpd) {
		View3D *vd= rpd->paint_v3d;
		
		if(vd) {
			if(vd->depths) vd->depths->damaged= 1;
			retopo_queue_updates(vd);
			if(retopo_mesh_paint_check() && vd->retopo_view_data)
				allqueue(REDRAWVIEW3D, 0);
		}
	}
}

/* Returns 1 if event should be processed by caller, 0 otherwise */
char retopo_paint(const unsigned short event)
{
	RetopoPaintData *rpd= get_retopo_paint_data();

	if(!event) return 1;
	if(rpd) {
		RetopoPaintLine *l;
		short mouse[2];
		char lbut= get_mbut() & (U.flag & USER_LMOUSESELECT ? R_MOUSE : L_MOUSE);
		
		if(rpd->paint_v3d && rpd->paint_v3d!=G.vd) return 1;
	
		getmouseco_areawin(mouse);

		if(rpd->in_drag && !lbut) { /* End drag */
			rpd->in_drag= 0;

			switch(G.scene->toolsettings->retopo_paint_tool) {
			case RETOPO_PEN:
				break;
			case RETOPO_LINE:
				retopo_paint_add_line(rpd, mouse);
				break;
			case RETOPO_ELLIPSE:
				retopo_paint_add_ellipse(rpd, mouse);
				break;
			}
			BIF_undo_push("Retopo paint");
		}

		switch(event) {
		case MOUSEX:
		case MOUSEY:
			switch(G.scene->toolsettings->retopo_paint_tool) {
			case RETOPO_PEN:
				if(rpd->in_drag && rpd->lines.last) {
					l= rpd->lines.last;

					if(((RetopoPaintPoint*)l->points.last)->loc.x != mouse[0] ||
					   ((RetopoPaintPoint*)l->points.last)->loc.y != mouse[1]) {
						add_rppoint(l,mouse[0],mouse[1]);
					}
					rpd->nearest.line= NULL;
					
					break;
				} else if(G.scene->toolsettings->retopo_hotspot) { /* Find nearest endpoint */
					float sdist;
					RetopoPaintLine *l= rpd->lines.first;
					RetopoPaintSel n= {NULL,NULL,l,1};
					sdist= rpd->seldist + 10;
					for(l= rpd->lines.first; l; l= l->next) {
						float tdist;
						RetopoPaintPoint *p1= l->points.first, *p2= l->points.last;
						
						tdist= sqrt(pow(mouse[0] - p1->loc.x,2)+pow(mouse[1] - p1->loc.y,2));
						if(tdist < sdist && tdist < rpd->seldist) {
							sdist= tdist;
							n.line= l;
							n.first= 1;
						} else {
							tdist= sqrt(pow(mouse[0] - p2->loc.x,2)+pow(mouse[1] - p2->loc.y,2));
							if(tdist < sdist && tdist < rpd->seldist) {
								sdist= tdist;
								n.line= l;
								n.first= 0;
							}
						}
					}
					
					if(sdist < rpd->seldist)
						rpd->nearest= n;
					else rpd->nearest.line= NULL;
				}
				break;
			case RETOPO_LINE:
				break;
			case RETOPO_ELLIPSE:
				break;
			}
			allqueue(REDRAWVIEW3D,0);
			break;
		case RETKEY:
		case PADENTER:
			retopo_paint_apply();
		case ESCKEY:
			G.scene->toolsettings->retopo_mode&= ~RETOPO_PAINT;
			retopo_free_paint();

			BIF_undo_push("Retopo toggle");
		
			allqueue(REDRAWVIEW3D, 1);
			allqueue(REDRAWBUTSEDIT, 0);
			break;
		case CKEY:
			retopo_paint_toggle_cyclic(rpd->lines.last);
			BIF_undo_push("Retopo toggle cyclic");
			allqueue(REDRAWVIEW3D, 0);
			break;
		case EKEY:
			G.scene->toolsettings->retopo_paint_tool= RETOPO_ELLIPSE;
			allqueue(REDRAWVIEW3D, 1);
			break;
		case HKEY:
			G.scene->toolsettings->retopo_hotspot= !G.scene->toolsettings->retopo_hotspot;
			allqueue(REDRAWVIEW3D, 1);
			break;
		case LKEY:
			G.scene->toolsettings->retopo_paint_tool= RETOPO_LINE;
			allqueue(REDRAWVIEW3D, 1);
			break;
		case PKEY:
			G.scene->toolsettings->retopo_paint_tool= RETOPO_PEN;
			allqueue(REDRAWVIEW3D, 1);
			break;
		case XKEY:
		case DELKEY:
			l= rpd->lines.last;
			if(l) {
				BLI_freelistN(&l->points);
				BLI_freelistN(&l->hitlist);
				BLI_freelinkN(&rpd->lines, l);
				if(rpd->nearest.line == l)
					rpd->nearest.line= NULL;
				BIF_undo_push("Erase line");
				allqueue(REDRAWVIEW3D, 0);
			}
			break;
		case LEFTMOUSE:
			if(!rpd->in_drag) { /* Start new drag */
				rpd->in_drag= 1;
				
				if(!rpd->paint_v3d)
					rpd->paint_v3d= G.vd;
				
				/* Location of mouse down */
				rpd->sloc[0]= mouse[0];
				rpd->sloc[1]= mouse[1];
				
				switch(G.scene->toolsettings->retopo_paint_tool) {
				case RETOPO_PEN:
					if(rpd->nearest.line) {
						RetopoPaintPoint *p, *pt;
						int i;
						
						BLI_remlink(&rpd->lines,rpd->nearest.line);
						BLI_addtail(&rpd->lines,rpd->nearest.line);
						
						/* Check if we need to reverse the line */
						if(rpd->nearest.first) {
							for(p= rpd->nearest.line->points.first; p; p= p->prev) {
								pt= p->prev;
								p->prev= p->next;
								p->next= pt;
							}
							pt= rpd->nearest.line->points.first;
							rpd->nearest.line->points.first= rpd->nearest.line->points.last;
							rpd->nearest.line->points.last= pt;
							
							/* Reverse indices */
							i= 0;
							for(p= rpd->nearest.line->points.first; p; p= p->next)
								p->index= i++;
						}
					} else {
						add_rpline(rpd);
						add_rppoint(rpd->lines.last,mouse[0],mouse[1]);
					}
					break;
				case RETOPO_LINE:
					break;
				case RETOPO_ELLIPSE:
					break;
				}
				allqueue(REDRAWVIEW3D, 0);
			}
			break;
		case MIDDLEMOUSE:
		case WHEELUPMOUSE:
		case WHEELDOWNMOUSE:
		case PAD0: case PAD1: case PAD2: case PAD3: case PAD4:
		case PAD5: case PAD6: case PAD7: case PAD8: case PAD9:
		case PADMINUS: case PADPLUSKEY:
			return 1;
		}
		return 0;
	} else return 1;
}
void retopo_draw_paint_lines()
{
	RetopoPaintData *rpd= get_retopo_paint_data();

	if(rpd && rpd->paint_v3d==G.vd) {
		RetopoPaintLine *l;
		RetopoPaintPoint *p;

		glColor3f(0,0,0);
		glLineWidth(2);

		/* Draw existing lines */
		for(l= rpd->lines.first; l; l= l->next) {
			if(l==rpd->lines.last)
				glColor3f(0.3,0,0);
			glBegin(l->cyclic?GL_LINE_LOOP:GL_LINE_STRIP);
			for(p= l->points.first; p; p= p->next) {
				glVertex2s(p->loc.x,p->loc.y);
			}
			glEnd();
		}

		/* Draw ellipse */
		if(G.scene->toolsettings->retopo_paint_tool==RETOPO_ELLIPSE && rpd->in_drag) {
			short mouse[2];
			getmouseco_areawin(mouse);
		
			setlinestyle(3);
			fdrawXORellipse(rpd->sloc[0],rpd->sloc[1],abs(mouse[0]-rpd->sloc[0]),abs(mouse[1]-rpd->sloc[1]));
			setlinestyle(0);
		}
		else if(G.scene->toolsettings->retopo_paint_tool==RETOPO_LINE && rpd->in_drag) {
			short mouse[2];
			getmouseco_areawin(mouse);

			setlinestyle(3);
			sdrawXORline(rpd->sloc[0],rpd->sloc[1],mouse[0],mouse[1]);
			setlinestyle(0);
		}
		else if(rpd->nearest.line) { /* Draw selection */
			RetopoPaintPoint *p= rpd->nearest.first ? rpd->nearest.line->points.first :
				rpd->nearest.line->points.last;
			if(p)
				fdrawXORcirc(p->loc.x, p->loc.y, rpd->seldist);
		}

		glLineWidth(1);
	}
}

RetopoPaintData *retopo_paint_data_copy(RetopoPaintData *rpd)
{
	RetopoPaintData *copy;
	RetopoPaintLine *l, *lcp;
	RetopoPaintPoint *p, *pcp;

	if(!rpd) return NULL;

	copy= MEM_mallocN(sizeof(RetopoPaintData),"RetopoPaintDataCopy");

	memcpy(copy,rpd,sizeof(RetopoPaintData));
	copy->lines.first= copy->lines.last= NULL;
	copy->nearest.next= copy->nearest.prev= NULL;
	copy->nearest.line= NULL;
	copy->nearest.first= 0;
	
	for(l= rpd->lines.first; l; l= l->next) {
		lcp= MEM_dupallocN(l);
		BLI_addtail(&copy->lines, lcp);
		
		lcp->hitlist.first= lcp->hitlist.last= NULL;
		lcp->points.first= lcp->points.last= NULL;
		lcp->cyclic= NULL;
		
		for(p= l->points.first; p; p= p->next) {
			pcp= MEM_dupallocN(p);
			BLI_addtail(&lcp->points, pcp);
			pcp->eve= NULL;
		}
	}

	copy->intersections.first= copy->intersections.last= NULL;

	return copy;
}

char retopo_mesh_check(void)
{
	return G.obedit && G.obedit->type==OB_MESH && (G.scene->toolsettings->retopo_mode & RETOPO);
}
char retopo_curve_check(void)
{
	return G.obedit && (G.obedit->type==OB_CURVE ||
		            G.obedit->type==OB_SURF) && (((Curve*)G.obedit->data)->flag & CU_RETOPO);
}

void retopo_toggle(void *j1,void *j2)
{
	if(retopo_mesh_check() || retopo_curve_check()) {
		if(G.vd->depths) G.vd->depths->damaged= 1;
		retopo_queue_updates(G.vd);
	} else {
		if(G.editMesh && G.scene->toolsettings->retopo_mode & RETOPO_PAINT)
			retopo_end_okee();
	}

	allqueue(REDRAWBUTSEDIT, 0);
	allqueue(REDRAWVIEW3D, 0);
}

static void retopo_do_2d(View3D *v3d, double proj[2], float *v, char adj)
{
	/* Check to make sure vert is visible in window */
	if(proj[0]>0 && proj[1]>0 && proj[0] < v3d->depths->w && proj[1] < v3d->depths->h) {
		float depth= v3d->depths->depths[((int)proj[1])*v3d->depths->w+((int)proj[0])];
		double px, py, pz;
	
		/* Don't modify the point if it'll be mapped to the background */
		if(depth==v3d->depths->depth_range[1]) {
			if(adj) {
				/* Find the depth of (0,0,0); */
				gluProject(0,0,0,v3d->retopo_view_data->mats.modelview,
					   v3d->retopo_view_data->mats.projection,
					   (GLint *)v3d->retopo_view_data->mats.viewport,&px,&py,&pz);
				depth= pz;
			}
			else return;
		}
		
		/* Find 3D location with new depth (unproject) */
		gluUnProject(proj[0],proj[1],depth,v3d->retopo_view_data->mats.modelview,
			     v3d->retopo_view_data->mats.projection,
			     (GLint *)v3d->retopo_view_data->mats.viewport,&px,&py,&pz);
		
		v[0]= px;
		v[1]= py;
		v[2]= pz;
	}
}

void retopo_do_vert(View3D *v3d, float *v)
{
	double proj[3];

	/* Find 2D location (project) */
	gluProject(v[0],v[1],v[2],v3d->retopo_view_data->mats.modelview,v3d->retopo_view_data->mats.projection,
		   (GLint *)v3d->retopo_view_data->mats.viewport,&proj[0],&proj[1],&proj[2]);
	
	retopo_do_2d(v3d,proj,v,0);
}

void retopo_do_all(void)
{
	RetopoViewData *rvd= G.vd->retopo_view_data;
	if(retopo_mesh_check()) {
		if(rvd) {
			EditMesh *em= G.editMesh;
			EditVert *eve;
			
			/* Apply retopo to all selected vertices */
			eve= em->verts.first;
			while(eve) {
				if(eve->f & SELECT)
					retopo_do_vert(G.vd,eve->co);
				eve= eve->next;
			}
			
			DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
			allqueue(REDRAWVIEW3D, 0);
		}
	}
	else if(retopo_curve_check()) {
		if(rvd) {
			extern ListBase editNurb;
			Nurb *nu;
			BPoint *bp;
			int i, j;

			for(nu= editNurb.first; nu; nu= nu->next)
			{
				if(nu->type & CU_2D) {
					/* Can't wrap a 2D curve onto a 3D surface */
				}
				else if(nu->type & CU_BEZIER) {
					for(i=0; i<nu->pntsu; ++i) {
						if(nu->bezt[i].f1 & SELECT)
							retopo_do_vert(G.vd, nu->bezt[i].vec[0]);
						if(nu->bezt[i].f2 & SELECT)
							retopo_do_vert(G.vd, nu->bezt[i].vec[1]);
						if(nu->bezt[i].f3 & SELECT)
							retopo_do_vert(G.vd, nu->bezt[i].vec[2]);
					}
				}
				else {
					bp= nu->bp;
					for(i=0; i<nu->pntsv; ++i) {
						for(j=0; j<nu->pntsu; ++j, ++bp) {
							if(bp->f1 & SELECT)
								retopo_do_vert(G.vd,bp->vec);
						}
					}
				}
				
				testhandlesNurb(nu);
			}
			
			DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
			allqueue(REDRAWVIEW3D, 0);			
		}
	}
}

void retopo_do_all_cb(void *j1, void *j2)
{
	/* This is called from editbuttons, so user needs to specify view */
	if(!select_area(SPACE_VIEW3D)) return;

	if(G.vd->drawtype == OB_WIRE) {
		error("Cannot apply retopo in wireframe mode");
		return;
	}

	retopo_do_all();
	BIF_undo_push("Retopo all");
}

void retopo_queue_updates(View3D *v3d)
{
	if(retopo_mesh_check() || retopo_curve_check()) {
		if(!v3d->retopo_view_data)
			v3d->retopo_view_data= MEM_callocN(sizeof(RetopoViewData),"RetopoViewData");
		
		v3d->retopo_view_data->queue_matrix_update= 1;
		
		allqueue(REDRAWVIEW3D, 0);
	}
}

void retopo_matrix_update(View3D *v3d)
{
	RetopoPaintData *rpd= get_retopo_paint_data();
	if((retopo_mesh_check() || retopo_curve_check()) && (!rpd || rpd->paint_v3d==v3d)) {
		RetopoViewData *rvd= v3d->retopo_view_data;
		if(!rvd) {
			rvd= MEM_callocN(sizeof(RetopoViewData),"RetopoViewData");
			v3d->retopo_view_data= rvd;
			rvd->queue_matrix_update= 1;
		}
		if(rvd && rvd->queue_matrix_update) {
			bgl_get_mats(&rvd->mats);

			rvd->queue_matrix_update= 0;
		}
	}
}

void retopo_free_view_data(View3D *v3d)
{
	if(v3d->retopo_view_data) {
		MEM_freeN(v3d->retopo_view_data);
		v3d->retopo_view_data= NULL;
	}
}
