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