Blender V2.61 - r43446

particle_edit.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2007 by Janne Karhu.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): none yet.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <stdlib.h>
00034 #include <math.h>
00035 #include <string.h>
00036 #include <assert.h>
00037 
00038 #include "MEM_guardedalloc.h"
00039 
00040 #include "DNA_scene_types.h"
00041 #include "DNA_mesh_types.h"
00042 #include "DNA_meshdata_types.h"
00043 #include "DNA_view3d_types.h"
00044 #include "DNA_screen_types.h"
00045 #include "DNA_space_types.h"
00046 
00047 #include "BLI_math.h"
00048 #include "BLI_blenlib.h"
00049 #include "BLI_dynstr.h"
00050 #include "BLI_kdtree.h"
00051 #include "BLI_rand.h"
00052 #include "BLI_utildefines.h"
00053 
00054 #include "BKE_DerivedMesh.h"
00055 #include "BKE_depsgraph.h"
00056 
00057 #include "BKE_context.h"
00058 #include "BKE_global.h"
00059 #include "BKE_object.h"
00060 #include "BKE_mesh.h"
00061 #include "BKE_modifier.h"
00062 #include "BKE_particle.h"
00063 #include "BKE_report.h"
00064 #include "BKE_scene.h"
00065 
00066 #include "BKE_pointcache.h"
00067 
00068 #include "BIF_gl.h"
00069 #include "BIF_glutil.h"
00070 
00071 #include "ED_physics.h"
00072 #include "ED_mesh.h"
00073 #include "ED_particle.h"
00074 #include "ED_view3d.h"
00075 
00076 #include "UI_resources.h"
00077 
00078 #include "WM_api.h"
00079 #include "WM_types.h"
00080 
00081 #include "RNA_access.h"
00082 #include "RNA_define.h"
00083 
00084 #include "physics_intern.h"
00085 
00086 static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
00087 static void PTCacheUndo_clear(PTCacheEdit *edit);
00088 static void recalc_emitter_field(Object *ob, ParticleSystem *psys);
00089 
00090 #define KEY_K                   PTCacheEditKey *key; int k
00091 #define POINT_P                 PTCacheEditPoint *point; int p
00092 #define LOOP_POINTS             for(p=0, point=edit->points; p<edit->totpoint; p++, point++)
00093 #define LOOP_VISIBLE_POINTS     for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(!(point->flag & PEP_HIDE))
00094 #define LOOP_SELECTED_POINTS    for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point_is_selected(point))
00095 #define LOOP_UNSELECTED_POINTS  for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(!point_is_selected(point))
00096 #define LOOP_EDITED_POINTS      for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point->flag & PEP_EDIT_RECALC)
00097 #define LOOP_TAGGED_POINTS      for(p=0, point=edit->points; p<edit->totpoint; p++, point++) if(point->flag & PEP_TAG)
00098 #define LOOP_KEYS               for(k=0, key=point->keys; k<point->totkey; k++, key++)
00099 #define LOOP_VISIBLE_KEYS       for(k=0, key=point->keys; k<point->totkey; k++, key++) if(!(key->flag & PEK_HIDE))
00100 #define LOOP_SELECTED_KEYS      for(k=0, key=point->keys; k<point->totkey; k++, key++) if((key->flag & PEK_SELECT) && !(key->flag & PEK_HIDE))
00101 #define LOOP_TAGGED_KEYS        for(k=0, key=point->keys; k<point->totkey; k++, key++) if(key->flag & PEK_TAG)
00102 
00103 #define KEY_WCO                 (key->flag & PEK_USE_WCO ? key->world_co : key->co)
00104 
00105 /**************************** utilities *******************************/
00106 
00107 int PE_poll(bContext *C)
00108 {
00109     Scene *scene= CTX_data_scene(C);
00110     Object *ob= CTX_data_active_object(C);
00111 
00112     if(!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
00113         return 0;
00114     
00115     return (PE_get_current(scene, ob) != NULL);
00116 }
00117 
00118 int PE_hair_poll(bContext *C)
00119 {
00120     Scene *scene= CTX_data_scene(C);
00121     Object *ob= CTX_data_active_object(C);
00122     PTCacheEdit *edit;
00123 
00124     if(!scene || !ob || !(ob->mode & OB_MODE_PARTICLE_EDIT))
00125         return 0;
00126     
00127     edit= PE_get_current(scene, ob);
00128 
00129     return (edit && edit->psys);
00130 }
00131 
00132 int PE_poll_view3d(bContext *C)
00133 {
00134     return PE_poll(C) && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
00135         CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
00136 }
00137 
00138 void PE_free_ptcache_edit(PTCacheEdit *edit)
00139 {
00140     POINT_P;
00141 
00142     if(edit==0) return;
00143 
00144     PTCacheUndo_clear(edit);
00145 
00146     if(edit->points) {
00147         LOOP_POINTS {
00148             if(point->keys) 
00149                 MEM_freeN(point->keys);
00150         }
00151 
00152         MEM_freeN(edit->points);
00153     }
00154 
00155     if(edit->mirror_cache)
00156         MEM_freeN(edit->mirror_cache);
00157 
00158     if(edit->emitter_cosnos) {
00159         MEM_freeN(edit->emitter_cosnos);
00160         edit->emitter_cosnos= 0;
00161     }
00162 
00163     if(edit->emitter_field) {
00164         BLI_kdtree_free(edit->emitter_field);
00165         edit->emitter_field= 0;
00166     }
00167 
00168     psys_free_path_cache(edit->psys, edit);
00169 
00170     MEM_freeN(edit);
00171 }
00172 
00173 /************************************************/
00174 /*          Edit Mode Helpers                   */
00175 /************************************************/
00176 
00177 int PE_start_edit(PTCacheEdit *edit)
00178 {
00179     if(edit) {
00180         edit->edited = 1;
00181         if(edit->psys)
00182             edit->psys->flag |= PSYS_EDITED;
00183         return 1;
00184     }
00185 
00186     return 0;
00187 }
00188 
00189 ParticleEditSettings *PE_settings(Scene *scene)
00190 {
00191     return scene->toolsettings ? &scene->toolsettings->particle : NULL;
00192 }
00193 
00194 /* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set
00195  *
00196  * note: this function runs on poll, therefor it can runs many times a second
00197  * keep it fast! */
00198 static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
00199 {
00200     ParticleEditSettings *pset= PE_settings(scene);
00201     PTCacheEdit *edit = NULL;
00202     ListBase pidlist;
00203     PTCacheID *pid;
00204 
00205     if(pset==NULL || ob==NULL)
00206         return NULL;
00207 
00208     pset->scene = scene;
00209     pset->object = ob;
00210 
00211     BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
00212 
00213     /* in the case of only one editable thing, set pset->edittype accordingly */
00214     if(pidlist.first && pidlist.first == pidlist.last) {
00215         pid = pidlist.first;
00216         switch(pid->type) {
00217             case PTCACHE_TYPE_PARTICLES:
00218                 pset->edittype = PE_TYPE_PARTICLES;
00219                 break;
00220             case PTCACHE_TYPE_SOFTBODY:
00221                 pset->edittype = PE_TYPE_SOFTBODY;
00222                 break;
00223             case PTCACHE_TYPE_CLOTH:
00224                 pset->edittype = PE_TYPE_CLOTH;
00225                 break;
00226         }
00227     }
00228 
00229     for(pid=pidlist.first; pid; pid=pid->next) {
00230         if(pset->edittype == PE_TYPE_PARTICLES && pid->type == PTCACHE_TYPE_PARTICLES) {
00231             ParticleSystem *psys = pid->calldata;
00232 
00233             if(psys->flag & PSYS_CURRENT) {
00234                 if(psys->part && psys->part->type == PART_HAIR) {
00235                     if(psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
00236                         if(create && !psys->pointcache->edit)
00237                             PE_create_particle_edit(scene, ob, pid->cache, NULL);
00238                         edit = pid->cache->edit;
00239                     }
00240                     else {
00241                         if(create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
00242                             PE_create_particle_edit(scene, ob, NULL, psys);
00243                         edit = psys->edit;
00244                     }
00245                 }
00246                 else {
00247                     if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
00248                         PE_create_particle_edit(scene, ob, pid->cache, psys);
00249                     edit = pid->cache->edit;
00250                 }
00251 
00252                 break;
00253             }
00254         }
00255         else if(pset->edittype == PE_TYPE_SOFTBODY && pid->type == PTCACHE_TYPE_SOFTBODY) {
00256             if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
00257             {
00258                 pset->flag |= PE_FADE_TIME;
00259                 // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
00260                 PE_create_particle_edit(scene, ob, pid->cache, NULL);
00261             }
00262             edit = pid->cache->edit;
00263             break;
00264         }
00265         else if(pset->edittype == PE_TYPE_CLOTH && pid->type == PTCACHE_TYPE_CLOTH) {
00266             if(create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
00267             {
00268                 pset->flag |= PE_FADE_TIME;
00269                 // NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
00270                 PE_create_particle_edit(scene, ob, pid->cache, NULL);
00271             }
00272             edit = pid->cache->edit;
00273             break;
00274         }
00275     }
00276 
00277     if(edit)
00278         edit->pid = *pid;
00279 
00280     BLI_freelistN(&pidlist);
00281 
00282     return edit;
00283 }
00284 
00285 PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
00286 {
00287     return pe_get_current(scene, ob, 0);
00288 }
00289 
00290 PTCacheEdit *PE_create_current(Scene *scene, Object *ob)
00291 {
00292     return pe_get_current(scene, ob, 1);
00293 }
00294 
00295 void PE_current_changed(Scene *scene, Object *ob)
00296 {
00297     if(ob->mode == OB_MODE_PARTICLE_EDIT)
00298         PE_create_current(scene, ob);
00299 }
00300 
00301 void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
00302 {
00303     ParticleEditSettings *pset=PE_settings(scene);
00304     POINT_P; KEY_K;
00305 
00306 
00307     if(pset->flag & PE_FADE_TIME && pset->selectmode==SCE_SELECT_POINT) {
00308         LOOP_POINTS {
00309             LOOP_KEYS {
00310                 if(fabs(cfra-*key->time) < pset->fade_frames)
00311                     key->flag &= ~PEK_HIDE;
00312                 else {
00313                     key->flag |= PEK_HIDE;
00314                     //key->flag &= ~PEK_SELECT;
00315                 }
00316             }
00317         }
00318     }
00319     else {
00320         LOOP_POINTS {
00321             LOOP_KEYS {
00322                 key->flag &= ~PEK_HIDE;
00323             }
00324         }
00325     }
00326 }
00327 
00328 static int pe_x_mirror(Object *ob)
00329 {
00330     if(ob->type == OB_MESH)
00331         return (((Mesh*)ob->data)->editflag & ME_EDIT_MIRROR_X);
00332     
00333     return 0;
00334 }
00335 
00336 /****************** common struct passed to callbacks ******************/
00337 
00338 typedef struct PEData {
00339     ViewContext vc;
00340     bglMats mats;
00341     
00342     Scene *scene;
00343     Object *ob;
00344     DerivedMesh *dm;
00345     PTCacheEdit *edit;
00346 
00347     const int *mval;
00348     rcti *rect;
00349     float rad;
00350     float dist;
00351     float dval;
00352     int select;
00353 
00354     float *dvec;
00355     float combfac;
00356     float pufffac;
00357     float cutfac;
00358     float smoothfac;
00359     float weightfac;
00360     float growfac;
00361     int totrekey;
00362 
00363     int invert;
00364     int tot;
00365     float vec[3];
00366 } PEData;
00367 
00368 static void PE_set_data(bContext *C, PEData *data)
00369 {
00370     memset(data, 0, sizeof(*data));
00371 
00372     data->scene= CTX_data_scene(C);
00373     data->ob= CTX_data_active_object(C);
00374     data->edit= PE_get_current(data->scene, data->ob);
00375 }
00376 
00377 static void PE_set_view3d_data(bContext *C, PEData *data)
00378 {
00379     PE_set_data(C, data);
00380 
00381     view3d_set_viewcontext(C, &data->vc);
00382     /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather than (obmat * viewmat) */
00383     view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
00384 
00385     if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT)) {
00386         if(data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
00387             /* needed or else the draw matrix can be incorrect */
00388             view3d_operator_needs_opengl(C);
00389 
00390             view3d_validate_backbuf(&data->vc);
00391             /* we may need to force an update here by setting the rv3d as dirty
00392              * for now it seems ok, but take care!:
00393              * rv3d->depths->dirty = 1; */
00394             ED_view3d_depth_update(data->vc.ar);
00395         }
00396     }
00397 }
00398 
00399 /*************************** selection utilities *******************************/
00400 
00401 static int key_test_depth(PEData *data, const float co[3])
00402 {
00403     View3D *v3d= data->vc.v3d;
00404     double ux, uy, uz;
00405     float depth;
00406     short wco[3], x,y;
00407 
00408     /* nothing to do */
00409     if((v3d->drawtype<=OB_WIRE) || (v3d->flag & V3D_ZBUF_SELECT)==0)
00410         return 1;
00411 
00412     project_short(data->vc.ar, co, wco);
00413     
00414     if(wco[0] == IS_CLIPPED)
00415         return 0;
00416 
00417     gluProject(co[0],co[1],co[2], data->mats.modelview, data->mats.projection,
00418                (GLint *)data->mats.viewport, &ux, &uy, &uz);
00419 
00420     x=wco[0];
00421     y=wco[1];
00422 
00423 #if 0 /* works well but too slow on some systems [#23118] */
00424     x+= (short)data->vc.ar->winrct.xmin;
00425     y+= (short)data->vc.ar->winrct.ymin;
00426 
00427     /* PE_set_view3d_data calls this. no need to call here */
00428     /* view3d_validate_backbuf(&data->vc); */
00429     glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
00430 #else /* faster to use depths, these are calculated in PE_set_view3d_data */
00431     {
00432         ViewDepths *vd = data->vc.rv3d->depths;
00433         assert(vd && vd->depths);
00434         /* we know its not clipped */
00435         depth= vd->depths[y * vd->w + x];
00436     }
00437 #endif
00438 
00439     if((float)uz - 0.00001f > depth)
00440         return 0;
00441     else
00442         return 1;
00443 }
00444 
00445 static int key_inside_circle(PEData *data, float rad, const float co[3], float *distance)
00446 {
00447     float dx, dy, dist;
00448     int sco[2];
00449 
00450     project_int(data->vc.ar, co, sco);
00451     
00452     if(sco[0] == IS_CLIPPED)
00453         return 0;
00454     
00455     dx= data->mval[0] - sco[0];
00456     dy= data->mval[1] - sco[1];
00457     dist= sqrt(dx*dx + dy*dy);
00458 
00459     if(dist > rad)
00460         return 0;
00461 
00462     if(key_test_depth(data, co)) {
00463         if(distance)
00464             *distance=dist;
00465 
00466         return 1;
00467     }
00468     
00469     return 0;
00470 }
00471 
00472 static int key_inside_rect(PEData *data, const float co[3])
00473 {
00474     int sco[2];
00475 
00476     project_int(data->vc.ar, co,sco);
00477 
00478     if(sco[0] == IS_CLIPPED)
00479         return 0;
00480     
00481     if(sco[0] > data->rect->xmin && sco[0] < data->rect->xmax &&
00482        sco[1] > data->rect->ymin && sco[1] < data->rect->ymax)
00483         return key_test_depth(data, co);
00484 
00485     return 0;
00486 }
00487 
00488 static int key_inside_test(PEData *data, const float co[3])
00489 {
00490     if(data->mval)
00491         return key_inside_circle(data, data->rad, co, NULL);
00492     else
00493         return key_inside_rect(data, co);
00494 }
00495 
00496 static int point_is_selected(PTCacheEditPoint *point)
00497 {
00498     KEY_K;
00499 
00500     if(point->flag & PEP_HIDE)
00501         return 0;
00502 
00503     LOOP_SELECTED_KEYS {
00504         return 1;
00505     }
00506     
00507     return 0;
00508 }
00509 
00510 /*************************** iterators *******************************/
00511 
00512 typedef void (*ForPointFunc)(PEData *data, int point_index);
00513 typedef void (*ForKeyFunc)(PEData *data, int point_index, int key_index);
00514 typedef void (*ForKeyMatFunc)(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key);
00515 
00516 static void for_mouse_hit_keys(PEData *data, ForKeyFunc func, int nearest)
00517 {
00518     ParticleEditSettings *pset= PE_settings(data->scene);
00519     PTCacheEdit *edit= data->edit;
00520     POINT_P; KEY_K;
00521     int nearest_point, nearest_key;
00522     float dist= data->rad;
00523 
00524     /* in path select mode we have no keys */
00525     if(pset->selectmode==SCE_SELECT_PATH)
00526         return;
00527 
00528     nearest_point= -1;
00529     nearest_key= -1;
00530 
00531     LOOP_VISIBLE_POINTS {
00532         if(pset->selectmode == SCE_SELECT_END) {
00533             /* only do end keys */
00534             key= point->keys + point->totkey-1;
00535 
00536             if(nearest) {
00537                 if(key_inside_circle(data, dist, KEY_WCO, &dist)) {
00538                     nearest_point= p;
00539                     nearest_key= point->totkey-1;
00540                 }
00541             }
00542             else if(key_inside_test(data, KEY_WCO))
00543                 func(data, p, point->totkey-1);
00544         }
00545         else {
00546             /* do all keys */
00547             LOOP_VISIBLE_KEYS {
00548                 if(nearest) {
00549                     if(key_inside_circle(data, dist, KEY_WCO, &dist)) {
00550                         nearest_point= p;
00551                         nearest_key= k;
00552                     }
00553                 }
00554                 else if(key_inside_test(data, KEY_WCO))
00555                     func(data, p, k);
00556             }
00557         }
00558     }
00559 
00560     /* do nearest only */
00561     if(nearest && nearest_point > -1)
00562         func(data, nearest_point, nearest_key);
00563 }
00564 
00565 static void foreach_mouse_hit_point(PEData *data, ForPointFunc func, int selected)
00566 {
00567     ParticleEditSettings *pset= PE_settings(data->scene);
00568     PTCacheEdit *edit= data->edit;
00569     POINT_P; KEY_K;
00570 
00571     /* all is selected in path mode */
00572     if(pset->selectmode==SCE_SELECT_PATH)
00573         selected=0;
00574 
00575     LOOP_VISIBLE_POINTS {
00576         if(pset->selectmode==SCE_SELECT_END) {
00577             /* only do end keys */
00578             key= point->keys + point->totkey - 1;
00579 
00580             if(selected==0 || key->flag & PEK_SELECT)
00581                 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
00582                     func(data, p);
00583         }
00584         else {
00585             /* do all keys */
00586             LOOP_VISIBLE_KEYS {
00587                 if(selected==0 || key->flag & PEK_SELECT) {
00588                     if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
00589                         func(data, p);
00590                         break;
00591                     }
00592                 }
00593             }
00594         }
00595     }
00596 }
00597 
00598 static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected)
00599 {
00600     PTCacheEdit *edit = data->edit;
00601     ParticleSystem *psys = edit->psys;
00602     ParticleSystemModifierData *psmd = NULL;
00603     ParticleEditSettings *pset= PE_settings(data->scene);
00604     POINT_P; KEY_K;
00605     float mat[4][4]= MAT4_UNITY, imat[4][4]= MAT4_UNITY;
00606 
00607     if(edit->psys)
00608         psmd= psys_get_modifier(data->ob, edit->psys);
00609 
00610     /* all is selected in path mode */
00611     if(pset->selectmode==SCE_SELECT_PATH)
00612         selected= 0;
00613 
00614     LOOP_VISIBLE_POINTS {
00615         if(pset->selectmode==SCE_SELECT_END) {
00616             /* only do end keys */
00617             key= point->keys + point->totkey-1;
00618 
00619             if(selected==0 || key->flag & PEK_SELECT) {
00620                 if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
00621                     if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
00622                         psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
00623                         invert_m4_m4(imat,mat);
00624                     }
00625 
00626                     func(data, mat, imat, p, point->totkey-1, key);
00627                 }
00628             }
00629         }
00630         else {
00631             /* do all keys */
00632             LOOP_VISIBLE_KEYS {
00633                 if(selected==0 || key->flag & PEK_SELECT) {
00634                     if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
00635                         if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
00636                             psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
00637                             invert_m4_m4(imat,mat);
00638                         }
00639 
00640                         func(data, mat, imat, p, k, key);
00641                     }
00642                 }
00643             }
00644         }
00645     }
00646 }
00647 
00648 static void foreach_selected_point(PEData *data, ForPointFunc func)
00649 {
00650     PTCacheEdit *edit = data->edit;
00651     POINT_P;
00652 
00653     LOOP_SELECTED_POINTS {
00654         func(data, p);
00655     }
00656 }
00657 
00658 static void foreach_selected_key(PEData *data, ForKeyFunc func)
00659 {
00660     PTCacheEdit *edit = data->edit;
00661     POINT_P; KEY_K;
00662 
00663     LOOP_VISIBLE_POINTS {
00664         LOOP_SELECTED_KEYS {
00665             func(data, p, k);
00666         }
00667     }
00668 }
00669 
00670 static void foreach_point(PEData *data, ForPointFunc func)
00671 {
00672     PTCacheEdit *edit = data->edit;
00673     POINT_P;
00674 
00675     LOOP_POINTS { 
00676         func(data, p);
00677     }
00678 }
00679 
00680 static int count_selected_keys(Scene *scene, PTCacheEdit *edit)
00681 {
00682     ParticleEditSettings *pset= PE_settings(scene);
00683     POINT_P; KEY_K;
00684     int sel= 0;
00685 
00686     LOOP_VISIBLE_POINTS {
00687         if(pset->selectmode==SCE_SELECT_POINT) {
00688             LOOP_SELECTED_KEYS {
00689                 sel++;
00690             }
00691         }
00692         else if(pset->selectmode==SCE_SELECT_END) {
00693             key = point->keys + point->totkey - 1;
00694             if(key->flag & PEK_SELECT)
00695                 sel++;
00696         }
00697     }
00698 
00699     return sel;
00700 }
00701 
00702 /************************************************/
00703 /*          Particle Edit Mirroring             */
00704 /************************************************/
00705 
00706 static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys)
00707 {
00708     PTCacheEdit *edit;
00709     ParticleSystemModifierData *psmd;
00710     KDTree *tree;
00711     KDTreeNearest nearest;
00712     HairKey *key;
00713     PARTICLE_P;
00714     float mat[4][4], co[3];
00715     int index, totpart;
00716 
00717     edit= psys->edit;
00718     psmd= psys_get_modifier(ob, psys);
00719     totpart= psys->totpart;
00720 
00721     if(!psmd->dm)
00722         return;
00723 
00724     tree= BLI_kdtree_new(totpart);
00725 
00726     /* insert particles into kd tree */
00727     LOOP_PARTICLES {
00728         key = pa->hair;
00729         psys_mat_hair_to_orco(ob, psmd->dm, psys->part->from, pa, mat);
00730         copy_v3_v3(co, key->co);
00731         mul_m4_v3(mat, co);
00732         BLI_kdtree_insert(tree, p, co, NULL);
00733     }
00734 
00735     BLI_kdtree_balance(tree);
00736 
00737     /* lookup particles and set in mirror cache */
00738     if(!edit->mirror_cache)
00739         edit->mirror_cache= MEM_callocN(sizeof(int)*totpart, "PE mirror cache");
00740     
00741     LOOP_PARTICLES {
00742         key = pa->hair;
00743         psys_mat_hair_to_orco(ob, psmd->dm, psys->part->from, pa, mat);
00744         copy_v3_v3(co, key->co);
00745         mul_m4_v3(mat, co);
00746         co[0]= -co[0];
00747 
00748         index= BLI_kdtree_find_nearest(tree, co, NULL, &nearest);
00749 
00750         /* this needs a custom threshold still, duplicated for editmode mirror */
00751         if(index != -1 && index != p && (nearest.dist <= 0.0002f))
00752             edit->mirror_cache[p]= index;
00753         else
00754             edit->mirror_cache[p]= -1;
00755     }
00756 
00757     /* make sure mirrors are in two directions */
00758     LOOP_PARTICLES {
00759         if(edit->mirror_cache[p]) {
00760             index= edit->mirror_cache[p];
00761             if(edit->mirror_cache[index] != p)
00762                 edit->mirror_cache[p]= -1;
00763         }
00764     }
00765 
00766     BLI_kdtree_free(tree);
00767 }
00768 
00769 static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys, ParticleData *pa, ParticleData *mpa)
00770 {
00771     HairKey *hkey, *mhkey;
00772     PTCacheEditPoint *point, *mpoint;
00773     PTCacheEditKey *key, *mkey;
00774     PTCacheEdit *edit;
00775     float mat[4][4], mmat[4][4], immat[4][4];
00776     int i, mi, k;
00777 
00778     edit= psys->edit;
00779     i= pa - psys->particles;
00780 
00781     /* find mirrored particle if needed */
00782     if(!mpa) {
00783         if(!edit->mirror_cache)
00784             PE_update_mirror_cache(ob, psys);
00785         
00786         if(!edit->mirror_cache)
00787             return; /* something went wrong! */
00788 
00789         mi= edit->mirror_cache[i];
00790         if(mi == -1)
00791             return;
00792         mpa= psys->particles + mi;
00793     }
00794     else
00795         mi= mpa - psys->particles;
00796 
00797     point = edit->points + i;
00798     mpoint = edit->points + mi;
00799 
00800     /* make sure they have the same amount of keys */
00801     if(pa->totkey != mpa->totkey) {
00802         if(mpa->hair) MEM_freeN(mpa->hair);
00803         if(mpoint->keys) MEM_freeN(mpoint->keys);
00804 
00805         mpa->hair= MEM_dupallocN(pa->hair);
00806         mpa->totkey= pa->totkey;
00807         mpoint->keys= MEM_dupallocN(point->keys);
00808         mpoint->totkey= point->totkey;
00809 
00810         mhkey= mpa->hair;
00811         mkey= mpoint->keys;
00812         for(k=0; k<mpa->totkey; k++, mkey++, mhkey++) {
00813             mkey->co= mhkey->co;
00814             mkey->time= &mhkey->time;
00815             mkey->flag &= ~PEK_SELECT;
00816         }
00817     }
00818 
00819     /* mirror positions and tags */
00820     psys_mat_hair_to_orco(ob, dm, psys->part->from, pa, mat);
00821     psys_mat_hair_to_orco(ob, dm, psys->part->from, mpa, mmat);
00822     invert_m4_m4(immat, mmat);
00823 
00824     hkey=pa->hair;
00825     mhkey=mpa->hair;
00826     key= point->keys;
00827     mkey= mpoint->keys;
00828     for(k=0; k<pa->totkey; k++, hkey++, mhkey++, key++, mkey++) {
00829         copy_v3_v3(mhkey->co, hkey->co);
00830         mul_m4_v3(mat, mhkey->co);
00831         mhkey->co[0]= -mhkey->co[0];
00832         mul_m4_v3(immat, mhkey->co);
00833 
00834         if(key->flag & PEK_TAG)
00835             mkey->flag |= PEK_TAG;
00836 
00837         mkey->length = key->length;
00838     }
00839 
00840     if(point->flag & PEP_TAG)
00841         mpoint->flag |= PEP_TAG;
00842     if(point->flag & PEP_EDIT_RECALC)
00843         mpoint->flag |= PEP_EDIT_RECALC;
00844 }
00845 
00846 static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
00847 {
00848     PTCacheEdit *edit;
00849     ParticleSystemModifierData *psmd;
00850     POINT_P;
00851 
00852     if(!psys)
00853         return;
00854 
00855     edit= psys->edit;
00856     psmd= psys_get_modifier(ob, psys);
00857 
00858     if(!psmd->dm)
00859         return;
00860 
00861     if(!edit->mirror_cache)
00862         PE_update_mirror_cache(ob, psys);
00863 
00864     if(!edit->mirror_cache)
00865         return; /* something went wrong */
00866 
00867     /* we delay settings the PARS_EDIT_RECALC for mirrored particles
00868      * to avoid doing mirror twice */
00869     LOOP_POINTS {
00870         if(point->flag & PEP_EDIT_RECALC) {
00871             PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
00872 
00873             if(edit->mirror_cache[p] != -1)
00874                 edit->points[edit->mirror_cache[p]].flag &= ~PEP_EDIT_RECALC;
00875         }
00876     }
00877 
00878     LOOP_POINTS {
00879         if(point->flag & PEP_EDIT_RECALC)
00880             if(edit->mirror_cache[p] != -1)
00881                 edit->points[edit->mirror_cache[p]].flag |= PEP_EDIT_RECALC;
00882     }
00883 }
00884 
00885 /************************************************/
00886 /*          Edit Calculation                    */
00887 /************************************************/
00888 /* tries to stop edited particles from going through the emitter's surface */
00889 static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
00890 {
00891     ParticleEditSettings *pset= PE_settings(scene);
00892     ParticleSystem *psys;
00893     ParticleSystemModifierData *psmd;
00894     POINT_P; KEY_K;
00895     int index;
00896     float *vec, *nor, dvec[3], dot, dist_1st=0.0f;
00897     float hairimat[4][4], hairmat[4][4];
00898 
00899     if(edit==NULL || edit->psys==NULL || (pset->flag & PE_DEFLECT_EMITTER)==0 || (edit->psys->flag & PSYS_GLOBAL_HAIR))
00900         return;
00901 
00902     psys = edit->psys;
00903     psmd = psys_get_modifier(ob,psys);
00904 
00905     if(!psmd->dm)
00906         return;
00907 
00908     LOOP_EDITED_POINTS {
00909         psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles + p, hairmat);
00910     
00911         LOOP_KEYS {
00912             mul_m4_v3(hairmat, key->co);
00913         }
00914 
00915         LOOP_KEYS {
00916             if(k==0) {
00917                 dist_1st = len_v3v3((key+1)->co, key->co);
00918                 dist_1st *= 0.75f * pset->emitterdist;
00919             }
00920             else {
00921                 index= BLI_kdtree_find_nearest(edit->emitter_field,key->co,NULL,NULL);
00922                 
00923                 vec=edit->emitter_cosnos +index*6;
00924                 nor=vec+3;
00925 
00926                 sub_v3_v3v3(dvec, key->co, vec);
00927 
00928                 dot=dot_v3v3(dvec,nor);
00929                 copy_v3_v3(dvec,nor);
00930 
00931                 if(dot>0.0f) {
00932                     if(dot<dist_1st) {
00933                         normalize_v3(dvec);
00934                         mul_v3_fl(dvec,dist_1st-dot);
00935                         add_v3_v3(key->co, dvec);
00936                     }
00937                 }
00938                 else {
00939                     normalize_v3(dvec);
00940                     mul_v3_fl(dvec,dist_1st-dot);
00941                     add_v3_v3(key->co, dvec);
00942                 }
00943                 if(k==1)
00944                     dist_1st*=1.3333f;
00945             }
00946         }
00947         
00948         invert_m4_m4(hairimat,hairmat);
00949 
00950         LOOP_KEYS {
00951             mul_m4_v3(hairimat, key->co);
00952         }
00953     }
00954 }
00955 /* force set distances between neighbouring keys */
00956 static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
00957 {
00958     
00959     ParticleEditSettings *pset=PE_settings(scene);
00960     POINT_P; KEY_K;
00961     float dv1[3];
00962 
00963     if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
00964         return;
00965 
00966     if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
00967         return;
00968 
00969     LOOP_EDITED_POINTS {
00970         LOOP_KEYS {
00971             if(k) {
00972                 sub_v3_v3v3(dv1, key->co, (key - 1)->co);
00973                 normalize_v3(dv1);
00974                 mul_v3_fl(dv1, (key - 1)->length);
00975                 add_v3_v3v3(key->co, (key - 1)->co, dv1);
00976             }
00977         }
00978     }
00979 }
00980 /* try to find a nice solution to keep distances between neighbouring keys */
00981 static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
00982 {
00983     ParticleEditSettings *pset=PE_settings(scene);
00984     POINT_P;
00985     PTCacheEditKey *key;
00986     int j, k;
00987     float tlen;
00988     float dv0[3]= {0.0f, 0.0f, 0.0f};
00989     float dv1[3]= {0.0f, 0.0f, 0.0f};
00990     float dv2[3]= {0.0f, 0.0f, 0.0f};
00991 
00992     if(edit==0 || (pset->flag & PE_KEEP_LENGTHS)==0)
00993         return;
00994 
00995     if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
00996         return;
00997 
00998     LOOP_EDITED_POINTS {
00999         for(j=1; j<point->totkey; j++) {
01000             float mul= 1.0f / (float)point->totkey;
01001 
01002             if(pset->flag & PE_LOCK_FIRST) {
01003                 key= point->keys + 1;
01004                 k= 1;
01005                 dv1[0]= dv1[1]= dv1[2]= 0.0;
01006             }
01007             else {
01008                 key= point->keys;
01009                 k= 0;
01010                 dv0[0]= dv0[1]= dv0[2]= 0.0;
01011             }
01012 
01013             for(; k<point->totkey; k++, key++) {
01014                 if(k) {
01015                     sub_v3_v3v3(dv0, (key - 1)->co, key->co);
01016                     tlen= normalize_v3(dv0);
01017                     mul_v3_fl(dv0, (mul * (tlen - (key - 1)->length)));
01018                 }
01019 
01020                 if(k < point->totkey - 1) {
01021                     sub_v3_v3v3(dv2, (key + 1)->co, key->co);
01022                     tlen= normalize_v3(dv2);
01023                     mul_v3_fl(dv2, mul * (tlen - key->length));
01024                 }
01025 
01026                 if(k) {
01027                     add_v3_v3((key-1)->co, dv1);
01028                 }
01029 
01030                 add_v3_v3v3(dv1, dv0, dv2);
01031             }
01032         }
01033     }
01034 }
01035 /* set current distances to be kept between neighbouting keys */
01036 static void recalc_lengths(PTCacheEdit *edit)
01037 {
01038     POINT_P; KEY_K;
01039 
01040     if(edit==0)
01041         return;
01042 
01043     LOOP_EDITED_POINTS {
01044         key= point->keys;
01045         for(k=0; k<point->totkey-1; k++, key++) {
01046             key->length= len_v3v3(key->co, (key + 1)->co);
01047         }
01048     }
01049 }
01050 
01051 /* calculate a tree for finding nearest emitter's vertice */
01052 static void recalc_emitter_field(Object *ob, ParticleSystem *psys)
01053 {
01054     DerivedMesh *dm=psys_get_modifier(ob,psys)->dm;
01055     PTCacheEdit *edit= psys->edit;
01056     float *vec, *nor;
01057     int i, totface /*, totvert*/;
01058 
01059     if(!dm)
01060         return;
01061 
01062     if(edit->emitter_cosnos)
01063         MEM_freeN(edit->emitter_cosnos);
01064 
01065     BLI_kdtree_free(edit->emitter_field);
01066 
01067     totface=dm->getNumFaces(dm);
01068     /*totvert=dm->getNumVerts(dm);*/ /*UNSUED*/
01069 
01070     edit->emitter_cosnos=MEM_callocN(totface*6*sizeof(float),"emitter cosnos");
01071 
01072     edit->emitter_field= BLI_kdtree_new(totface);
01073 
01074     vec=edit->emitter_cosnos;
01075     nor=vec+3;
01076 
01077     for(i=0; i<totface; i++, vec+=6, nor+=6) {
01078         MFace *mface=dm->getFaceData(dm,i,CD_MFACE);
01079         MVert *mvert;
01080 
01081         mvert=dm->getVertData(dm,mface->v1,CD_MVERT);
01082         copy_v3_v3(vec,mvert->co);
01083         VECCOPY(nor,mvert->no);
01084 
01085         mvert=dm->getVertData(dm,mface->v2,CD_MVERT);
01086         add_v3_v3v3(vec,vec,mvert->co);
01087         VECADD(nor,nor,mvert->no);
01088 
01089         mvert=dm->getVertData(dm,mface->v3,CD_MVERT);
01090         add_v3_v3v3(vec,vec,mvert->co);
01091         VECADD(nor,nor,mvert->no);
01092 
01093         if(mface->v4) {
01094             mvert=dm->getVertData(dm,mface->v4,CD_MVERT);
01095             add_v3_v3v3(vec,vec,mvert->co);
01096             VECADD(nor,nor,mvert->no);
01097             
01098             mul_v3_fl(vec,0.25);
01099         }
01100         else
01101             mul_v3_fl(vec,0.3333f);
01102 
01103         normalize_v3(nor);
01104 
01105         BLI_kdtree_insert(edit->emitter_field, i, vec, NULL);
01106     }
01107 
01108     BLI_kdtree_balance(edit->emitter_field);
01109 }
01110 
01111 static void PE_update_selection(Scene *scene, Object *ob, int useflag)
01112 {
01113     PTCacheEdit *edit= PE_get_current(scene, ob);
01114     HairKey *hkey;
01115     POINT_P; KEY_K;
01116 
01117     /* flag all particles to be updated if not using flag */
01118     if(!useflag)
01119         LOOP_POINTS
01120             point->flag |= PEP_EDIT_RECALC;
01121 
01122     /* flush edit key flag to hair key flag to preserve selection 
01123      * on save */
01124     if(edit->psys) LOOP_POINTS {
01125         hkey = edit->psys->particles[p].hair;
01126         LOOP_KEYS {
01127             hkey->editflag= key->flag;
01128             hkey++;
01129         }
01130     }
01131 
01132     psys_cache_edit_paths(scene, ob, edit, CFRA);
01133 
01134 
01135     /* disable update flag */
01136     LOOP_POINTS
01137         point->flag &= ~PEP_EDIT_RECALC;
01138 }
01139 
01140 static void update_world_cos(Object *ob, PTCacheEdit *edit)
01141 {
01142     ParticleSystem *psys = edit->psys;
01143     ParticleSystemModifierData *psmd= psys_get_modifier(ob, psys);
01144     POINT_P; KEY_K;
01145     float hairmat[4][4];
01146 
01147     if(psys==0 || psys->edit==0 || psmd->dm==NULL)
01148         return;
01149 
01150     LOOP_POINTS {
01151         if(!(psys->flag & PSYS_GLOBAL_HAIR))
01152             psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles+p, hairmat);
01153 
01154         LOOP_KEYS {
01155             copy_v3_v3(key->world_co,key->co);
01156             if(!(psys->flag & PSYS_GLOBAL_HAIR))
01157                 mul_m4_v3(hairmat, key->world_co);
01158         }
01159     }
01160 }
01161 static void update_velocities(PTCacheEdit *edit)
01162 {
01163     /*TODO: get frs_sec properly */
01164     float vec1[3], vec2[3], frs_sec, dfra;
01165     POINT_P; KEY_K;
01166 
01167     /* hair doesn't use velocities */
01168     if(edit->psys || !edit->points || !edit->points->keys->vel)
01169         return;
01170 
01171     frs_sec = edit->pid.flag & PTCACHE_VEL_PER_SEC ? 25.0f : 1.0f;
01172 
01173     LOOP_EDITED_POINTS {
01174         LOOP_KEYS {
01175             if(k==0) {
01176                 dfra = *(key+1)->time - *key->time;
01177 
01178                 if(dfra <= 0.0f)
01179                     continue;
01180 
01181                 sub_v3_v3v3(key->vel, (key+1)->co, key->co);
01182 
01183                 if(point->totkey>2) {
01184                     sub_v3_v3v3(vec1, (key+1)->co, (key+2)->co);
01185                     project_v3_v3v3(vec2, vec1, key->vel);
01186                     sub_v3_v3v3(vec2, vec1, vec2);
01187                     madd_v3_v3fl(key->vel, vec2, 0.5f);
01188                 }
01189             }
01190             else if(k==point->totkey-1) {
01191                 dfra = *key->time - *(key-1)->time;
01192 
01193                 if(dfra <= 0.0f)
01194                     continue;
01195 
01196                 sub_v3_v3v3(key->vel, key->co, (key-1)->co);
01197 
01198                 if(point->totkey>2) {
01199                     sub_v3_v3v3(vec1, (key-2)->co, (key-1)->co);
01200                     project_v3_v3v3(vec2, vec1, key->vel);
01201                     sub_v3_v3v3(vec2, vec1, vec2);
01202                     madd_v3_v3fl(key->vel, vec2, 0.5f);
01203                 }
01204             }
01205             else {
01206                 dfra = *(key+1)->time - *(key-1)->time;
01207                 
01208                 if(dfra <= 0.0f)
01209                     continue;
01210 
01211                 sub_v3_v3v3(key->vel, (key+1)->co, (key-1)->co);
01212             }
01213             mul_v3_fl(key->vel, frs_sec/dfra);
01214         }
01215     }
01216 }
01217 
01218 void PE_update_object(Scene *scene, Object *ob, int useflag)
01219 {
01220     /* use this to do partial particle updates, not usable when adding or
01221        removing, then a full redo is necessary and calling this may crash */
01222     ParticleEditSettings *pset= PE_settings(scene);
01223     PTCacheEdit *edit = PE_get_current(scene, ob);
01224     POINT_P;
01225 
01226     if(!edit)
01227         return;
01228 
01229     /* flag all particles to be updated if not using flag */
01230     if(!useflag)
01231         LOOP_POINTS {
01232             point->flag |= PEP_EDIT_RECALC;
01233         }
01234 
01235     /* do post process on particle edit keys */
01236     pe_iterate_lengths(scene, edit);
01237     pe_deflect_emitter(scene, ob, edit);
01238     PE_apply_lengths(scene, edit);
01239     if(pe_x_mirror(ob))
01240         PE_apply_mirror(ob,edit->psys);
01241     if(edit->psys)
01242         update_world_cos(ob, edit);
01243     if(pset->flag & PE_AUTO_VELOCITY)
01244         update_velocities(edit);
01245     PE_hide_keys_time(scene, edit, CFRA);
01246 
01247     /* regenerate path caches */
01248     psys_cache_edit_paths(scene, ob, edit, CFRA);
01249 
01250     /* disable update flag */
01251     LOOP_POINTS {
01252         point->flag &= ~PEP_EDIT_RECALC;
01253     }
01254 
01255     if(edit->psys)
01256         edit->psys->flag &= ~PSYS_HAIR_UPDATED;
01257 }
01258 
01259 /************************************************/
01260 /*          Edit Selections                     */
01261 /************************************************/
01262 
01263 /*-----selection callbacks-----*/
01264 
01265 static void select_key(PEData *data, int point_index, int key_index)
01266 {
01267     PTCacheEdit *edit = data->edit;
01268     PTCacheEditPoint *point = edit->points + point_index;
01269     PTCacheEditKey *key = point->keys + key_index;
01270 
01271     if(data->select)
01272         key->flag |= PEK_SELECT;
01273     else
01274         key->flag &= ~PEK_SELECT;
01275 
01276     point->flag |= PEP_EDIT_RECALC;
01277 }
01278 
01279 static void select_keys(PEData *data, int point_index, int UNUSED(key_index))
01280 {
01281     PTCacheEdit *edit = data->edit;
01282     PTCacheEditPoint *point = edit->points + point_index;
01283     KEY_K;
01284 
01285     LOOP_KEYS {
01286         if(data->select)
01287             key->flag |= PEK_SELECT;
01288         else
01289             key->flag &= ~PEK_SELECT;
01290     }
01291 
01292     point->flag |= PEP_EDIT_RECALC;
01293 }
01294 
01295 static void toggle_key_select(PEData *data, int point_index, int key_index)
01296 {
01297     PTCacheEdit *edit = data->edit;
01298     PTCacheEditPoint *point = edit->points + point_index;
01299     PTCacheEditKey *key = point->keys + key_index;
01300 
01301     key->flag ^= PEK_SELECT;
01302     point->flag |= PEP_EDIT_RECALC;
01303 }
01304 
01305 /************************ de select all operator ************************/
01306 
01307 static int pe_select_all_exec(bContext *C, wmOperator *op)
01308 {
01309     Scene *scene= CTX_data_scene(C);
01310     Object *ob= CTX_data_active_object(C);
01311     PTCacheEdit *edit= PE_get_current(scene, ob);
01312     POINT_P; KEY_K;
01313     int action = RNA_enum_get(op->ptr, "action");
01314 
01315     if (action == SEL_TOGGLE) {
01316         action = SEL_SELECT;
01317         LOOP_VISIBLE_POINTS {
01318             LOOP_SELECTED_KEYS {
01319                 action = SEL_DESELECT;
01320                 break;
01321             }
01322 
01323             if (action == SEL_DESELECT)
01324                 break;
01325         }
01326     }
01327 
01328     LOOP_VISIBLE_POINTS {
01329         LOOP_VISIBLE_KEYS {
01330             switch (action) {
01331             case SEL_SELECT:
01332                 if ((key->flag & PEK_SELECT) == 0) {
01333                     key->flag |= PEK_SELECT;
01334                     point->flag |= PEP_EDIT_RECALC;
01335                 }
01336                 break;
01337             case SEL_DESELECT:
01338                 if (key->flag & PEK_SELECT) {
01339                     key->flag &= ~PEK_SELECT;
01340                     point->flag |= PEP_EDIT_RECALC;
01341                 }
01342                 break;
01343             case SEL_INVERT:
01344                 if ((key->flag & PEK_SELECT) == 0) {
01345                     key->flag |= PEK_SELECT;
01346                     point->flag |= PEP_EDIT_RECALC;
01347                 } else {
01348                     key->flag &= ~PEK_SELECT;
01349                     point->flag |= PEP_EDIT_RECALC;
01350                 }
01351                 break;
01352             }
01353         }
01354     }
01355 
01356     PE_update_selection(scene, ob, 1);
01357     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01358 
01359     return OPERATOR_FINISHED;
01360 }
01361 
01362 void PARTICLE_OT_select_all(wmOperatorType *ot)
01363 {
01364     /* identifiers */
01365     ot->name= "Selection of all particles";
01366     ot->idname= "PARTICLE_OT_select_all";
01367     
01368     /* api callbacks */
01369     ot->exec= pe_select_all_exec;
01370     ot->poll= PE_poll;
01371 
01372     /* flags */
01373     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01374 
01375     WM_operator_properties_select_all(ot);
01376 }
01377 
01378 /************************ pick select operator ************************/
01379 
01380 int PE_mouse_particles(bContext *C, const int mval[2], int extend)
01381 {
01382     PEData data;
01383     Scene *scene= CTX_data_scene(C);
01384     Object *ob= CTX_data_active_object(C);
01385     PTCacheEdit *edit= PE_get_current(scene, ob);
01386     POINT_P; KEY_K;
01387     
01388     if(!PE_start_edit(edit))
01389         return OPERATOR_CANCELLED;
01390 
01391     if(!extend) {
01392         LOOP_VISIBLE_POINTS {
01393             LOOP_SELECTED_KEYS {
01394                 key->flag &= ~PEK_SELECT;
01395                 point->flag |= PEP_EDIT_RECALC;
01396             }
01397         }
01398     }
01399 
01400     PE_set_view3d_data(C, &data);
01401     data.mval= mval;
01402     data.rad= 75.0f;
01403 
01404     for_mouse_hit_keys(&data, toggle_key_select, 1);  /* nearest only */
01405 
01406     PE_update_selection(scene, ob, 1);
01407     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01408 
01409     return OPERATOR_FINISHED;
01410 }
01411 
01412 /************************ select first operator ************************/
01413 
01414 static void select_root(PEData *data, int point_index)
01415 {
01416     if (data->edit->points[point_index].flag & PEP_HIDE)
01417         return;
01418     
01419     data->edit->points[point_index].keys->flag |= PEK_SELECT;
01420     data->edit->points[point_index].flag |= PEP_EDIT_RECALC; /* redraw selection only */
01421 }
01422 
01423 static int select_roots_exec(bContext *C, wmOperator *UNUSED(op))
01424 {
01425     PEData data;
01426 
01427     PE_set_data(C, &data);
01428     foreach_point(&data, select_root);
01429 
01430     PE_update_selection(data.scene, data.ob, 1);
01431     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01432 
01433     return OPERATOR_FINISHED;
01434 }
01435 
01436 void PARTICLE_OT_select_roots(wmOperatorType *ot)
01437 {
01438     /* identifiers */
01439     ot->name= "Select Roots";
01440     ot->idname= "PARTICLE_OT_select_roots";
01441     
01442     /* api callbacks */
01443     ot->exec= select_roots_exec;
01444     ot->poll= PE_poll;
01445 
01446     /* flags */
01447     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01448 }
01449 
01450 /************************ select last operator ************************/
01451 
01452 static void select_tip(PEData *data, int point_index)
01453 {
01454     PTCacheEditPoint *point = data->edit->points + point_index;
01455     
01456     if (point->flag & PEP_HIDE)
01457         return;
01458     
01459     point->keys[point->totkey - 1].flag |= PEK_SELECT;
01460     point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
01461 }
01462 
01463 static int select_tips_exec(bContext *C, wmOperator *UNUSED(op))
01464 {
01465     PEData data;
01466 
01467     PE_set_data(C, &data);
01468     foreach_point(&data, select_tip);
01469 
01470     PE_update_selection(data.scene, data.ob, 1);
01471     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01472 
01473     return OPERATOR_FINISHED;
01474 }
01475 
01476 void PARTICLE_OT_select_tips(wmOperatorType *ot)
01477 {
01478     /* identifiers */
01479     ot->name= "Select Tips";
01480     ot->idname= "PARTICLE_OT_select_tips";
01481     
01482     /* api callbacks */
01483     ot->exec= select_tips_exec;
01484     ot->poll= PE_poll;
01485 
01486     /* flags */
01487     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01488 }
01489 
01490 /************************ select linked operator ************************/
01491 
01492 static int select_linked_exec(bContext *C, wmOperator *op)
01493 {
01494     PEData data;
01495     int mval[2];
01496     int location[2];
01497 
01498     RNA_int_get_array(op->ptr, "location", location);
01499     mval[0]= location[0];
01500     mval[1]= location[1];
01501 
01502     PE_set_view3d_data(C, &data);
01503     data.mval= mval;
01504     data.rad=75.0f;
01505     data.select= !RNA_boolean_get(op->ptr, "deselect");
01506 
01507     for_mouse_hit_keys(&data, select_keys, 1);  /* nearest only */
01508     PE_update_selection(data.scene, data.ob, 1);
01509     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01510 
01511     return OPERATOR_FINISHED;
01512 }
01513 
01514 static int select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
01515 {
01516     RNA_int_set_array(op->ptr, "location", event->mval);
01517     return select_linked_exec(C, op);
01518 }
01519 
01520 void PARTICLE_OT_select_linked(wmOperatorType *ot)
01521 {
01522     /* identifiers */
01523     ot->name= "Select Linked";
01524     ot->idname= "PARTICLE_OT_select_linked";
01525     
01526     /* api callbacks */
01527     ot->exec= select_linked_exec;
01528     ot->invoke= select_linked_invoke;
01529     ot->poll= PE_poll_view3d;
01530 
01531     /* flags */
01532     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01533 
01534     /* properties */
01535     RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked keys rather than selecting them");
01536     RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
01537 }
01538 
01539 /************************ border select operator ************************/
01540 void PE_deselect_all_visible(PTCacheEdit *edit)
01541 {
01542     POINT_P; KEY_K;
01543 
01544     LOOP_VISIBLE_POINTS {
01545         LOOP_SELECTED_KEYS {
01546             key->flag &= ~PEK_SELECT;
01547             point->flag |= PEP_EDIT_RECALC;
01548         }
01549     }
01550 }
01551 
01552 int PE_border_select(bContext *C, rcti *rect, int select, int extend)
01553 {
01554     Scene *scene= CTX_data_scene(C);
01555     Object *ob= CTX_data_active_object(C);
01556     PTCacheEdit *edit= PE_get_current(scene, ob);
01557     PEData data;
01558 
01559     if(!PE_start_edit(edit))
01560         return OPERATOR_CANCELLED;
01561 
01562     if (extend == 0 && select)
01563         PE_deselect_all_visible(edit);
01564 
01565     PE_set_view3d_data(C, &data);
01566     data.rect= rect;
01567     data.select= select;
01568 
01569     for_mouse_hit_keys(&data, select_key, 0);
01570 
01571     PE_update_selection(scene, ob, 1);
01572     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01573 
01574     return OPERATOR_FINISHED;
01575 }
01576 
01577 /************************ circle select operator ************************/
01578 
01579 int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
01580 {
01581     Scene *scene= CTX_data_scene(C);
01582     Object *ob= CTX_data_active_object(C);
01583     PTCacheEdit *edit= PE_get_current(scene, ob);
01584     PEData data;
01585 
01586     if(!PE_start_edit(edit))
01587         return OPERATOR_FINISHED;
01588 
01589     PE_set_view3d_data(C, &data);
01590     data.mval= mval;
01591     data.rad= rad;
01592     data.select= selecting;
01593 
01594     for_mouse_hit_keys(&data, select_key, 0);
01595 
01596     PE_update_selection(scene, ob, 1);
01597     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01598 
01599     return OPERATOR_FINISHED;
01600 }
01601 
01602 /************************ lasso select operator ************************/
01603 
01604 int PE_lasso_select(bContext *C, int mcords[][2], short moves, short extend, short select)
01605 {
01606     Scene *scene= CTX_data_scene(C);
01607     Object *ob= CTX_data_active_object(C);
01608     ARegion *ar= CTX_wm_region(C);
01609     ParticleEditSettings *pset= PE_settings(scene);
01610     PTCacheEdit *edit = PE_get_current(scene, ob);
01611     ParticleSystem *psys = edit->psys;
01612     ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
01613     POINT_P; KEY_K;
01614     float co[3], mat[4][4]= MAT4_UNITY;
01615     int vertco[2];
01616 
01617     PEData data;
01618 
01619     if(!PE_start_edit(edit))
01620         return OPERATOR_CANCELLED;
01621 
01622     if (extend == 0 && select)
01623         PE_deselect_all_visible(edit);
01624 
01625     /* only for depths */
01626     PE_set_view3d_data(C, &data);
01627 
01628     LOOP_VISIBLE_POINTS {
01629         if(edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
01630             psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles + p, mat);
01631 
01632         if(pset->selectmode==SCE_SELECT_POINT) {
01633             LOOP_KEYS {
01634                 copy_v3_v3(co, key->co);
01635                 mul_m4_v3(mat, co);
01636                 project_int(ar, co, vertco);
01637                 if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1]) && key_test_depth(&data, co)) {
01638                     if(select && !(key->flag & PEK_SELECT)) {
01639                         key->flag |= PEK_SELECT;
01640                         point->flag |= PEP_EDIT_RECALC;
01641                     }
01642                     else if(key->flag & PEK_SELECT) {
01643                         key->flag &= ~PEK_SELECT;
01644                         point->flag |= PEP_EDIT_RECALC;
01645                     }
01646                 }
01647             }
01648         }
01649         else if(pset->selectmode==SCE_SELECT_END) {
01650             key= point->keys + point->totkey - 1;
01651 
01652             copy_v3_v3(co, key->co);
01653             mul_m4_v3(mat, co);
01654             project_int(ar, co,vertco);
01655             if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1]) && key_test_depth(&data, co)) {
01656                 if(select && !(key->flag & PEK_SELECT)) {
01657                     key->flag |= PEK_SELECT;
01658                     point->flag |= PEP_EDIT_RECALC;
01659                 }
01660                 else if(key->flag & PEK_SELECT) {
01661                     key->flag &= ~PEK_SELECT;
01662                     point->flag |= PEP_EDIT_RECALC;
01663                 }
01664             }
01665         }
01666     }
01667 
01668     PE_update_selection(scene, ob, 1);
01669     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01670 
01671     return OPERATOR_FINISHED;
01672 }
01673 
01674 /*************************** hide operator **************************/
01675 
01676 static int hide_exec(bContext *C, wmOperator *op)
01677 {
01678     Object *ob= CTX_data_active_object(C);
01679     Scene *scene= CTX_data_scene(C);
01680     PTCacheEdit *edit= PE_get_current(scene, ob);
01681     POINT_P; KEY_K;
01682     
01683     if(RNA_enum_get(op->ptr, "unselected")) {
01684         LOOP_UNSELECTED_POINTS {
01685             point->flag |= PEP_HIDE;
01686             point->flag |= PEP_EDIT_RECALC;
01687 
01688             LOOP_KEYS
01689                 key->flag &= ~PEK_SELECT;
01690         }
01691     }
01692     else {
01693         LOOP_SELECTED_POINTS {
01694             point->flag |= PEP_HIDE;
01695             point->flag |= PEP_EDIT_RECALC;
01696 
01697             LOOP_KEYS
01698                 key->flag &= ~PEK_SELECT;
01699         }
01700     }
01701 
01702     PE_update_selection(scene, ob, 1);
01703     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01704 
01705     return OPERATOR_FINISHED;
01706 }
01707 
01708 void PARTICLE_OT_hide(wmOperatorType *ot)
01709 {
01710     /* identifiers */
01711     ot->name= "Hide Selected";
01712     ot->idname= "PARTICLE_OT_hide";
01713     
01714     /* api callbacks */
01715     ot->exec= hide_exec;
01716     ot->poll= PE_poll;
01717 
01718     /* flags */
01719     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01720 
01721     /* props */
01722     RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
01723 }
01724 
01725 /*************************** reveal operator **************************/
01726 
01727 static int reveal_exec(bContext *C, wmOperator *UNUSED(op))
01728 {
01729     Object *ob= CTX_data_active_object(C);
01730     Scene *scene= CTX_data_scene(C);
01731     PTCacheEdit *edit= PE_get_current(scene, ob);
01732     POINT_P; KEY_K;
01733 
01734     LOOP_POINTS {
01735         if(point->flag & PEP_HIDE) {
01736             point->flag &= ~PEP_HIDE;
01737             point->flag |= PEP_EDIT_RECALC;
01738 
01739             LOOP_KEYS
01740                 key->flag |= PEK_SELECT;
01741         }
01742     }
01743 
01744     PE_update_selection(scene, ob, 1);
01745     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
01746 
01747     return OPERATOR_FINISHED;
01748 }
01749 
01750 void PARTICLE_OT_reveal(wmOperatorType *ot)
01751 {
01752     /* identifiers */
01753     ot->name= "Reveal";
01754     ot->idname= "PARTICLE_OT_reveal";
01755     
01756     /* api callbacks */
01757     ot->exec= reveal_exec;
01758     ot->poll= PE_poll;
01759 
01760     /* flags */
01761     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01762 }
01763 
01764 /************************ select less operator ************************/
01765 
01766 static void select_less_keys(PEData *data, int point_index)
01767 {
01768     PTCacheEdit *edit= data->edit;
01769     PTCacheEditPoint *point = edit->points + point_index;
01770     KEY_K;
01771 
01772     LOOP_SELECTED_KEYS {
01773         if(k==0) {
01774             if(((key+1)->flag&PEK_SELECT)==0)
01775                 key->flag |= PEK_TAG;
01776         }
01777         else if(k==point->totkey-1) {
01778             if(((key-1)->flag&PEK_SELECT)==0)
01779                 key->flag |= PEK_TAG;
01780         }
01781         else {
01782             if((((key-1)->flag & (key+1)->flag) & PEK_SELECT)==0)
01783                 key->flag |= PEK_TAG;
01784         }
01785     }
01786 
01787     LOOP_KEYS {
01788         if(key->flag&PEK_TAG) {
01789             key->flag &= ~(PEK_TAG|PEK_SELECT);
01790             point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
01791         }
01792     }
01793 }
01794 
01795 static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
01796 {
01797     PEData data;
01798 
01799     PE_set_data(C, &data);
01800     foreach_point(&data, select_less_keys);
01801 
01802     PE_update_selection(data.scene, data.ob, 1);
01803     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01804 
01805     return OPERATOR_FINISHED;
01806 }
01807 
01808 void PARTICLE_OT_select_less(wmOperatorType *ot)
01809 {
01810     /* identifiers */
01811     ot->name= "Select Less";
01812     ot->idname= "PARTICLE_OT_select_less";
01813     
01814     /* api callbacks */
01815     ot->exec= select_less_exec;
01816     ot->poll= PE_poll;
01817 
01818     /* flags */
01819     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01820 }
01821 
01822 /************************ select more operator ************************/
01823 
01824 static void select_more_keys(PEData *data, int point_index)
01825 {
01826     PTCacheEdit *edit= data->edit;
01827     PTCacheEditPoint *point = edit->points + point_index;
01828     KEY_K;
01829 
01830     LOOP_KEYS {
01831         if(key->flag & PEK_SELECT) continue;
01832 
01833         if(k==0) {
01834             if((key+1)->flag&PEK_SELECT)
01835                 key->flag |= PEK_TAG;
01836         }
01837         else if(k==point->totkey-1) {
01838             if((key-1)->flag&PEK_SELECT)
01839                 key->flag |= PEK_TAG;
01840         }
01841         else {
01842             if(((key-1)->flag | (key+1)->flag) & PEK_SELECT)
01843                 key->flag |= PEK_TAG;
01844         }
01845     }
01846 
01847     LOOP_KEYS {
01848         if(key->flag&PEK_TAG) {
01849             key->flag &= ~PEK_TAG;
01850             key->flag |= PEK_SELECT;
01851             point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
01852         }
01853     }
01854 }
01855 
01856 static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
01857 {
01858     PEData data;
01859 
01860     PE_set_data(C, &data);
01861     foreach_point(&data, select_more_keys);
01862 
01863     PE_update_selection(data.scene, data.ob, 1);
01864     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
01865 
01866     return OPERATOR_FINISHED;
01867 }
01868 
01869 void PARTICLE_OT_select_more(wmOperatorType *ot)
01870 {
01871     /* identifiers */
01872     ot->name= "Select More";
01873     ot->idname= "PARTICLE_OT_select_more";
01874     
01875     /* api callbacks */
01876     ot->exec= select_more_exec;
01877     ot->poll= PE_poll;
01878 
01879     /* flags */
01880     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01881 }
01882 
01883 /************************ rekey operator ************************/
01884 
01885 static void rekey_particle(PEData *data, int pa_index)
01886 {
01887     PTCacheEdit *edit= data->edit;
01888     ParticleSystem *psys= edit->psys;
01889     ParticleSimulationData sim= {0};
01890     ParticleData *pa= psys->particles + pa_index;
01891     PTCacheEditPoint *point = edit->points + pa_index;
01892     ParticleKey state;
01893     HairKey *key, *new_keys, *okey;
01894     PTCacheEditKey *ekey;
01895     float dval, sta, end;
01896     int k;
01897 
01898     sim.scene= data->scene;
01899     sim.ob= data->ob;
01900     sim.psys= edit->psys;
01901 
01902     pa->flag |= PARS_REKEY;
01903 
01904     key= new_keys= MEM_callocN(data->totrekey * sizeof(HairKey),"Hair re-key keys");
01905 
01906     okey = pa->hair;
01907     /* root and tip stay the same */
01908     copy_v3_v3(key->co, okey->co);
01909     copy_v3_v3((key + data->totrekey - 1)->co, (okey + pa->totkey - 1)->co);
01910 
01911     sta= key->time= okey->time;
01912     end= (key + data->totrekey - 1)->time= (okey + pa->totkey - 1)->time;
01913     dval= (end - sta) / (float)(data->totrekey - 1);
01914 
01915     /* interpolate new keys from old ones */
01916     for(k=1,key++; k<data->totrekey-1; k++,key++) {
01917         state.time= (float)k / (float)(data->totrekey-1);
01918         psys_get_particle_on_path(&sim, pa_index, &state, 0);
01919         copy_v3_v3(key->co, state.co);
01920         key->time= sta + k * dval;
01921     }
01922 
01923     /* replace keys */
01924     if(pa->hair)
01925         MEM_freeN(pa->hair);
01926     pa->hair= new_keys;
01927 
01928     point->totkey=pa->totkey=data->totrekey;
01929 
01930 
01931     if(point->keys)
01932         MEM_freeN(point->keys);
01933     ekey= point->keys= MEM_callocN(pa->totkey * sizeof(PTCacheEditKey),"Hair re-key edit keys");
01934         
01935     for(k=0, key=pa->hair; k<pa->totkey; k++, key++, ekey++) {
01936         ekey->co= key->co;
01937         ekey->time= &key->time;
01938         ekey->flag |= PEK_SELECT;
01939         if(!(psys->flag & PSYS_GLOBAL_HAIR))
01940             ekey->flag |= PEK_USE_WCO;
01941     }
01942 
01943     pa->flag &= ~PARS_REKEY;
01944     point->flag |= PEP_EDIT_RECALC;
01945 }
01946 
01947 static int rekey_exec(bContext *C, wmOperator *op)
01948 {
01949     PEData data;
01950 
01951     PE_set_data(C, &data);
01952 
01953     data.dval= 1.0f / (float)(data.totrekey-1);
01954     data.totrekey= RNA_int_get(op->ptr, "keys");
01955 
01956     foreach_selected_point(&data, rekey_particle);
01957     
01958     recalc_lengths(data.edit);
01959     PE_update_object(data.scene, data.ob, 1);
01960     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
01961 
01962     return OPERATOR_FINISHED;
01963 }
01964 
01965 void PARTICLE_OT_rekey(wmOperatorType *ot)
01966 {
01967     /* identifiers */
01968     ot->name= "Rekey";
01969     ot->idname= "PARTICLE_OT_rekey";
01970     
01971     /* api callbacks */
01972     ot->exec= rekey_exec;
01973     ot->invoke= WM_operator_props_popup;
01974     ot->poll= PE_poll;
01975 
01976     /* flags */
01977     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
01978 
01979     /* properties */
01980     RNA_def_int(ot->srna, "keys", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
01981 }
01982 
01983 static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float path_time)
01984 {
01985     PTCacheEdit *edit= PE_get_current(scene, ob);
01986     ParticleSystem *psys;
01987     ParticleSimulationData sim= {0};
01988     ParticleData *pa;
01989     ParticleKey state;
01990     HairKey *new_keys, *key;
01991     PTCacheEditKey *ekey;
01992     int k;
01993 
01994     if(!edit || !edit->psys) return;
01995 
01996     psys = edit->psys;
01997 
01998     sim.scene= scene;
01999     sim.ob= ob;
02000     sim.psys= psys;
02001 
02002     pa= psys->particles + pa_index;
02003 
02004     pa->flag |= PARS_REKEY;
02005 
02006     key= new_keys= MEM_dupallocN(pa->hair);
02007     
02008     /* interpolate new keys from old ones (roots stay the same) */
02009     for(k=1, key++; k < pa->totkey; k++, key++) {
02010         state.time= path_time * (float)k / (float)(pa->totkey-1);
02011         psys_get_particle_on_path(&sim, pa_index, &state, 0);
02012         copy_v3_v3(key->co, state.co);
02013     }
02014 
02015     /* replace hair keys */
02016     if(pa->hair)
02017         MEM_freeN(pa->hair);
02018     pa->hair= new_keys;
02019 
02020     /* update edit pointers */
02021     for(k=0, key=pa->hair, ekey=edit->points[pa_index].keys; k<pa->totkey; k++, key++, ekey++) {
02022         ekey->co= key->co;
02023         ekey->time= &key->time;
02024     }
02025 
02026     pa->flag &= ~PARS_REKEY;
02027 }
02028 
02029 /************************* utilities **************************/
02030 
02031 static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
02032 {
02033     PTCacheEdit *edit = psys->edit;
02034     ParticleData *pa, *npa=0, *new_pars=0;
02035     POINT_P;
02036     PTCacheEditPoint *npoint=0, *new_points=0;
02037     ParticleSystemModifierData *psmd;
02038     int i, new_totpart= psys->totpart, removed= 0;
02039 
02040     if(mirror) {
02041         /* mirror tags */
02042         psmd= psys_get_modifier(ob, psys);
02043 
02044         LOOP_TAGGED_POINTS {
02045             PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
02046         }
02047     }
02048 
02049     LOOP_TAGGED_POINTS {
02050         new_totpart--;
02051         removed++;
02052     }
02053 
02054     if(new_totpart != psys->totpart) {
02055         if(new_totpart) {
02056             npa= new_pars= MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
02057             npoint= new_points= MEM_callocN(new_totpart * sizeof(PTCacheEditPoint), "PTCacheEditKey array");
02058 
02059             if(ELEM(NULL, new_pars, new_points)) {
02060                  /* allocation error! */
02061                 if(new_pars)
02062                     MEM_freeN(new_pars);
02063                 if(new_points)
02064                     MEM_freeN(new_points);
02065                 return 0;
02066             }
02067         }
02068 
02069         pa= psys->particles;
02070         point= edit->points;
02071         for(i=0; i<psys->totpart; i++, pa++, point++) {
02072             if(point->flag & PEP_TAG) {
02073                 if(point->keys)
02074                     MEM_freeN(point->keys);
02075                 if(pa->hair)
02076                     MEM_freeN(pa->hair);
02077             }
02078             else {
02079                 memcpy(npa, pa, sizeof(ParticleData));
02080                 memcpy(npoint, point, sizeof(PTCacheEditPoint));
02081                 npa++;
02082                 npoint++;
02083             }
02084         }
02085 
02086         if(psys->particles) MEM_freeN(psys->particles);
02087         psys->particles= new_pars;
02088 
02089         if(edit->points) MEM_freeN(edit->points);
02090         edit->points= new_points;
02091 
02092         if(edit->mirror_cache) {
02093             MEM_freeN(edit->mirror_cache);
02094             edit->mirror_cache= NULL;
02095         }
02096 
02097         if(psys->child) {
02098             MEM_freeN(psys->child);
02099             psys->child= NULL;
02100             psys->totchild=0;
02101         }
02102 
02103         edit->totpoint= psys->totpart= new_totpart;
02104     }
02105 
02106     return removed;
02107 }
02108 
02109 static void remove_tagged_keys(Object *ob, ParticleSystem *psys)
02110 {
02111     PTCacheEdit *edit= psys->edit;
02112     ParticleData *pa;
02113     HairKey *hkey, *nhkey, *new_hkeys=0;
02114     POINT_P; KEY_K;
02115     PTCacheEditKey *nkey, *new_keys;
02116     ParticleSystemModifierData *psmd;
02117     short new_totkey;
02118 
02119     if(pe_x_mirror(ob)) {
02120         /* mirror key tags */
02121         psmd= psys_get_modifier(ob, psys);
02122 
02123         LOOP_POINTS {
02124             LOOP_TAGGED_KEYS {
02125                 PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
02126                 break;
02127             }
02128         }
02129     }
02130 
02131     LOOP_POINTS {
02132         new_totkey= point->totkey;
02133         LOOP_TAGGED_KEYS {
02134             new_totkey--;
02135         }
02136         /* we can't have elements with less than two keys*/
02137         if(new_totkey < 2)
02138             point->flag |= PEP_TAG;
02139     }
02140     remove_tagged_particles(ob, psys, pe_x_mirror(ob));
02141 
02142     LOOP_POINTS {
02143         pa = psys->particles + p;
02144         new_totkey= pa->totkey;
02145 
02146         LOOP_TAGGED_KEYS {
02147             new_totkey--;
02148         }
02149 
02150         if(new_totkey != pa->totkey) {
02151             nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
02152             nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
02153 
02154             hkey= pa->hair;
02155             LOOP_KEYS {
02156                 while(key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
02157                     key++;
02158                     hkey++;
02159                 }
02160 
02161                 if(hkey < pa->hair + pa->totkey) {
02162                     copy_v3_v3(nhkey->co, hkey->co);
02163                     nhkey->editflag = hkey->editflag;
02164                     nhkey->time= hkey->time;
02165                     nhkey->weight= hkey->weight;
02166                     
02167                     nkey->co= nhkey->co;
02168                     nkey->time= &nhkey->time;
02169                     /* these can be copied from old edit keys */
02170                     nkey->flag = key->flag;
02171                     nkey->ftime = key->ftime;
02172                     nkey->length = key->length;
02173                     copy_v3_v3(nkey->world_co, key->world_co);
02174                 }
02175                 nkey++;
02176                 nhkey++;
02177                 hkey++;
02178             }
02179 
02180             if(pa->hair)
02181                 MEM_freeN(pa->hair);
02182 
02183             if(point->keys)
02184                 MEM_freeN(point->keys);
02185             
02186             pa->hair= new_hkeys;
02187             point->keys= new_keys;
02188 
02189             point->totkey= pa->totkey= new_totkey;
02190 
02191             /* flag for recalculating length */
02192             point->flag |= PEP_EDIT_RECALC;
02193         }
02194     }
02195 }
02196 
02197 /************************ subdivide opertor *********************/
02198 
02199 /* works like normal edit mode subdivide, inserts keys between neighbouring selected keys */
02200 static void subdivide_particle(PEData *data, int pa_index)
02201 {
02202     PTCacheEdit *edit= data->edit;
02203     ParticleSystem *psys= edit->psys;
02204     ParticleSimulationData sim= {0};
02205     ParticleData *pa= psys->particles + pa_index;
02206     PTCacheEditPoint *point = edit->points + pa_index;
02207     ParticleKey state;
02208     HairKey *key, *nkey, *new_keys;
02209     PTCacheEditKey *ekey, *nekey, *new_ekeys;
02210 
02211     int k;
02212     short totnewkey=0;
02213     float endtime;
02214 
02215     sim.scene= data->scene;
02216     sim.ob= data->ob;
02217     sim.psys= edit->psys;
02218 
02219     for(k=0, ekey=point->keys; k<pa->totkey-1; k++,ekey++) {
02220         if(ekey->flag&PEK_SELECT && (ekey+1)->flag&PEK_SELECT)
02221             totnewkey++;
02222     }
02223 
02224     if(totnewkey==0) return;
02225 
02226     pa->flag |= PARS_REKEY;
02227 
02228     nkey= new_keys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(HairKey)),"Hair subdivide keys");
02229     nekey= new_ekeys= MEM_callocN((pa->totkey+totnewkey)*(sizeof(PTCacheEditKey)),"Hair subdivide edit keys");
02230     
02231     key = pa->hair;
02232     endtime= key[pa->totkey-1].time;
02233 
02234     for(k=0, ekey=point->keys; k<pa->totkey-1; k++, key++, ekey++) {
02235 
02236         memcpy(nkey,key,sizeof(HairKey));
02237         memcpy(nekey,ekey,sizeof(PTCacheEditKey));
02238 
02239         nekey->co= nkey->co;
02240         nekey->time= &nkey->time;
02241 
02242         nkey++;
02243         nekey++;
02244 
02245         if(ekey->flag & PEK_SELECT && (ekey+1)->flag & PEK_SELECT) {
02246             nkey->time= (key->time + (key+1)->time)*0.5f;
02247             state.time= (endtime != 0.0f)? nkey->time/endtime: 0.0f;
02248             psys_get_particle_on_path(&sim, pa_index, &state, 0);
02249             copy_v3_v3(nkey->co, state.co);
02250 
02251             nekey->co= nkey->co;
02252             nekey->time= &nkey->time;
02253             nekey->flag |= PEK_SELECT;
02254             if(!(psys->flag & PSYS_GLOBAL_HAIR))
02255                 nekey->flag |= PEK_USE_WCO;
02256 
02257             nekey++;
02258             nkey++;
02259         }
02260     }
02261     /*tip still not copied*/
02262     memcpy(nkey,key,sizeof(HairKey));
02263     memcpy(nekey,ekey,sizeof(PTCacheEditKey));
02264 
02265     nekey->co= nkey->co;
02266     nekey->time= &nkey->time;
02267 
02268     if(pa->hair)
02269         MEM_freeN(pa->hair);
02270     pa->hair= new_keys;
02271 
02272     if(point->keys)
02273         MEM_freeN(point->keys);
02274     point->keys= new_ekeys;
02275 
02276     point->totkey = pa->totkey = pa->totkey + totnewkey;
02277     point->flag |= PEP_EDIT_RECALC;
02278     pa->flag &= ~PARS_REKEY;
02279 }
02280 
02281 static int subdivide_exec(bContext *C, wmOperator *UNUSED(op))
02282 {
02283     PEData data;
02284 
02285     PE_set_data(C, &data);
02286     foreach_point(&data, subdivide_particle);
02287     
02288     recalc_lengths(data.edit);
02289     PE_update_object(data.scene, data.ob, 1);
02290     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
02291 
02292     return OPERATOR_FINISHED;
02293 }
02294 
02295 void PARTICLE_OT_subdivide(wmOperatorType *ot)
02296 {
02297     /* identifiers */
02298     ot->name= "Subdivide";
02299     ot->idname= "PARTICLE_OT_subdivide";
02300     
02301     /* api callbacks */
02302     ot->exec= subdivide_exec;
02303     ot->poll= PE_poll;
02304 
02305     /* flags */
02306     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02307 }
02308 
02309 /************************ remove doubles opertor *********************/
02310 
02311 static int remove_doubles_exec(bContext *C, wmOperator *op)
02312 {
02313     Scene *scene= CTX_data_scene(C);
02314     Object *ob= CTX_data_active_object(C);
02315     PTCacheEdit *edit= PE_get_current(scene, ob);
02316     ParticleSystem *psys = edit->psys;
02317     ParticleSystemModifierData *psmd;
02318     KDTree *tree;
02319     KDTreeNearest nearest[10];
02320     POINT_P;
02321     float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
02322     int n, totn, removed, totremoved;
02323 
02324     if(psys->flag & PSYS_GLOBAL_HAIR)
02325         return OPERATOR_CANCELLED;
02326 
02327     edit= psys->edit;
02328     psmd= psys_get_modifier(ob, psys);
02329     totremoved= 0;
02330 
02331     do {
02332         removed= 0;
02333 
02334         tree=BLI_kdtree_new(psys->totpart);
02335             
02336         /* insert particles into kd tree */
02337         LOOP_SELECTED_POINTS {
02338             psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles+p, mat);
02339             copy_v3_v3(co, point->keys->co);
02340             mul_m4_v3(mat, co);
02341             BLI_kdtree_insert(tree, p, co, NULL);
02342         }
02343 
02344         BLI_kdtree_balance(tree);
02345 
02346         /* tag particles to be removed */
02347         LOOP_SELECTED_POINTS {
02348             psys_mat_hair_to_object(ob, psmd->dm, psys->part->from, psys->particles+p, mat);
02349             copy_v3_v3(co, point->keys->co);
02350             mul_m4_v3(mat, co);
02351 
02352             totn= BLI_kdtree_find_n_nearest(tree,10,co,NULL,nearest);
02353 
02354             for(n=0; n<totn; n++) {
02355                 /* this needs a custom threshold still */
02356                 if(nearest[n].index > p && nearest[n].dist < threshold) {
02357                     if(!(point->flag & PEP_TAG)) {
02358                         point->flag |= PEP_TAG;
02359                         removed++;
02360                     }
02361                 }
02362             }
02363         }
02364 
02365         BLI_kdtree_free(tree);
02366 
02367         /* remove tagged particles - don't do mirror here! */
02368         remove_tagged_particles(ob, psys, 0);
02369         totremoved += removed;
02370     } while(removed);
02371 
02372     if(totremoved == 0)
02373         return OPERATOR_CANCELLED;
02374 
02375     BKE_reportf(op->reports, RPT_INFO, "Remove %d double particles", totremoved);
02376 
02377     DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
02378     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
02379 
02380     return OPERATOR_FINISHED;
02381 }
02382 
02383 void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
02384 {
02385     /* identifiers */
02386     ot->name= "Remove Doubles";
02387     ot->idname= "PARTICLE_OT_remove_doubles";
02388     
02389     /* api callbacks */
02390     ot->exec= remove_doubles_exec;
02391     ot->poll= PE_poll;
02392 
02393     /* flags */
02394     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02395 
02396     /* properties */
02397     RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX, "Threshold", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
02398 }
02399 
02400 
02401 static int weight_set_exec(bContext *C, wmOperator *op)
02402 {
02403     Scene *scene= CTX_data_scene(C);
02404     ParticleEditSettings *pset= PE_settings(scene);
02405     Object *ob= CTX_data_active_object(C);
02406     PTCacheEdit *edit= PE_get_current(scene, ob);
02407     ParticleSystem *psys = edit->psys;
02408     POINT_P;
02409     KEY_K;
02410     HairKey *hkey;
02411     float weight;
02412     ParticleBrushData *brush= &pset->brush[pset->brushtype];
02413     float factor= RNA_float_get(op->ptr, "factor");
02414 
02415     weight= brush->strength;
02416     edit= psys->edit;
02417 
02418     LOOP_SELECTED_POINTS {
02419         ParticleData *pa= psys->particles + p;
02420 
02421         LOOP_SELECTED_KEYS {
02422             hkey= pa->hair + k;
02423             hkey->weight= interpf(weight, hkey->weight, factor);
02424         }
02425     }
02426 
02427     DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
02428     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
02429 
02430     return OPERATOR_FINISHED;
02431 }
02432 
02433 void PARTICLE_OT_weight_set(wmOperatorType *ot)
02434 {
02435     /* identifiers */
02436     ot->name= "Weight Set";
02437     ot->idname= "PARTICLE_OT_weight_set";
02438 
02439     /* api callbacks */
02440     ot->exec= weight_set_exec;
02441     ot->poll= PE_poll;
02442 
02443     /* flags */
02444     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02445 
02446     RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor", "", 0, 1);
02447 }
02448 
02449 /************************ cursor drawing *******************************/
02450 
02451 static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
02452 {
02453     ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
02454     ParticleBrushData *brush;
02455 
02456     if(pset->brushtype < 0)
02457         return;
02458 
02459     brush= &pset->brush[pset->brushtype];
02460 
02461     if(brush) {
02462         glPushMatrix();
02463 
02464         glTranslatef((float)x, (float)y, 0.0f);
02465 
02466         glColor4ub(255, 255, 255, 128);
02467         glEnable(GL_LINE_SMOOTH );
02468         glEnable(GL_BLEND);
02469         glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
02470         glDisable(GL_BLEND);
02471         glDisable(GL_LINE_SMOOTH );
02472         
02473         glPopMatrix();
02474     }
02475 }
02476 
02477 static void toggle_particle_cursor(bContext *C, int enable)
02478 {
02479     ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
02480 
02481     if(pset->paintcursor && !enable) {
02482         WM_paint_cursor_end(CTX_wm_manager(C), pset->paintcursor);
02483         pset->paintcursor = NULL;
02484     }
02485     else if(enable)
02486         pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
02487 }
02488 
02489 /*************************** delete operator **************************/
02490 
02491 enum { DEL_PARTICLE, DEL_KEY };
02492 
02493 static EnumPropertyItem delete_type_items[]= {
02494     {DEL_PARTICLE, "PARTICLE", 0, "Particle", ""},
02495     {DEL_KEY, "KEY", 0, "Key", ""},
02496     {0, NULL, 0, NULL, NULL}};
02497 
02498 static void set_delete_particle(PEData *data, int pa_index)
02499 {
02500     PTCacheEdit *edit= data->edit;
02501 
02502     edit->points[pa_index].flag |= PEP_TAG;
02503 }
02504 
02505 static void set_delete_particle_key(PEData *data, int pa_index, int key_index)
02506 {
02507     PTCacheEdit *edit= data->edit;
02508 
02509     edit->points[pa_index].keys[key_index].flag |= PEK_TAG;
02510 }
02511 
02512 static int delete_exec(bContext *C, wmOperator *op)
02513 {
02514     PEData data;
02515     int type= RNA_enum_get(op->ptr, "type");
02516 
02517     PE_set_data(C, &data);
02518 
02519     if(type == DEL_KEY) {
02520         foreach_selected_key(&data, set_delete_particle_key);
02521         remove_tagged_keys(data.ob, data.edit->psys);
02522         recalc_lengths(data.edit);
02523     }
02524     else if(type == DEL_PARTICLE) {
02525         foreach_selected_point(&data, set_delete_particle);
02526         remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
02527         recalc_lengths(data.edit);
02528     }
02529 
02530     DAG_id_tag_update(&data.ob->id, OB_RECALC_DATA);
02531     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
02532 
02533     return OPERATOR_FINISHED;
02534 }
02535 
02536 void PARTICLE_OT_delete(wmOperatorType *ot)
02537 {
02538     /* identifiers */
02539     ot->name= "Delete";
02540     ot->idname= "PARTICLE_OT_delete";
02541     
02542     /* api callbacks */
02543     ot->exec= delete_exec;
02544     ot->invoke= WM_menu_invoke;
02545     ot->poll= PE_hair_poll;
02546 
02547     /* flags */
02548     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02549 
02550     /* properties */
02551     ot->prop= RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys");
02552 }
02553 
02554 /*************************** mirror operator **************************/
02555 
02556 static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
02557 {
02558     Mesh *me= (Mesh*)(ob->data);
02559     ParticleSystemModifierData *psmd;
02560     PTCacheEdit *edit= PE_get_current(scene, ob);
02561     ParticleSystem *psys = edit->psys;
02562     ParticleData *pa, *newpa, *new_pars;
02563     PTCacheEditPoint *newpoint, *new_points;
02564     POINT_P; KEY_K;
02565     HairKey *hkey;
02566     int *mirrorfaces;
02567     int rotation, totpart, newtotpart;
02568 
02569     if(psys->flag & PSYS_GLOBAL_HAIR)
02570         return;
02571 
02572     psmd= psys_get_modifier(ob, psys);
02573     if(!psmd->dm)
02574         return;
02575 
02576     mirrorfaces= mesh_get_x_mirror_faces(ob, NULL);
02577 
02578     if(!edit->mirror_cache)
02579         PE_update_mirror_cache(ob, psys);
02580 
02581     totpart= psys->totpart;
02582     newtotpart= psys->totpart;
02583     LOOP_VISIBLE_POINTS {
02584         pa = psys->particles + p;
02585         if(!tagged) {
02586             if(point_is_selected(point)) {
02587                 if(edit->mirror_cache[p] != -1) {
02588                     /* already has a mirror, don't need to duplicate */
02589                     PE_mirror_particle(ob, psmd->dm, psys, pa, NULL);
02590                     continue;
02591                 }
02592                 else
02593                     point->flag |= PEP_TAG;
02594             }
02595         }
02596 
02597         if((point->flag & PEP_TAG) && mirrorfaces[pa->num*2] != -1)
02598             newtotpart++;
02599     }
02600 
02601     if(newtotpart != psys->totpart) {
02602         /* allocate new arrays and copy existing */
02603         new_pars= MEM_callocN(newtotpart*sizeof(ParticleData), "ParticleData new");
02604         new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint), "PTCacheEditPoint new");
02605 
02606         if(psys->particles) {
02607             memcpy(new_pars, psys->particles, totpart*sizeof(ParticleData));
02608             MEM_freeN(psys->particles);
02609         }
02610         psys->particles= new_pars;
02611 
02612         if(edit->points) {
02613             memcpy(new_points, edit->points, totpart*sizeof(PTCacheEditPoint));
02614             MEM_freeN(edit->points);
02615         }
02616         edit->points= new_points;
02617 
02618         if(edit->mirror_cache) {
02619             MEM_freeN(edit->mirror_cache);
02620             edit->mirror_cache= NULL;
02621         }
02622 
02623         edit->totpoint= psys->totpart= newtotpart;
02624             
02625         /* create new elements */
02626         newpa= psys->particles + totpart;
02627         newpoint= edit->points + totpart;
02628 
02629         for(p=0, point=edit->points; p<totpart; p++, point++) {
02630             pa = psys->particles + p;
02631 
02632             if(point->flag & PEP_HIDE)
02633                 continue;
02634             if(!(point->flag & PEP_TAG) || mirrorfaces[pa->num*2] == -1)
02635                 continue;
02636 
02637             /* duplicate */
02638             *newpa= *pa;
02639             *newpoint= *point;
02640             if(pa->hair) newpa->hair= MEM_dupallocN(pa->hair);
02641             if(point->keys) newpoint->keys= MEM_dupallocN(point->keys);
02642 
02643             /* rotate weights according to vertex index rotation */
02644             rotation= mirrorfaces[pa->num*2+1];
02645             newpa->fuv[0]= pa->fuv[2];
02646             newpa->fuv[1]= pa->fuv[1];
02647             newpa->fuv[2]= pa->fuv[0];
02648             newpa->fuv[3]= pa->fuv[3];
02649             while(rotation-- > 0)
02650                 if(me->mface[pa->num].v4)
02651                     SHIFT4(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2], newpa->fuv[3])
02652                 else
02653                     SHIFT3(float, newpa->fuv[0], newpa->fuv[1], newpa->fuv[2])
02654 
02655             /* assign face inddex */
02656             newpa->num= mirrorfaces[pa->num*2];
02657             newpa->num_dmcache= psys_particle_dm_face_lookup(ob,psmd->dm,newpa->num,newpa->fuv, NULL);
02658 
02659             /* update edit key pointers */
02660             key= newpoint->keys;
02661             for(k=0, hkey=newpa->hair; k<newpa->totkey; k++, hkey++, key++) {
02662                 key->co= hkey->co;
02663                 key->time= &hkey->time;
02664             }
02665 
02666             /* map key positions as mirror over x axis */
02667             PE_mirror_particle(ob, psmd->dm, psys, pa, newpa);
02668 
02669             newpa++;
02670             newpoint++;
02671         }
02672     }
02673 
02674     LOOP_POINTS {
02675         point->flag &= ~PEP_TAG;
02676     }
02677 
02678     MEM_freeN(mirrorfaces);
02679 }
02680 
02681 static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
02682 {
02683     Scene *scene= CTX_data_scene(C);
02684     Object *ob= CTX_data_active_object(C);
02685     PTCacheEdit *edit= PE_get_current(scene, ob);
02686     
02687     PE_mirror_x(scene, ob, 0);
02688 
02689     update_world_cos(ob, edit);
02690     WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
02691     DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
02692 
02693     return OPERATOR_FINISHED;
02694 }
02695 
02696 void PARTICLE_OT_mirror(wmOperatorType *ot)
02697 {
02698     /* identifiers */
02699     ot->name= "Mirror";
02700     ot->idname= "PARTICLE_OT_mirror";
02701     
02702     /* api callbacks */
02703     ot->exec= mirror_exec;
02704     ot->poll= PE_poll;
02705 
02706     /* flags */
02707     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
02708 }
02709 
02710 /************************* brush edit callbacks ********************/
02711 
02712 static void brush_comb(PEData *data, float UNUSED(mat[][4]), float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
02713 {
02714     ParticleEditSettings *pset= PE_settings(data->scene);
02715     float cvec[3], fac;
02716 
02717     if(pset->flag & PE_LOCK_FIRST && key_index == 0) return;
02718 
02719     fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->combfac);
02720 
02721     copy_v3_v3(cvec,data->dvec);
02722     mul_mat3_m4_v3(imat,cvec);
02723     mul_v3_fl(cvec, fac);
02724     add_v3_v3(key->co, cvec);
02725 
02726     (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
02727 }
02728 
02729 static void brush_cut(PEData *data, int pa_index)
02730 {
02731     PTCacheEdit *edit = data->edit;
02732     ARegion *ar= data->vc.ar;
02733     Object *ob= data->ob;
02734     ParticleEditSettings *pset= PE_settings(data->scene);
02735     ParticleCacheKey *key= edit->pathcache[pa_index];
02736     float rad2, cut_time= 1.0;
02737     float x0, x1, v0, v1, o0, o1, xo0, xo1, d, dv;
02738     int k, cut, keys= (int)pow(2.0, (double)pset->draw_step);
02739     int vertco[2];
02740 
02741     /* blunt scissors */
02742     if(BLI_frand() > data->cutfac) return;
02743 
02744     /* don't cut hidden */
02745     if(edit->points[pa_index].flag & PEP_HIDE)
02746         return;
02747 
02748     rad2= data->rad * data->rad;
02749 
02750     cut=0;
02751 
02752     project_int_noclip(ar, key->co, vertco);
02753     x0= (float)vertco[0];
02754     x1= (float)vertco[1];
02755 
02756     o0= (float)data->mval[0];
02757     o1= (float)data->mval[1];
02758     
02759     xo0= x0 - o0;
02760     xo1= x1 - o1;
02761 
02762     /* check if root is inside circle */
02763     if(xo0*xo0 + xo1*xo1 < rad2 && key_test_depth(data, key->co)) {
02764         cut_time= -1.0f;
02765         cut= 1;
02766     }
02767     else {
02768         /* calculate path time closest to root that was inside the circle */
02769         for(k=1, key++; k<=keys; k++, key++) {
02770             project_int_noclip(ar, key->co, vertco);
02771 
02772             if(key_test_depth(data, key->co) == 0) {
02773                 x0= (float)vertco[0];
02774                 x1= (float)vertco[1];
02775 
02776                 xo0= x0 - o0;
02777                 xo1= x1 - o1;
02778                 continue;
02779             }
02780 
02781             v0= (float)vertco[0] - x0;
02782             v1= (float)vertco[1] - x1;
02783 
02784             dv= v0*v0 + v1*v1;
02785 
02786             d= (v0*xo1 - v1*xo0);
02787             
02788             d= dv * rad2 - d*d;
02789 
02790             if(d > 0.0f) {
02791                 d= sqrt(d);
02792 
02793                 cut_time= -(v0*xo0 + v1*xo1 + d);
02794 
02795                 if(cut_time > 0.0f) {
02796                     cut_time /= dv;
02797 
02798                     if(cut_time < 1.0f) {
02799                         cut_time += (float)(k-1);
02800                         cut_time /= (float)keys;
02801                         cut= 1;
02802                         break;
02803                     }
02804                 }
02805             }
02806 
02807             x0= (float)vertco[0];
02808             x1= (float)vertco[1];
02809 
02810             xo0= x0 - o0;
02811             xo1= x1 - o1;
02812         }
02813     }
02814 
02815     if(cut) {
02816         if(cut_time < 0.0f) {
02817             edit->points[pa_index].flag |= PEP_TAG;
02818         }
02819         else {
02820             rekey_particle_to_time(data->scene, ob, pa_index, cut_time);
02821             edit->points[pa_index].flag |= PEP_EDIT_RECALC;
02822         }
02823     }
02824 }
02825 
02826 static void brush_length(PEData *data, int point_index)
02827 {
02828     PTCacheEdit *edit= data->edit;
02829     PTCacheEditPoint *point = edit->points + point_index;
02830     KEY_K;
02831     float dvec[3],pvec[3] = {0.0f, 0.0f, 0.0f};
02832 
02833     LOOP_KEYS {
02834         if(k==0) {
02835             copy_v3_v3(pvec,key->co);
02836         }
02837         else {
02838             sub_v3_v3v3(dvec,key->co,pvec);
02839             copy_v3_v3(pvec,key->co);
02840             mul_v3_fl(dvec,data->growfac);
02841             add_v3_v3v3(key->co,(key-1)->co,dvec);
02842         }
02843     }
02844 
02845     point->flag |= PEP_EDIT_RECALC;
02846 }
02847 
02848 static void brush_puff(PEData *data, int point_index)
02849 {
02850     PTCacheEdit *edit = data->edit;
02851     ParticleSystem *psys = edit->psys;
02852     PTCacheEditPoint *point = edit->points + point_index;
02853     KEY_K;
02854     float mat[4][4], imat[4][4];
02855 
02856     float lastco[3], rootco[3] = {0.0f, 0.0f, 0.0f}, co[3], nor[3], kco[3], dco[3], ofs[3] = {0.0f, 0.0f, 0.0f}, fac=0.0f, length=0.0f;
02857     int puff_volume = 0;
02858     int change= 0;
02859 
02860     {
02861         ParticleEditSettings *pset= PE_settings(data->scene);
02862         ParticleBrushData *brush= &pset->brush[pset->brushtype];
02863         puff_volume = brush->flag & PE_BRUSH_DATA_PUFF_VOLUME;
02864     }
02865 
02866     if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
02867         psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
02868         invert_m4_m4(imat,mat);
02869     }
02870     else {
02871         unit_m4(mat);
02872         unit_m4(imat);
02873     }
02874 
02875     LOOP_KEYS {
02876         if(k==0) {
02877             /* find root coordinate and normal on emitter */
02878             copy_v3_v3(co, key->co);
02879             mul_m4_v3(mat, co);
02880             mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
02881 
02882             point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
02883             if(point_index == -1) return;
02884 
02885             copy_v3_v3(rootco, co);
02886             copy_v3_v3(nor, &edit->emitter_cosnos[point_index*6+3]);
02887             mul_mat3_m4_v3(data->ob->obmat, nor); /* normal into worldspace */
02888 
02889             normalize_v3(nor);
02890             length= 0.0f;
02891 
02892             fac= (float)pow((double)(1.0f - data->dist / data->rad), (double)data->pufffac);
02893             fac *= 0.025f;
02894             if(data->invert)
02895                 fac= -fac;
02896         }
02897         else {
02898             /* compute position as if hair was standing up straight.
02899              * */
02900             copy_v3_v3(lastco, co);
02901             copy_v3_v3(co, key->co);
02902             mul_m4_v3(mat, co);
02903             length += len_v3v3(lastco, co);
02904             if((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
02905                 madd_v3_v3v3fl(kco, rootco, nor, length);
02906 
02907                 /* blend between the current and straight position */
02908                 sub_v3_v3v3(dco, kco, co);
02909                 madd_v3_v3fl(co, dco, fac);
02910 
02911                 /* re-use dco to compare before and after translation and add to the offset  */
02912                 copy_v3_v3(dco, key->co);
02913 
02914                 mul_v3_m4v3(key->co, imat, co);
02915 
02916                 if(puff_volume) {
02917                     /* accumulate the total distance moved to apply to unselected
02918                      * keys that come after */
02919                     ofs[0] += key->co[0] - dco[0];
02920                     ofs[1] += key->co[1] - dco[1];
02921                     ofs[2] += key->co[2] - dco[2];
02922                 }
02923                 change = 1;
02924             }
02925             else {
02926 
02927                 if(puff_volume) {
02928 #if 0
02929                     /* this is simple but looks bad, adds annoying kinks */
02930                     add_v3_v3(key->co, ofs);
02931 #else
02932                     /* translate (not rotate) the rest of the hair if its not selected  */
02933                     if(ofs[0] || ofs[1] || ofs[2]) {
02934 #if 0                   /* kindof works but looks worse then whats below */
02935 
02936                         /* Move the unselected point on a vector based on the
02937                          * hair direction and the offset */
02938                         float c1[3], c2[3];
02939                         sub_v3_v3v3(dco, lastco, co);
02940                         mul_mat3_m4_v3(imat, dco); /* into particle space */
02941 
02942                         /* move the point along a vector perpendicular to the
02943                          * hairs direction, reduces odd kinks, */
02944                         cross_v3_v3v3(c1, ofs, dco);
02945                         cross_v3_v3v3(c2, c1, dco);
02946                         normalize_v3(c2);
02947                         mul_v3_fl(c2, len_v3(ofs));
02948                         add_v3_v3(key->co, c2);
02949 #else
02950                         /* Move the unselected point on a vector based on the
02951                          * the normal of the closest geometry */
02952                         float oco[3], onor[3];
02953                         copy_v3_v3(oco, key->co);
02954                         mul_m4_v3(mat, oco);
02955                         mul_v3_m4v3(kco, data->ob->imat, oco); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
02956 
02957                         point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
02958                         if(point_index != -1) {
02959                             copy_v3_v3(onor, &edit->emitter_cosnos[point_index*6+3]);
02960                             mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
02961                             mul_mat3_m4_v3(imat, onor); /* worldspace into particle space */
02962                             normalize_v3(onor);
02963 
02964 
02965                             mul_v3_fl(onor, len_v3(ofs));
02966                             add_v3_v3(key->co, onor);
02967                         }
02968 #endif
02969                     }
02970 #endif
02971                 }
02972             }
02973         }
02974     }
02975 
02976     if(change)
02977         point->flag |= PEP_EDIT_RECALC;
02978 }
02979 
02980 
02981 static void brush_weight(PEData *data, float UNUSED(mat[][4]), float UNUSED(imat[][4]), int point_index, int key_index, PTCacheEditKey *UNUSED(key))
02982 {
02983     /* roots have full weight allways */
02984     if(key_index) {
02985         PTCacheEdit *edit = data->edit;
02986         ParticleSystem *psys = edit->psys;
02987 
02988         ParticleData *pa= psys->particles + point_index;
02989         pa->hair[key_index].weight = data->weightfac;
02990 
02991         (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
02992     }
02993 }
02994 
02995 static void brush_smooth_get(PEData *data, float mat[][4], float UNUSED(imat[][4]), int UNUSED(point_index), int key_index, PTCacheEditKey *key)
02996 {   
02997     if(key_index) {
02998         float dvec[3];
02999 
03000         sub_v3_v3v3(dvec,key->co,(key-1)->co);
03001         mul_mat3_m4_v3(mat,dvec);
03002         add_v3_v3(data->vec, dvec);
03003         data->tot++;
03004     }
03005 }
03006 
03007 static void brush_smooth_do(PEData *data, float UNUSED(mat[][4]), float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
03008 {
03009     float vec[3], dvec[3];
03010     
03011     if(key_index) {
03012         copy_v3_v3(vec, data->vec);
03013         mul_mat3_m4_v3(imat,vec);
03014 
03015         sub_v3_v3v3(dvec,key->co,(key-1)->co);
03016 
03017         sub_v3_v3v3(dvec,vec,dvec);
03018         mul_v3_fl(dvec,data->smoothfac);
03019         
03020         add_v3_v3(key->co, dvec);
03021     }
03022 
03023     (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
03024 }
03025 
03026 /* convert from triangle barycentric weights to quad mean value weights */
03027 static void intersect_dm_quad_weights(const float v1[3], const float v2[3], const float v3[3], const float v4[3], float w[4])
03028 {
03029     float co[3], vert[4][3];
03030 
03031     copy_v3_v3(vert[0], v1);
03032     copy_v3_v3(vert[1], v2);
03033     copy_v3_v3(vert[2], v3);
03034     copy_v3_v3(vert[3], v4);
03035 
03036     co[0]= v1[0]*w[0] + v2[0]*w[1] + v3[0]*w[2] + v4[0]*w[3];
03037     co[1]= v1[1]*w[0] + v2[1]*w[1] + v3[1]*w[2] + v4[1]*w[3];
03038     co[2]= v1[2]*w[0] + v2[2]*w[1] + v3[2]*w[2] + v4[2]*w[3];
03039 
03040     interp_weights_poly_v3(w, vert, 4, co);
03041 }
03042 
03043 /* check intersection with a derivedmesh */
03044 static int particle_intersect_dm(Scene *scene, Object *ob, DerivedMesh *dm,
03045                                  float *vert_cos,
03046                                  const float co1[3], const float co2[3],
03047                                  float *min_d, int *min_face, float *min_w,
03048                                  float *face_minmax, float *pa_minmax,
03049                                  float radius, float *ipoint)
03050 {
03051     MFace *mface= NULL;
03052     MVert *mvert= NULL;
03053     int i, totface, intersect=0;
03054     float cur_d, cur_uv[2], v1[3], v2[3], v3[3], v4[3], min[3], max[3], p_min[3],p_max[3];
03055     float cur_ipoint[3];
03056     
03057     if(dm == NULL){
03058         psys_disable_all(ob);
03059 
03060         dm=mesh_get_derived_final(scene, ob, 0);
03061         if(dm == NULL)
03062             dm=mesh_get_derived_deform(scene, ob, 0);
03063 
03064         psys_enable_all(ob);
03065 
03066         if(dm == NULL)
03067             return 0;
03068     }
03069 
03070     
03071 
03072     if(pa_minmax==0){
03073         INIT_MINMAX(p_min,p_max);
03074         DO_MINMAX(co1,p_min,p_max);
03075         DO_MINMAX(co2,p_min,p_max);
03076     }
03077     else{
03078         copy_v3_v3(p_min,pa_minmax);
03079         copy_v3_v3(p_max,pa_minmax+3);
03080     }
03081 
03082     totface=dm->getNumFaces(dm);
03083     mface=dm->getFaceDataArray(dm,CD_MFACE);
03084     mvert=dm->getVertDataArray(dm,CD_MVERT);
03085     
03086     /* lets intersect the faces */
03087     for(i=0; i<totface; i++,mface++){
03088         if(vert_cos){
03089             copy_v3_v3(v1,vert_cos+3*mface->v1);
03090             copy_v3_v3(v2,vert_cos+3*mface->v2);
03091             copy_v3_v3(v3,vert_cos+3*mface->v3);
03092             if(mface->v4)
03093                 copy_v3_v3(v4,vert_cos+3*mface->v4);
03094         }
03095         else{
03096             copy_v3_v3(v1,mvert[mface->v1].co);
03097             copy_v3_v3(v2,mvert[mface->v2].co);
03098             copy_v3_v3(v3,mvert[mface->v3].co);
03099             if(mface->v4)
03100                 copy_v3_v3(v4,mvert[mface->v4].co);
03101         }
03102 
03103         if(face_minmax==0){
03104             INIT_MINMAX(min,max);
03105             DO_MINMAX(v1,min,max);
03106             DO_MINMAX(v2,min,max);
03107             DO_MINMAX(v3,min,max);
03108             if(mface->v4)
03109                 DO_MINMAX(v4,min,max)
03110             if(isect_aabb_aabb_v3(min,max,p_min,p_max)==0)
03111                 continue;
03112         }
03113         else{
03114             copy_v3_v3(min, face_minmax+6*i);
03115             copy_v3_v3(max, face_minmax+6*i+3);
03116             if(isect_aabb_aabb_v3(min,max,p_min,p_max)==0)
03117                 continue;
03118         }
03119 
03120         if(radius>0.0f){
03121             if(isect_sweeping_sphere_tri_v3(co1, co2, radius, v2, v3, v1, &cur_d, cur_ipoint)){
03122                 if(cur_d<*min_d){
03123                     *min_d=cur_d;
03124                     copy_v3_v3(ipoint,cur_ipoint);
03125                     *min_face=i;
03126                     intersect=1;
03127                 }
03128             }
03129             if(mface->v4){
03130                 if(isect_sweeping_sphere_tri_v3(co1, co2, radius, v4, v1, v3, &cur_d, cur_ipoint)){
03131                     if(cur_d<*min_d){
03132                         *min_d=cur_d;
03133                         copy_v3_v3(ipoint,cur_ipoint);
03134                         *min_face=i;
03135                         intersect=1;
03136                     }
03137                 }
03138             }
03139         }
03140         else{
03141             if(isect_line_tri_v3(co1, co2, v1, v2, v3, &cur_d, cur_uv)){
03142                 if(cur_d<*min_d){
03143                     *min_d=cur_d;
03144                     min_w[0]= 1.0f - cur_uv[0] - cur_uv[1];
03145                     min_w[1]= cur_uv[0];
03146                     min_w[2]= cur_uv[1];
03147                     min_w[3]= 0.0f;
03148                     if(mface->v4)
03149                         intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
03150                     *min_face=i;
03151                     intersect=1;
03152                 }
03153             }
03154             if(mface->v4){
03155                 if(isect_line_tri_v3(co1, co2, v1, v3, v4, &cur_d, cur_uv)){
03156                     if(cur_d<*min_d){
03157                         *min_d=cur_d;
03158                         min_w[0]= 1.0f - cur_uv[0] - cur_uv[1];
03159                         min_w[1]= 0.0f;
03160                         min_w[2]= cur_uv[0];
03161                         min_w[3]= cur_uv[1];
03162                         intersect_dm_quad_weights(v1, v2, v3, v4, min_w);
03163                         *min_face=i;
03164                         intersect=1;
03165                     }
03166                 }
03167             }
03168         }
03169     }
03170     return intersect;
03171 }
03172 
03173 static int brush_add(PEData *data, short number)
03174 {
03175     Scene *scene= data->scene;
03176     Object *ob= data->ob;
03177     PTCacheEdit *edit = data->edit;
03178     ParticleSystem *psys= edit->psys;
03179     ParticleData *add_pars= MEM_callocN(number*sizeof(ParticleData),"ParticleData add");
03180     ParticleSystemModifierData *psmd= psys_get_modifier(ob,psys);
03181     ParticleSimulationData sim= {0};
03182     ParticleEditSettings *pset= PE_settings(scene);
03183     int i, k, n= 0, totpart= psys->totpart;
03184     float mco[2];
03185     short dmx= 0, dmy= 0;
03186     float co1[3], co2[3], min_d, imat[4][4];
03187     float framestep, timestep;
03188     short size= pset->brush[PE_BRUSH_ADD].size;
03189     short size2= size*size;
03190     DerivedMesh *dm=0;
03191     invert_m4_m4(imat,ob->obmat);
03192 
03193     if(psys->flag & PSYS_GLOBAL_HAIR)
03194         return 0;
03195 
03196     BLI_srandom(psys->seed+data->mval[0]+data->mval[1]);
03197 
03198     sim.scene= scene;
03199     sim.ob= ob;
03200     sim.psys= psys;
03201     sim.psmd= psmd;
03202 
03203     timestep= psys_get_timestep(&sim);
03204 
03205     /* painting onto the deformed mesh, could be an option? */
03206     if(psmd->dm->deformedOnly)
03207         dm= psmd->dm;
03208     else
03209         dm= mesh_get_derived_deform(scene, ob, CD_MASK_BAREMESH);
03210 
03211     for(i=0; i<number; i++) {
03212         if(number>1) {
03213             dmx=dmy=size;
03214             while(dmx*dmx+dmy*dmy>size2) {
03215                 dmx=(short)((2.0f*BLI_frand()-1.0f)*size);
03216                 dmy=(short)((2.0f*BLI_frand()-1.0f)*size);
03217             }
03218         }
03219 
03220         mco[0]= data->mval[0] + dmx;
03221         mco[1]= data->mval[1] + dmy;
03222         ED_view3d_win_to_segment_clip(data->vc.ar, data->vc.v3d, mco, co1, co2);
03223 
03224         mul_m4_v3(imat,co1);
03225         mul_m4_v3(imat,co2);
03226         min_d=2.0;
03227         
03228         /* warning, returns the derived mesh face */
03229         if(particle_intersect_dm(scene, ob,dm,0,co1,co2,&min_d,&add_pars[n].num,add_pars[n].fuv,0,0,0,0)) {
03230             add_pars[n].num_dmcache= psys_particle_dm_face_lookup(ob,psmd->dm,add_pars[n].num,add_pars[n].fuv,NULL);
03231             n++;
03232         }
03233     }
03234     if(n) {
03235         int newtotpart=totpart+n;
03236         float hairmat[4][4], cur_co[3];
03237         KDTree *tree=0;
03238         ParticleData *pa, *new_pars= MEM_callocN(newtotpart*sizeof(ParticleData),"ParticleData new");
03239         PTCacheEditPoint *point, *new_points= MEM_callocN(newtotpart*sizeof(PTCacheEditPoint),"PTCacheEditPoint array new");
03240         PTCacheEditKey *key;
03241         HairKey *hkey;
03242 
03243         /* save existing elements */
03244         memcpy(new_pars, psys->particles, totpart * sizeof(ParticleData));
03245         memcpy(new_points, edit->points, totpart * sizeof(PTCacheEditPoint));
03246 
03247         /* change old arrays to new ones */
03248         if(psys->particles) MEM_freeN(psys->particles);
03249         psys->particles= new_pars;
03250 
03251         if(edit->points) MEM_freeN(edit->points);
03252         edit->points= new_points;
03253 
03254         if(edit->mirror_cache) {
03255             MEM_freeN(edit->mirror_cache);
03256             edit->mirror_cache= NULL;
03257         }
03258 
03259         /* create tree for interpolation */
03260         if(pset->flag & PE_INTERPOLATE_ADDED && psys->totpart) {
03261             tree=BLI_kdtree_new(psys->totpart);
03262             
03263             for(i=0, pa=psys->particles; i<totpart; i++, pa++) {
03264                 psys_particle_on_dm(psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,cur_co,0,0,0,0,0);
03265                 BLI_kdtree_insert(tree, i, cur_co, NULL);
03266             }
03267 
03268             BLI_kdtree_balance(tree);
03269         }
03270 
03271         edit->totpoint= psys->totpart= newtotpart;
03272 
03273         /* create new elements */
03274         pa= psys->particles + totpart;
03275         point= edit->points + totpart;
03276 
03277         for(i=totpart; i<newtotpart; i++, pa++, point++) {
03278             memcpy(pa, add_pars + i - totpart, sizeof(ParticleData));
03279             pa->hair= MEM_callocN(pset->totaddkey * sizeof(HairKey), "BakeKey key add");
03280             key= point->keys= MEM_callocN(pset->totaddkey * sizeof(PTCacheEditKey), "PTCacheEditKey add");
03281             point->totkey= pa->totkey= pset->totaddkey;
03282 
03283             for(k=0, hkey=pa->hair; k<pa->totkey; k++, hkey++, key++) {
03284                 key->co= hkey->co;
03285                 key->time= &hkey->time;
03286 
03287                 if(!(psys->flag & PSYS_GLOBAL_HAIR))
03288                     key->flag |= PEK_USE_WCO;
03289             }
03290             
03291             pa->size= 1.0f;
03292             initialize_particle(&sim, pa,i);
03293             reset_particle(&sim, pa, 0.0, 1.0);
03294             point->flag |= PEP_EDIT_RECALC;
03295             if(pe_x_mirror(ob))
03296                 point->flag |= PEP_TAG; /* signal for duplicate */
03297             
03298             framestep= pa->lifetime/(float)(pset->totaddkey-1);
03299 
03300             if(tree) {
03301                 ParticleData *ppa;
03302                 HairKey *thkey;
03303                 ParticleKey key3[3];
03304                 KDTreeNearest ptn[3];
03305                 int w, maxw;
03306                 float maxd, totw=0.0, weight[3];
03307 
03308                 psys_particle_on_dm(psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,0,0);
03309                 maxw= BLI_kdtree_find_n_nearest(tree,3,co1,NULL,ptn);
03310 
03311                 maxd= ptn[maxw-1].dist;
03312                 
03313                 for(w=0; w<maxw; w++) {
03314                     weight[w]= (float)pow(2.0, (double)(-6.0f * ptn[w].dist / maxd));
03315                     totw += weight[w];
03316                 }
03317                 for(;w<3; w++) {
03318                     weight[w]= 0.0f;
03319                 }
03320 
03321                 for(w=0; w<maxw; w++)
03322                     weight[w] /= totw;
03323 
03324                 ppa= psys->particles+ptn[0].index;
03325 
03326                 for(k=0; k<pset->totaddkey; k++) {
03327                     thkey= (HairKey*)pa->hair + k;
03328                     thkey->time= pa->time + k * framestep;
03329 
03330                     key3[0].time= thkey->time/ 100.0f;
03331                     psys_get_particle_on_path(&sim, ptn[0].index, key3, 0);
03332                     mul_v3_fl(key3[0].co, weight[0]);
03333                     
03334                     /* TODO: interpolatint the weight would be nicer */
03335                     thkey->weight= (ppa->hair+MIN2(k, ppa->totkey-1))->weight;
03336                     
03337                     if(maxw>1) {
03338                         key3[1].time= key3[0].time;
03339                         psys_get_particle_on_path(&sim, ptn[1].index, &key3[1], 0);
03340                         mul_v3_fl(key3[1].co, weight[1]);
03341                         add_v3_v3(key3[0].co, key3[1].co);
03342 
03343                         if(maxw>2) {                        
03344                             key3[2].time= key3[0].time;
03345                             psys_get_particle_on_path(&sim, ptn[2].index, &key3[2], 0);
03346                             mul_v3_fl(key3[2].co, weight[2]);
03347                             add_v3_v3(key3[0].co, key3[2].co);
03348                         }
03349                     }
03350 
03351                     if(k==0)
03352                         sub_v3_v3v3(co1, pa->state.co, key3[0].co);
03353 
03354                     add_v3_v3v3(thkey->co, key3[0].co, co1);
03355 
03356                     thkey->time= key3[0].time;
03357                 }
03358             }
03359             else {
03360                 for(k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
03361                     madd_v3_v3v3fl(hkey->co, pa->state.co, pa->state.vel, k * framestep * timestep);
03362                     hkey->time += k * framestep;
03363                     hkey->weight = 1.f - (float)k/(float)(pset->totaddkey-1);
03364                 }
03365             }
03366             for(k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
03367                 psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat);
03368                 invert_m4_m4(imat,hairmat);
03369                 mul_m4_v3(imat, hkey->co);
03370             }
03371         }
03372 
03373         if(tree)
03374             BLI_kdtree_free(tree);
03375     }
03376     if(add_pars)
03377         MEM_freeN(add_pars);
03378     
03379     if(!psmd->dm->deformedOnly)
03380         dm->release(dm);
03381     
03382     return n;
03383 }
03384 
03385 /************************* brush edit operator ********************/
03386 
03387 typedef struct BrushEdit {
03388     Scene *scene;
03389     Object *ob;
03390     PTCacheEdit *edit;
03391 
03392     int first;
03393     int lastmouse[2];
03394 
03395     /* optional cached view settings to avoid setting on every mousemove */
03396     PEData data;
03397 } BrushEdit;
03398 
03399 static int brush_edit_init(bContext *C, wmOperator *op)
03400 {
03401     Scene *scene= CTX_data_scene(C);
03402     Object *ob= CTX_data_active_object(C);
03403     ParticleEditSettings *pset= PE_settings(scene);
03404     PTCacheEdit *edit= PE_get_current(scene, ob);
03405     ARegion *ar= CTX_wm_region(C);
03406     BrushEdit *bedit;
03407 
03408     if(pset->brushtype < 0)
03409         return 0;
03410 
03411     initgrabz(ar->regiondata, ob->obmat[3][0], ob->obmat[3][1], ob->obmat[3][2]);
03412 
03413     bedit= MEM_callocN(sizeof(BrushEdit), "BrushEdit");
03414     bedit->first= 1;
03415     op->customdata= bedit;
03416 
03417     bedit->scene= scene;
03418     bedit->ob= ob;
03419     bedit->edit= edit;
03420 
03421     /* cache view depths and settings for re-use */
03422     PE_set_view3d_data(C, &bedit->data);
03423 
03424     return 1;
03425 }
03426 
03427 static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
03428 {
03429     BrushEdit *bedit= op->customdata;
03430     Scene *scene= bedit->scene;
03431     Object *ob= bedit->ob;
03432     PTCacheEdit *edit= bedit->edit;
03433     ParticleEditSettings *pset= PE_settings(scene);
03434     ParticleSystemModifierData *psmd= edit->psys ? psys_get_modifier(ob, edit->psys) : NULL;
03435     ParticleBrushData *brush= &pset->brush[pset->brushtype];
03436     ARegion *ar= CTX_wm_region(C);
03437     float vec[3], mousef[2];
03438     int mval[2];
03439     int flip, mouse[2], removed= 0, added=0, selected= 0, tot_steps= 1, step= 1;
03440     float dx, dy, dmax;
03441     int lock_root = pset->flag & PE_LOCK_FIRST;
03442 
03443     if(!PE_start_edit(edit))
03444         return;
03445 
03446     RNA_float_get_array(itemptr, "mouse", mousef);
03447     mouse[0] = mousef[0];
03448     mouse[1] = mousef[1];
03449     flip= RNA_boolean_get(itemptr, "pen_flip");
03450 
03451     if(bedit->first) {
03452         bedit->lastmouse[0]= mouse[0];
03453         bedit->lastmouse[1]= mouse[1];
03454     }
03455 
03456     dx= mouse[0] - bedit->lastmouse[0];
03457     dy= mouse[1] - bedit->lastmouse[1];
03458 
03459     mval[0]= mouse[0];
03460     mval[1]= mouse[1];
03461 
03462 
03463     /* disable locking temporatily for disconnected hair */
03464     if(edit->psys && edit->psys->flag & PSYS_GLOBAL_HAIR)
03465         pset->flag &= ~PE_LOCK_FIRST;
03466 
03467     if(((pset->brushtype == PE_BRUSH_ADD) ?
03468         (sqrt(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0))
03469         || bedit->first) {
03470         PEData data= bedit->data;
03471 
03472         view3d_operator_needs_opengl(C);
03473         selected= (short)count_selected_keys(scene, edit);
03474 
03475         dmax = MAX2(fabs(dx), fabs(dy));
03476         tot_steps = dmax/(0.2f * brush->size) + 1;
03477 
03478         dx /= (float)tot_steps;
03479         dy /= (float)tot_steps;
03480 
03481         for(step = 1; step<=tot_steps; step++) {
03482             mval[0] = bedit->lastmouse[0] + step*dx;
03483             mval[1] = bedit->lastmouse[1] + step*dy;
03484 
03485             switch(pset->brushtype) {
03486                 case PE_BRUSH_COMB:
03487                 {
03488                     float mval_f[2];
03489                     data.mval= mval;
03490                     data.rad= (float)brush->size;
03491 
03492                     data.combfac= (brush->strength - 0.5f) * 2.0f;
03493                     if(data.combfac < 0.0f)
03494                         data.combfac= 1.0f - 9.0f * data.combfac;
03495                     else
03496                         data.combfac= 1.0f - data.combfac;
03497 
03498                     invert_m4_m4(ob->imat, ob->obmat);
03499 
03500                     mval_f[0]= dx;
03501                     mval_f[1]= dy;
03502                     ED_view3d_win_to_delta(ar, mval_f, vec);
03503                     data.dvec= vec;
03504 
03505                     foreach_mouse_hit_key(&data, brush_comb, selected);
03506                     break;
03507                 }
03508                 case PE_BRUSH_CUT:
03509                 {
03510                     if(edit->psys && edit->pathcache) {
03511                         data.mval= mval;
03512                         data.rad= (float)brush->size;
03513                         data.cutfac= brush->strength;
03514 
03515                         if(selected)
03516                             foreach_selected_point(&data, brush_cut);
03517                         else
03518                             foreach_point(&data, brush_cut);
03519 
03520                         removed= remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
03521                         if(pset->flag & PE_KEEP_LENGTHS)
03522                             recalc_lengths(edit);
03523                     }
03524                     else
03525                         removed= 0;
03526 
03527                     break;
03528                 }
03529                 case PE_BRUSH_LENGTH:
03530                 {
03531                     data.mval= mval;
03532                 
03533                     data.rad= (float)brush->size;
03534                     data.growfac= brush->strength / 50.0f;
03535 
03536                     if(brush->invert ^ flip)
03537                         data.growfac= 1.0f - data.growfac;
03538                     else
03539                         data.growfac= 1.0f + data.growfac;
03540 
03541                     foreach_mouse_hit_point(&data, brush_length, selected);
03542 
03543                     if(pset->flag & PE_KEEP_LENGTHS)
03544                         recalc_lengths(edit);
03545                     break;
03546                 }
03547                 case PE_BRUSH_PUFF:
03548                 {
03549                     if(edit->psys) {
03550                         data.dm= psmd->dm;
03551                         data.mval= mval;
03552                         data.rad= (float)brush->size;
03553                         data.select= selected;
03554 
03555                         data.pufffac= (brush->strength - 0.5f) * 2.0f;
03556                         if(data.pufffac < 0.0f)
03557                             data.pufffac= 1.0f - 9.0f * data.pufffac;
03558                         else
03559                             data.pufffac= 1.0f - data.pufffac;
03560 
03561                         data.invert= (brush->invert ^ flip);
03562                         invert_m4_m4(ob->imat, ob->obmat);
03563 
03564                         foreach_mouse_hit_point(&data, brush_puff, selected);
03565                     }
03566                     break;
03567                 }
03568                 case PE_BRUSH_ADD:
03569                 {
03570                     if(edit->psys && edit->psys->part->from==PART_FROM_FACE) {
03571                         data.mval= mval;
03572 
03573                         added= brush_add(&data, brush->count);
03574 
03575                         if(pset->flag & PE_KEEP_LENGTHS)
03576                             recalc_lengths(edit);
03577                     }
03578                     else
03579                         added= 0;
03580                     break;
03581                 }
03582                 case PE_BRUSH_SMOOTH:
03583                 {
03584                     data.mval= mval;
03585                     data.rad= (float)brush->size;
03586 
03587                     data.vec[0]= data.vec[1]= data.vec[2]= 0.0f;
03588                     data.tot= 0;
03589 
03590                     data.smoothfac= brush->strength;
03591 
03592                     invert_m4_m4(ob->imat, ob->obmat);
03593 
03594                     foreach_mouse_hit_key(&data, brush_smooth_get, selected);
03595 
03596                     if(data.tot) {
03597                         mul_v3_fl(data.vec, 1.0f / (float)data.tot);
03598                         foreach_mouse_hit_key(&data, brush_smooth_do, selected);
03599                     }
03600 
03601                     break;
03602                 }
03603                 case PE_BRUSH_WEIGHT:
03604                 {
03605                     if(edit->psys) {
03606                         data.dm= psmd->dm;
03607                         data.mval= mval;
03608                         data.rad= (float)brush->size;
03609 
03610                         data.weightfac = brush->strength; /* note that this will never be zero */
03611 
03612                         foreach_mouse_hit_key(&data, brush_weight, selected);
03613                     }
03614 
03615                     break;
03616                 }
03617             }
03618             if((pset->flag & PE_KEEP_LENGTHS)==0)
03619                 recalc_lengths(edit);
03620 
03621             if(ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
03622                 if(pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob))
03623                     PE_mirror_x(scene, ob, 1);
03624 
03625                 update_world_cos(ob,edit);
03626                 psys_free_path_cache(NULL, edit);
03627                 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
03628             }
03629             else
03630                 PE_update_object(scene, ob, 1);
03631         }
03632 
03633         if(edit->psys)
03634             WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
03635         else
03636         {
03637             DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
03638             WM_event_add_notifier(C, NC_OBJECT|ND_MODIFIER, ob);
03639         }
03640 
03641         bedit->lastmouse[0]= mouse[0];
03642         bedit->lastmouse[1]= mouse[1];
03643         bedit->first= 0;
03644     }
03645 
03646     pset->flag |= lock_root;
03647 }
03648 
03649 static void brush_edit_exit(wmOperator *op)
03650 {
03651     BrushEdit *bedit= op->customdata;
03652 
03653     MEM_freeN(bedit);
03654 }
03655 
03656 static int brush_edit_exec(bContext *C, wmOperator *op)
03657 {
03658     if(!brush_edit_init(C, op))
03659         return OPERATOR_CANCELLED;
03660 
03661     RNA_BEGIN(op->ptr, itemptr, "stroke") {
03662         brush_edit_apply(C, op, &itemptr);
03663     }
03664     RNA_END;
03665 
03666     brush_edit_exit(op);
03667 
03668     return OPERATOR_FINISHED;
03669 }
03670 
03671 static void brush_edit_apply_event(bContext *C, wmOperator *op, wmEvent *event)
03672 {
03673     PointerRNA itemptr;
03674     float mouse[2];
03675 
03676     VECCOPY2D(mouse, event->mval);
03677 
03678     /* fill in stroke */
03679     RNA_collection_add(op->ptr, "stroke", &itemptr);
03680 
03681     RNA_float_set_array(&itemptr, "mouse", mouse);
03682     RNA_boolean_set(&itemptr, "pen_flip", event->shift != FALSE); // XXX hardcoded
03683 
03684     /* apply */
03685     brush_edit_apply(C, op, &itemptr);
03686 }
03687 
03688 static int brush_edit_invoke(bContext *C, wmOperator *op, wmEvent *event)
03689 {
03690     if(!brush_edit_init(C, op))
03691         return OPERATOR_CANCELLED;
03692     
03693     brush_edit_apply_event(C, op, event);
03694 
03695     WM_event_add_modal_handler(C, op);
03696 
03697     return OPERATOR_RUNNING_MODAL;
03698 }
03699 
03700 static int brush_edit_modal(bContext *C, wmOperator *op, wmEvent *event)
03701 {
03702     switch(event->type) {
03703         case LEFTMOUSE:
03704         case MIDDLEMOUSE:
03705         case RIGHTMOUSE: // XXX hardcoded
03706             brush_edit_exit(op);
03707             return OPERATOR_FINISHED;
03708         case MOUSEMOVE:
03709             brush_edit_apply_event(C, op, event);
03710             break;
03711     }
03712 
03713     return OPERATOR_RUNNING_MODAL;
03714 }
03715 
03716 static int brush_edit_cancel(bContext *UNUSED(C), wmOperator *op)
03717 {
03718     brush_edit_exit(op);
03719 
03720     return OPERATOR_CANCELLED;
03721 }
03722 
03723 void PARTICLE_OT_brush_edit(wmOperatorType *ot)
03724 {
03725     /* identifiers */
03726     ot->name= "Brush Edit";
03727     ot->idname= "PARTICLE_OT_brush_edit";
03728     
03729     /* api callbacks */
03730     ot->exec= brush_edit_exec;
03731     ot->invoke= brush_edit_invoke;
03732     ot->modal= brush_edit_modal;
03733     ot->cancel= brush_edit_cancel;
03734     ot->poll= PE_poll_view3d;
03735 
03736     /* flags */
03737     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
03738 
03739     /* properties */
03740     RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
03741 }
03742 
03743 /*********************** undo ***************************/
03744 
03745 static void free_PTCacheUndo(PTCacheUndo *undo)
03746 {
03747     PTCacheEditPoint *point;
03748     int i;
03749 
03750     for(i=0, point=undo->points; i<undo->totpoint; i++, point++) {
03751         if(undo->particles && (undo->particles + i)->hair)
03752             MEM_freeN((undo->particles + i)->hair);
03753         if(point->keys)
03754             MEM_freeN(point->keys);
03755     }
03756     if(undo->points)
03757         MEM_freeN(undo->points);
03758 
03759     if(undo->particles)
03760         MEM_freeN(undo->particles);
03761 
03762     BKE_ptcache_free_mem(&undo->mem_cache);
03763 }
03764 
03765 static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
03766 {
03767     PTCacheEditPoint *point;
03768     int i;
03769 
03770     undo->totpoint= edit->totpoint;
03771 
03772     if(edit->psys) {
03773         ParticleData *pa;
03774 
03775         pa= undo->particles= MEM_dupallocN(edit->psys->particles);
03776 
03777         for(i=0; i<edit->totpoint; i++, pa++)
03778             pa->hair= MEM_dupallocN(pa->hair);
03779 
03780         undo->psys_flag = edit->psys->flag;
03781     }
03782     else {
03783         PTCacheMem *pm;
03784 
03785         BLI_duplicatelist(&undo->mem_cache, &edit->pid.cache->mem_cache);
03786         pm = undo->mem_cache.first;
03787 
03788         for(; pm; pm=pm->next) {
03789             for(i=0; i<BPHYS_TOT_DATA; i++)
03790                 pm->data[i] = MEM_dupallocN(pm->data[i]);
03791         }
03792     }
03793 
03794     point= undo->points = MEM_dupallocN(edit->points);
03795     undo->totpoint = edit->totpoint;
03796 
03797     for(i=0; i<edit->totpoint; i++, point++) {
03798         point->keys= MEM_dupallocN(point->keys);
03799         /* no need to update edit key->co & key->time pointers here */
03800     }
03801 }
03802 
03803 static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
03804 {
03805     ParticleSystem *psys = edit->psys;
03806     ParticleData *pa;
03807     HairKey *hkey;
03808     POINT_P; KEY_K;
03809 
03810     LOOP_POINTS {
03811         if(psys && psys->particles[p].hair)
03812             MEM_freeN(psys->particles[p].hair);
03813 
03814         if(point->keys)
03815             MEM_freeN(point->keys);
03816     }
03817     if(psys && psys->particles)
03818         MEM_freeN(psys->particles);
03819     if(edit->points)
03820         MEM_freeN(edit->points);
03821     if(edit->mirror_cache) {
03822         MEM_freeN(edit->mirror_cache);
03823         edit->mirror_cache= NULL;
03824     }
03825 
03826     edit->points= MEM_dupallocN(undo->points);
03827     edit->totpoint = undo->totpoint;
03828 
03829     LOOP_POINTS {
03830         point->keys= MEM_dupallocN(point->keys);
03831     }
03832 
03833     if(psys) {
03834         psys->particles= MEM_dupallocN(undo->particles);
03835 
03836         psys->totpart= undo->totpoint;
03837 
03838         LOOP_POINTS {
03839             pa = psys->particles + p;
03840             hkey= pa->hair = MEM_dupallocN(pa->hair);
03841 
03842             LOOP_KEYS {
03843                 key->co= hkey->co;
03844                 key->time= &hkey->time;
03845                 hkey++;
03846             }
03847         }
03848 
03849         psys->flag = undo->psys_flag;
03850     }
03851     else {
03852         PTCacheMem *pm;
03853         int i;
03854 
03855         BKE_ptcache_free_mem(&edit->pid.cache->mem_cache);
03856 
03857         BLI_duplicatelist(&edit->pid.cache->mem_cache, &undo->mem_cache);
03858 
03859         pm = edit->pid.cache->mem_cache.first;
03860 
03861         for(; pm; pm=pm->next) {
03862             for(i=0; i<BPHYS_TOT_DATA; i++)
03863                 pm->data[i] = MEM_dupallocN(pm->data[i]);
03864 
03865             BKE_ptcache_mem_pointers_init(pm);
03866 
03867             LOOP_POINTS {
03868                 LOOP_KEYS {
03869                     if((int)key->ftime == (int)pm->frame) {
03870                         key->co = pm->cur[BPHYS_DATA_LOCATION];
03871                         key->vel = pm->cur[BPHYS_DATA_VELOCITY];
03872                         key->rot = pm->cur[BPHYS_DATA_ROTATION];
03873                         key->time = &key->ftime;
03874                     }
03875                 }
03876                 BKE_ptcache_mem_pointers_incr(pm);
03877             }
03878         }
03879     }
03880 }
03881 
03882 void PE_undo_push(Scene *scene, const char *str)
03883 {
03884     PTCacheEdit *edit= PE_get_current(scene, OBACT);
03885     PTCacheUndo *undo;
03886     int nr;
03887 
03888     if(!edit) return;
03889 
03890     /* remove all undos after (also when curundo==NULL) */
03891     while(edit->undo.last != edit->curundo) {
03892         undo= edit->undo.last;
03893         BLI_remlink(&edit->undo, undo);
03894         free_PTCacheUndo(undo);
03895         MEM_freeN(undo);
03896     }
03897 
03898     /* make new */
03899     edit->curundo= undo= MEM_callocN(sizeof(PTCacheUndo), "particle undo file");
03900     BLI_strncpy(undo->name, str, sizeof(undo->name));
03901     BLI_addtail(&edit->undo, undo);
03902     
03903     /* and limit amount to the maximum */
03904     nr= 0;
03905     undo= edit->undo.last;
03906     while(undo) {
03907         nr++;
03908         if(nr==U.undosteps) break;
03909         undo= undo->prev;
03910     }
03911     if(undo) {
03912         while(edit->undo.first!=undo) {
03913             PTCacheUndo *first= edit->undo.first;
03914             BLI_remlink(&edit->undo, first);
03915             free_PTCacheUndo(first);
03916             MEM_freeN(first);
03917         }
03918     }
03919 
03920     /* copy  */
03921     make_PTCacheUndo(edit,edit->curundo);
03922 }
03923 
03924 void PE_undo_step(Scene *scene, int step)
03925 {   
03926     PTCacheEdit *edit= PE_get_current(scene, OBACT);
03927 
03928     if(!edit) return;
03929 
03930     if(step==0) {
03931         get_PTCacheUndo(edit,edit->curundo);
03932     }
03933     else if(step==1) {
03934         
03935         if(edit->curundo==NULL || edit->curundo->prev==NULL);
03936         else {
03937             if(G.f & G_DEBUG) printf("undo %s\n", edit->curundo->name);
03938             edit->curundo= edit->curundo->prev;
03939             get_PTCacheUndo(edit, edit->curundo);
03940         }
03941     }
03942     else {
03943         /* curundo has to remain current situation! */
03944         
03945         if(edit->curundo==NULL || edit->curundo->next==NULL);
03946         else {
03947             get_PTCacheUndo(edit, edit->curundo->next);
03948             edit->curundo= edit->curundo->next;
03949             if(G.f & G_DEBUG) printf("redo %s\n", edit->curundo->name);
03950         }
03951     }
03952 
03953     DAG_id_tag_update(&OBACT->id, OB_RECALC_DATA);
03954 }
03955 
03956 int PE_undo_valid(Scene *scene)
03957 {
03958     PTCacheEdit *edit= PE_get_current(scene, OBACT);
03959     
03960     if(edit) {
03961         return (edit->undo.last != edit->undo.first);
03962     }
03963     return 0;
03964 }
03965 
03966 static void PTCacheUndo_clear(PTCacheEdit *edit)
03967 {
03968     PTCacheUndo *undo;
03969 
03970     if(edit==NULL) return;
03971     
03972     undo= edit->undo.first;
03973     while(undo) {
03974         free_PTCacheUndo(undo);
03975         undo= undo->next;
03976     }
03977     BLI_freelistN(&edit->undo);
03978     edit->curundo= NULL;
03979 }
03980 
03981 void PE_undo(Scene *scene)
03982 {
03983     PE_undo_step(scene, 1);
03984 }
03985 
03986 void PE_redo(Scene *scene)
03987 {
03988     PE_undo_step(scene, -1);
03989 }
03990 
03991 void PE_undo_number(Scene *scene, int nr)
03992 {
03993     PTCacheEdit *edit= PE_get_current(scene, OBACT);
03994     PTCacheUndo *undo;
03995     int a=0;
03996     
03997     for(undo= edit->undo.first; undo; undo= undo->next, a++) {
03998         if(a==nr) break;
03999     }
04000     edit->curundo= undo;
04001     PE_undo_step(scene, 0);
04002 }
04003 
04004 
04005 /* get name of undo item, return null if no item with this index */
04006 /* if active pointer, set it to 1 if true */
04007 const char *PE_undo_get_name(Scene *scene, int nr, int *active)
04008 {
04009     PTCacheEdit *edit= PE_get_current(scene, OBACT);
04010     PTCacheUndo *undo;
04011     
04012     if(active) *active= 0;
04013     
04014     if(edit) {
04015         undo= BLI_findlink(&edit->undo, nr);
04016         if(undo) {
04017             if(active && undo==edit->curundo)
04018                 *active= 1;
04019             return undo->name;
04020         }
04021     }
04022     return NULL;
04023 }
04024 
04025 /************************ utilities ******************************/
04026 
04027 int PE_minmax(Scene *scene, float min[3], float max[3])
04028 {
04029     Object *ob= OBACT;
04030     PTCacheEdit *edit= PE_get_current(scene, ob);
04031     ParticleSystem *psys;
04032     ParticleSystemModifierData *psmd = NULL;
04033     POINT_P; KEY_K;
04034     float co[3], mat[4][4];
04035     int ok= 0;
04036 
04037     if(!edit) return ok;
04038 
04039     if((psys = edit->psys))
04040         psmd= psys_get_modifier(ob, psys);
04041     else
04042         unit_m4(mat);
04043 
04044     LOOP_VISIBLE_POINTS {
04045         if(psys)
04046             psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, psys->particles+p, mat);
04047 
04048         LOOP_SELECTED_KEYS {
04049             copy_v3_v3(co, key->co);
04050             mul_m4_v3(mat, co);
04051             DO_MINMAX(co, min, max);        
04052             ok= 1;
04053         }
04054     }
04055 
04056     if(!ok) {
04057         minmax_object(ob, min, max);
04058         ok= 1;
04059     }
04060   
04061     return ok;
04062 }
04063 
04064 /************************ particle edit toggle operator ************************/
04065 
04066 /* initialize needed data for bake edit */
04067 static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
04068 {
04069     PTCacheEdit *edit= (psys)? psys->edit : cache->edit;
04070     ParticleSystemModifierData *psmd= (psys)? psys_get_modifier(ob, psys): NULL;
04071     POINT_P; KEY_K;
04072     ParticleData *pa = NULL;
04073     HairKey *hkey;
04074     int totpoint;
04075 
04076     /* no psmd->dm happens in case particle system modifier is not enabled */
04077     if(!(psys && psmd && psmd->dm) && !cache)
04078         return;
04079 
04080     if(cache && cache->flag & PTCACHE_DISK_CACHE)
04081         return;
04082 
04083     if(psys == NULL && cache->mem_cache.first == NULL)
04084         return;
04085 
04086     if(!edit) {
04087         totpoint = psys ? psys->totpart : (int)((PTCacheMem*)cache->mem_cache.first)->totpoint;
04088 
04089         edit= MEM_callocN(sizeof(PTCacheEdit), "PE_create_particle_edit");
04090         edit->points=MEM_callocN(totpoint*sizeof(PTCacheEditPoint),"PTCacheEditPoints");
04091         edit->totpoint = totpoint;
04092 
04093         if(psys && !cache) {
04094             psys->edit= edit;
04095             edit->psys = psys;
04096 
04097             psys->free_edit= PE_free_ptcache_edit;
04098 
04099             edit->pathcache = NULL;
04100             edit->pathcachebufs.first = edit->pathcachebufs.last = NULL;
04101 
04102             pa = psys->particles;
04103             LOOP_POINTS {
04104                 point->totkey = pa->totkey;
04105                 point->keys= MEM_callocN(point->totkey*sizeof(PTCacheEditKey),"ParticleEditKeys");
04106                 point->flag |= PEP_EDIT_RECALC;
04107 
04108                 hkey = pa->hair;
04109                 LOOP_KEYS {
04110                     key->co= hkey->co;
04111                     key->time= &hkey->time;
04112                     key->flag= hkey->editflag;
04113                     if(!(psys->flag & PSYS_GLOBAL_HAIR)) {
04114                         key->flag |= PEK_USE_WCO;
04115                         hkey->editflag |= PEK_USE_WCO;
04116                     }
04117 
04118                     hkey++;
04119                 }
04120                 pa++;
04121             }
04122             update_world_cos(ob, edit);
04123         }
04124         else {
04125             PTCacheMem *pm;
04126             int totframe=0;
04127 
04128             cache->edit= edit;
04129             cache->free_edit= PE_free_ptcache_edit;
04130             edit->psys = NULL;
04131 
04132             for(pm=cache->mem_cache.first; pm; pm=pm->next)
04133                 totframe++;
04134 
04135             for(pm=cache->mem_cache.first; pm; pm=pm->next) {
04136                 LOOP_POINTS {
04137                     if(BKE_ptcache_mem_pointers_seek(p, pm) == 0)
04138                         continue;
04139 
04140                     if(!point->totkey) {
04141                         key = point->keys = MEM_callocN(totframe*sizeof(PTCacheEditKey),"ParticleEditKeys");
04142                         point->flag |= PEP_EDIT_RECALC;
04143                     }
04144                     else
04145                         key = point->keys + point->totkey;
04146 
04147                     key->co = pm->cur[BPHYS_DATA_LOCATION];
04148                     key->vel = pm->cur[BPHYS_DATA_VELOCITY];
04149                     key->rot = pm->cur[BPHYS_DATA_ROTATION];
04150                     key->ftime = (float)pm->frame;
04151                     key->time = &key->ftime;
04152                     BKE_ptcache_mem_pointers_incr(pm);
04153 
04154                     point->totkey++;
04155                 }
04156             }
04157             psys = NULL;
04158         }
04159 
04160         UI_GetThemeColor3ubv(TH_EDGE_SELECT, edit->sel_col);
04161         UI_GetThemeColor3ubv(TH_WIRE, edit->nosel_col);
04162 
04163         recalc_lengths(edit);
04164         if(psys && !cache)
04165             recalc_emitter_field(ob, psys);
04166         PE_update_object(scene, ob, 1);
04167 
04168         PTCacheUndo_clear(edit);
04169         PE_undo_push(scene, "Original");
04170     }
04171 }
04172 
04173 static int particle_edit_toggle_poll(bContext *C)
04174 {
04175     Scene *scene= CTX_data_scene(C);
04176     Object *ob= CTX_data_active_object(C);
04177 
04178     if(!scene || !ob || ob->id.lib)
04179         return 0;
04180     
04181     return (ob->particlesystem.first || modifiers_findByType(ob, eModifierType_Cloth) || modifiers_findByType(ob, eModifierType_Softbody));
04182 }
04183 
04184 static int particle_edit_toggle_exec(bContext *C, wmOperator *UNUSED(op))
04185 {
04186     Scene *scene= CTX_data_scene(C);
04187     Object *ob= CTX_data_active_object(C);
04188 
04189     if(!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
04190         PTCacheEdit *edit;
04191         ob->mode |= OB_MODE_PARTICLE_EDIT;
04192         edit= PE_create_current(scene, ob);
04193     
04194         /* mesh may have changed since last entering editmode.
04195          * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
04196         if(edit && edit->psys)
04197             recalc_emitter_field(ob, edit->psys);
04198         
04199         toggle_particle_cursor(C, 1);
04200         WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
04201     }
04202     else {
04203         ob->mode &= ~OB_MODE_PARTICLE_EDIT;
04204         toggle_particle_cursor(C, 0);
04205         WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
04206     }
04207 
04208     DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
04209 
04210     return OPERATOR_FINISHED;
04211 }
04212 
04213 void PARTICLE_OT_particle_edit_toggle(wmOperatorType *ot)
04214 {
04215     /* identifiers */
04216     ot->name= "Particle Edit Toggle";
04217     ot->idname= "PARTICLE_OT_particle_edit_toggle";
04218     
04219     /* api callbacks */
04220     ot->exec= particle_edit_toggle_exec;
04221     ot->poll= particle_edit_toggle_poll;
04222 
04223     /* flags */
04224     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
04225 }
04226 
04227 
04228 /************************ set editable operator ************************/
04229 
04230 static int clear_edited_exec(bContext *C, wmOperator *UNUSED(op))
04231 {
04232     Object *ob= CTX_data_active_object(C);
04233     ParticleSystem *psys = psys_get_current(ob);
04234     
04235     if(psys->edit) {
04236         if(psys->edit->edited || 1) { // XXX okee("Lose changes done in particle mode?")) 
04237             PE_free_ptcache_edit(psys->edit);
04238 
04239             psys->edit = NULL;
04240             psys->free_edit = NULL;
04241 
04242             psys->recalc |= PSYS_RECALC_RESET;
04243             psys->flag &= ~PSYS_GLOBAL_HAIR;
04244             psys->flag &= ~PSYS_EDITED;
04245 
04246             psys_reset(psys, PSYS_RESET_DEPSGRAPH);
04247             WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
04248             DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
04249         }
04250     }
04251     else { /* some operation might have protected hair from editing so let's clear the flag */
04252         psys->recalc |= PSYS_RECALC_RESET;
04253         psys->flag &= ~PSYS_GLOBAL_HAIR;
04254         psys->flag &= ~PSYS_EDITED;
04255         WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
04256         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
04257     }
04258 
04259     return OPERATOR_FINISHED;
04260 }
04261 
04262 void PARTICLE_OT_edited_clear(wmOperatorType *ot)
04263 {
04264     /* identifiers */
04265     ot->name= "Clear Edited";
04266     ot->idname= "PARTICLE_OT_edited_clear";
04267     
04268     /* api callbacks */
04269     ot->exec= clear_edited_exec;
04270     ot->poll= particle_edit_toggle_poll;
04271 
04272     /* flags */
04273     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
04274 }
04275