merge with/from trunk at r35190
[blender.git] / source / blender / editors / physics / particle_edit.c
index ac986ba7df62843e0fdd9916119c7ea6e294b68b..b5d07025ca5e377bc443313a536042ad2e8fa16f 100644 (file)
@@ -15,7 +15,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * The Original Code is Copyright (C) 2007 by Janne Karhu.
  * All rights reserved.
 #include <stdlib.h>
 #include <math.h>
 #include <string.h>
+#include <assert.h>
 
 #include "MEM_guardedalloc.h"
 
 #include "DNA_scene_types.h"
 #include "DNA_mesh_types.h"
 #include "DNA_meshdata_types.h"
-#include "DNA_modifier_types.h"
-#include "DNA_object_force.h"
-#include "DNA_object_types.h"
-#include "DNA_vec_types.h"
-#include "DNA_userdef_types.h"
 #include "DNA_view3d_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
-#include "DNA_windowmanager_types.h"
+
+#include "BLI_math.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+#include "BLI_kdtree.h"
+#include "BLI_rand.h"
+#include "BLI_utildefines.h"
 
 #include "BKE_DerivedMesh.h"
 #include "BKE_depsgraph.h"
 #include "BKE_particle.h"
 #include "BKE_report.h"
 #include "BKE_scene.h"
-#include "BKE_utildefines.h"
-#include "BKE_pointcache.h"
-
-#include "BLI_math.h"
-#include "BLI_blenlib.h"
-#include "BLI_dynstr.h"
-#include "BLI_kdtree.h"
-#include "BLI_rand.h"
 
-#include "PIL_time.h"
+#include "BKE_pointcache.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
 
+#include "ED_physics.h"
 #include "ED_mesh.h"
 #include "ED_particle.h"
 #include "ED_view3d.h"
 
-#include "UI_interface.h"
 #include "UI_resources.h"
 
 #include "WM_api.h"
@@ -88,6 +82,7 @@
 
 static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys);
 static void PTCacheUndo_clear(PTCacheEdit *edit);
+static void recalc_emitter_field(Object *ob, ParticleSystem *psys);
 
 #define KEY_K                                  PTCacheEditKey *key; int k
 #define POINT_P                                        PTCacheEditPoint *point; int p
@@ -131,7 +126,7 @@ int PE_hair_poll(bContext *C)
        return (edit && edit->psys);
 }
 
-int PE_poll_3dview(bContext *C)
+int PE_poll_view3d(bContext *C)
 {
        return PE_poll(C) && CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
                CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
@@ -167,7 +162,7 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
                edit->emitter_field= 0;
        }
 
-       psys_free_path_cache(NULL, edit);
+       psys_free_path_cache(edit->psys, edit);
 
        MEM_freeN(edit);
 }
@@ -190,10 +185,13 @@ int PE_start_edit(PTCacheEdit *edit)
 
 ParticleEditSettings *PE_settings(Scene *scene)
 {
-       return &scene->toolsettings->particle;
+       return scene->toolsettings ? &scene->toolsettings->particle : NULL;
 }
 
-/* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set */
+/* always gets atleast the first particlesystem even if PSYS_CURRENT flag is not set
+ *
+ * note: this function runs on poll, therefor it can runs many times a second
+ * keep it fast! */
 static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
 {
        ParticleEditSettings *pset= PE_settings(scene);
@@ -201,13 +199,13 @@ static PTCacheEdit *pe_get_current(Scene *scene, Object *ob, int create)
        ListBase pidlist;
        PTCacheID *pid;
 
+       if(pset==NULL || ob==NULL)
+               return NULL;
+
        pset->scene = scene;
        pset->object = ob;
 
-       if(ob==NULL)
-               return NULL;
-
-       BKE_ptcache_ids_from_object(&pidlist, ob);
+       BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
 
        /* in the case of only one editable thing, set pset->edittype accordingly */
        if(pidlist.first && pidlist.first == pidlist.last) {
@@ -316,6 +314,14 @@ void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra)
        }
 }
 
+static int pe_x_mirror(Object *ob)
+{
+       if(ob->type == OB_MESH)
+               return (((Mesh*)ob->data)->editflag & ME_EDIT_MIRROR_X);
+       
+       return 0;
+}
+
 /****************** common struct passed to callbacks ******************/
 
 typedef struct PEData {
@@ -362,10 +368,21 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
        PE_set_data(C, data);
 
        view3d_set_viewcontext(C, &data->vc);
-       view3d_get_transformation(data->vc.ar, data->vc.rv3d, data->ob, &data->mats);
-
-       if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT))
-               view3d_validate_backbuf(&data->vc);
+       /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather then (obmat * viewmat) */
+       view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats);
+
+       if((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT)) {
+               if(data->vc.v3d->flag & V3D_INVALID_BACKBUF) {
+                       /* needed or else the draw matrix can be incorrect */
+                       view3d_operator_needs_opengl(C);
+
+                       view3d_validate_backbuf(&data->vc);
+                       /* we may need to force an update here by setting the rv3d as dirty
+                        * for now it seems ok, but take care!:
+                        * rv3d->depths->dirty = 1; */
+                       view3d_update_depths(data->vc.ar);
+               }
+       }
 }
 
 /*************************** selection utilities *******************************/
@@ -373,7 +390,6 @@ static void PE_set_view3d_data(bContext *C, PEData *data)
 static int key_test_depth(PEData *data, float co[3])
 {
        View3D *v3d= data->vc.v3d;
-       RegionView3D *rv3d= data->vc.rv3d;
        double ux, uy, uz;
        float depth;
        short wco[3], x,y;
@@ -393,26 +409,26 @@ static int key_test_depth(PEData *data, float co[3])
        x=wco[0];
        y=wco[1];
 
-       // XXX verify ..
-
-       if(rv3d->depths && x<rv3d->depths->w && y<rv3d->depths->h) {
-               /* the 0.0001 is an experimental threshold to make selecting keys right next to a surface work better */
-               if((float)uz - 0.0001 > rv3d->depths->depths[y*rv3d->depths->w+x])
-                       return 0;
-               else
-                       return 1;
+#if 0 /* works well but too slow on some systems [#23118] */
+       x+= (short)data->vc.ar->winrct.xmin;
+       y+= (short)data->vc.ar->winrct.ymin;
+
+       /* PE_set_view3d_data calls this. no need to call here */
+       /* view3d_validate_backbuf(&data->vc); */
+       glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
+#else /* faster to use depths, these are calculated in PE_set_view3d_data */
+       {
+               ViewDepths *vd = data->vc.rv3d->depths;
+               assert(vd && vd->depths);
+               /* we know its not clipped */
+               depth= vd->depths[y * vd->w + x];
        }
-       else {
-               x+= (short)data->vc.ar->winrct.xmin;
-               y+= (short)data->vc.ar->winrct.ymin;
+#endif
 
-               glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
-
-               if((float)uz - 0.0001 > depth)
-                       return 0;
-               else
-                       return 1;
-       }
+       if((float)uz - 0.00001 > depth)
+               return 0;
+       else
+               return 1;
 }
 
 static int key_inside_circle(PEData *data, float rad, float co[3], float *distance)
@@ -469,13 +485,10 @@ static int key_inside_test(PEData *data, float co[3])
 static int point_is_selected(PTCacheEditPoint *point)
 {
        KEY_K;
-       int sel;
 
        if(point->flag & PEP_HIDE)
                return 0;
 
-       sel= 0;
-
        LOOP_SELECTED_KEYS {
                return 1;
        }
@@ -578,7 +591,7 @@ static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected
        ParticleSystemModifierData *psmd = NULL;
        ParticleEditSettings *pset= PE_settings(data->scene);
        POINT_P; KEY_K;
-       float mat[4][4], imat[4][4];
+       float mat[4][4]= MAT4_UNITY, imat[4][4]= MAT4_UNITY;
 
        if(edit->psys)
                psmd= psys_get_modifier(data->ob, edit->psys);
@@ -587,29 +600,35 @@ static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected
        if(pset->selectmode==SCE_SELECT_PATH)
                selected= 0;
 
-       unit_m4(imat);
-       unit_m4(mat);
-
        LOOP_VISIBLE_POINTS {
-               if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
-                       psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
-                       invert_m4_m4(imat,mat);
-               }
-
                if(pset->selectmode==SCE_SELECT_END) {
                        /* only do end keys */
                        key= point->keys + point->totkey-1;
 
-                       if(selected==0 || key->flag & PEK_SELECT)
-                               if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
+                       if(selected==0 || key->flag & PEK_SELECT) {
+                               if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
+                                       if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
+                                               psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
+                                               invert_m4_m4(imat,mat);
+                                       }
+
                                        func(data, mat, imat, p, point->totkey-1, key);
+                               }
+                       }
                }
                else {
                        /* do all keys */
                        LOOP_VISIBLE_KEYS {
-                               if(selected==0 || key->flag & PEK_SELECT)
-                                       if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist))
+                               if(selected==0 || key->flag & PEK_SELECT) {
+                                       if(key_inside_circle(data, data->rad, KEY_WCO, &data->dist)) {
+                                               if(edit->psys && !(edit->psys->flag & PSYS_GLOBAL_HAIR)) {
+                                                       psys_mat_hair_to_global(data->ob, psmd->dm, psys->part->from, psys->particles + p, mat);
+                                                       invert_m4_m4(imat,mat);
+                                               }
+
                                                func(data, mat, imat, p, k, key);
+                                       }
+                               }
                        }
                }
        }
@@ -752,6 +771,9 @@ static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys
        if(!mpa) {
                if(!edit->mirror_cache)
                        PE_update_mirror_cache(ob, psys);
+               
+               if(!edit->mirror_cache)
+                       return; /* something went wrong! */
 
                mi= edit->mirror_cache[i];
                if(mi == -1)
@@ -770,6 +792,7 @@ static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys
                if(mpoint->keys) MEM_freeN(mpoint->keys);
 
                mpa->hair= MEM_dupallocN(pa->hair);
+               mpa->totkey= pa->totkey;
                mpoint->keys= MEM_dupallocN(point->keys);
                mpoint->totkey= point->totkey;
 
@@ -778,7 +801,7 @@ static void PE_mirror_particle(Object *ob, DerivedMesh *dm, ParticleSystem *psys
                for(k=0; k<mpa->totkey; k++, mkey++, mhkey++) {
                        mkey->co= mhkey->co;
                        mkey->time= &mhkey->time;
-                       mkey->flag &= PEK_SELECT;
+                       mkey->flag &= ~PEK_SELECT;
                }
        }
 
@@ -819,9 +842,15 @@ static void PE_apply_mirror(Object *ob, ParticleSystem *psys)
        edit= psys->edit;
        psmd= psys_get_modifier(ob, psys);
 
-       if(!edit->mirror_cache || !psmd->dm)
+       if(!psmd->dm)
                return;
 
+       if(!edit->mirror_cache)
+               PE_update_mirror_cache(ob, psys);
+
+       if(!edit->mirror_cache)
+               return; /* something went wrong */
+
        /* we delay settings the PARS_EDIT_RECALC for mirrored particles
         * to avoid doing mirror twice */
        LOOP_POINTS {
@@ -890,13 +919,13 @@ static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
                                        if(dot<dist_1st) {
                                                normalize_v3(dvec);
                                                mul_v3_fl(dvec,dist_1st-dot);
-                                               add_v3_v3v3(key->co,key->co,dvec);
+                                               add_v3_v3(key->co, dvec);
                                        }
                                }
                                else {
                                        normalize_v3(dvec);
                                        mul_v3_fl(dvec,dist_1st-dot);
-                                       add_v3_v3v3(key->co,key->co,dvec);
+                                       add_v3_v3(key->co, dvec);
                                }
                                if(k==1)
                                        dist_1st*=1.3333f;
@@ -911,7 +940,7 @@ static void pe_deflect_emitter(Scene *scene, Object *ob, PTCacheEdit *edit)
        }
 }
 /* force set distances between neighbouring keys */
-void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
+static void PE_apply_lengths(Scene *scene, PTCacheEdit *edit)
 {
        
        ParticleEditSettings *pset=PE_settings(scene);
@@ -982,7 +1011,7 @@ static void pe_iterate_lengths(Scene *scene, PTCacheEdit *edit)
                                }
 
                                if(k) {
-                                       add_v3_v3v3((key-1)->co,(key-1)->co,dv1);
+                                       add_v3_v3((key-1)->co, dv1);
                                }
 
                                VECADD(dv1,dv0,dv2);
@@ -1011,10 +1040,8 @@ static void recalc_emitter_field(Object *ob, ParticleSystem *psys)
 {
        DerivedMesh *dm=psys_get_modifier(ob,psys)->dm;
        PTCacheEdit *edit= psys->edit;
-       MFace *mface;
-       MVert *mvert;
        float *vec, *nor;
-       int i, totface, totvert;
+       int i, totface /*, totvert*/;
 
        if(!dm)
                return;
@@ -1024,8 +1051,8 @@ static void recalc_emitter_field(Object *ob, ParticleSystem *psys)
 
        BLI_kdtree_free(edit->emitter_field);
 
-       totface=dm->getNumFaces(dm);
-       totvert=dm->getNumVerts(dm);
+       totface=dm->getNumTessFaces(dm);
+       /*totvert=dm->getNumVerts(dm);*/ /*UNSUED*/
 
        edit->emitter_cosnos=MEM_callocN(totface*6*sizeof(float),"emitter cosnos");
 
@@ -1034,9 +1061,9 @@ static void recalc_emitter_field(Object *ob, ParticleSystem *psys)
        vec=edit->emitter_cosnos;
        nor=vec+3;
 
-       mvert=dm->getVertDataArray(dm,CD_MVERT);
        for(i=0; i<totface; i++, vec+=6, nor+=6) {
-               mface=dm->getFaceData(dm,i,CD_MFACE);
+               MFace *mface=dm->getTessFaceData(dm,i,CD_MFACE);
+               MVert *mvert;
 
                mvert=dm->getVertData(dm,mface->v1,CD_MVERT);
                VECCOPY(vec,mvert->co);
@@ -1118,7 +1145,7 @@ static void update_world_cos(Object *ob, PTCacheEdit *edit)
                }
        }
 }
-static void update_velocities(Object *ob, PTCacheEdit *edit)
+static void update_velocities(PTCacheEdit *edit)
 {
        /*TODO: get frs_sec properly */
        float vec1[3], vec2[3], frs_sec, dfra;
@@ -1196,12 +1223,12 @@ void PE_update_object(Scene *scene, Object *ob, int useflag)
        pe_iterate_lengths(scene, edit);
        pe_deflect_emitter(scene, ob, edit);
        PE_apply_lengths(scene, edit);
-       if(pset->flag & PE_X_MIRROR)
+       if(pe_x_mirror(ob))
                PE_apply_mirror(ob,edit->psys);
        if(edit->psys)
                update_world_cos(ob, edit);
        if(pset->flag & PE_AUTO_VELOCITY)
-               update_velocities(ob, edit);
+               update_velocities(edit);
        PE_hide_keys_time(scene, edit, CFRA);
 
        /* regenerate path caches */
@@ -1236,7 +1263,7 @@ static void select_key(PEData *data, int point_index, int key_index)
        point->flag |= PEP_EDIT_RECALC;
 }
 
-static void select_keys(PEData *data, int point_index, int key_index)
+static void select_keys(PEData *data, int point_index, int UNUSED(key_index))
 {
        PTCacheEdit *edit = data->edit;
        PTCacheEditPoint *point = edit->points + point_index;
@@ -1314,7 +1341,7 @@ static int select_all_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1364,7 +1391,7 @@ int PE_mouse_particles(bContext *C, short *mval, int extend)
        for_mouse_hit_keys(&data, toggle_key_select, 1);  /* nearest only */
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1373,11 +1400,14 @@ int PE_mouse_particles(bContext *C, short *mval, int extend)
 
 static void select_root(PEData *data, int point_index)
 {
+       if (data->edit->points[point_index].flag & PEP_HIDE)
+               return;
+       
        data->edit->points[point_index].keys->flag |= PEK_SELECT;
        data->edit->points[point_index].flag |= PEP_EDIT_RECALC; /* redraw selection only */
 }
 
-static int select_first_exec(bContext *C, wmOperator *op)
+static int select_roots_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
 
@@ -1385,19 +1415,19 @@ static int select_first_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_root);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_select_first(wmOperatorType *ot)
+void PARTICLE_OT_select_roots(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Select First";
-       ot->idname= "PARTICLE_OT_select_first";
+       ot->name= "Select Roots";
+       ot->idname= "PARTICLE_OT_select_roots";
        
        /* api callbacks */
-       ot->exec= select_first_exec;
+       ot->exec= select_roots_exec;
        ot->poll= PE_poll;
 
        /* flags */
@@ -1409,11 +1439,15 @@ void PARTICLE_OT_select_first(wmOperatorType *ot)
 static void select_tip(PEData *data, int point_index)
 {
        PTCacheEditPoint *point = data->edit->points + point_index;
+       
+       if (point->flag & PEP_HIDE)
+               return;
+       
        point->keys[point->totkey - 1].flag |= PEK_SELECT;
        point->flag |= PEP_EDIT_RECALC; /* redraw selection only */
 }
 
-static int select_last_exec(bContext *C, wmOperator *op)
+static int select_tips_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
 
@@ -1421,19 +1455,19 @@ static int select_last_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_tip);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
 
-void PARTICLE_OT_select_last(wmOperatorType *ot)
+void PARTICLE_OT_select_tips(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Select Last";
-       ot->idname= "PARTICLE_OT_select_last";
+       ot->name= "Select Tips";
+       ot->idname= "PARTICLE_OT_select_tips";
        
        /* api callbacks */
-       ot->exec= select_last_exec;
+       ot->exec= select_tips_exec;
        ot->poll= PE_poll;
 
        /* flags */
@@ -1452,8 +1486,6 @@ static int select_linked_exec(bContext *C, wmOperator *op)
        mval[0]= location[0];
        mval[1]= location[1];
 
-       view3d_operator_needs_opengl(C);
-
        PE_set_view3d_data(C, &data);
        data.mval= mval;
        data.rad=75.0f;
@@ -1461,7 +1493,7 @@ static int select_linked_exec(bContext *C, wmOperator *op)
 
        for_mouse_hit_keys(&data, select_keys, 1);  /* nearest only */
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1487,7 +1519,7 @@ void PARTICLE_OT_select_linked(wmOperatorType *ot)
        /* api callbacks */
        ot->exec= select_linked_exec;
        ot->invoke= select_linked_invoke;
-       ot->poll= PE_poll_3dview;
+       ot->poll= PE_poll_view3d;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
@@ -1498,6 +1530,17 @@ void PARTICLE_OT_select_linked(wmOperatorType *ot)
 }
 
 /************************ border select operator ************************/
+void PE_deselect_all_visible(PTCacheEdit *edit)
+{
+       POINT_P; KEY_K;
+
+       LOOP_VISIBLE_POINTS {
+               LOOP_SELECTED_KEYS {
+                       key->flag &= ~PEK_SELECT;
+                       point->flag |= PEP_EDIT_RECALC;
+               }
+       }
+}
 
 int PE_border_select(bContext *C, rcti *rect, int select, int extend)
 {
@@ -1509,16 +1552,8 @@ int PE_border_select(bContext *C, rcti *rect, int select, int extend)
        if(!PE_start_edit(edit))
                return OPERATOR_CANCELLED;
 
-       if (extend == 0 && select) {
-               POINT_P; KEY_K;
-
-               LOOP_VISIBLE_POINTS {
-                       LOOP_SELECTED_KEYS {
-                               key->flag &= ~PEK_SELECT;
-                               point->flag |= PEP_EDIT_RECALC;
-                       }
-               }
-       }
+       if (extend == 0 && select)
+               PE_deselect_all_visible(edit);
 
        PE_set_view3d_data(C, &data);
        data.rect= rect;
@@ -1527,7 +1562,7 @@ int PE_border_select(bContext *C, rcti *rect, int select, int extend)
        for_mouse_hit_keys(&data, select_key, 0);
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1552,14 +1587,14 @@ int PE_circle_select(bContext *C, int selecting, short *mval, float rad)
        for_mouse_hit_keys(&data, select_key, 0);
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
 
 /************************ lasso select operator ************************/
 
-int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
+int PE_lasso_select(bContext *C, short mcords[][2], short moves, short extend, short select)
 {
        Scene *scene= CTX_data_scene(C);
        Object *ob= CTX_data_active_object(C);
@@ -1569,13 +1604,19 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
        ParticleSystem *psys = edit->psys;
        ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
        POINT_P; KEY_K;
-       float co[3], mat[4][4];
+       float co[3], mat[4][4]= MAT4_UNITY;
        short vertco[2];
 
+       PEData data;
+
        if(!PE_start_edit(edit))
                return OPERATOR_CANCELLED;
 
-       unit_m4(mat);
+       if (extend == 0 && select)
+               PE_deselect_all_visible(edit);
+
+       /* only for depths */
+       PE_set_view3d_data(C, &data);
 
        LOOP_VISIBLE_POINTS {
                if(edit->psys && !(psys->flag & PSYS_GLOBAL_HAIR))
@@ -1586,7 +1627,7 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
                                VECCOPY(co, key->co);
                                mul_m4_v3(mat, co);
                                project_short(ar, co, vertco);
-                               if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1])) {
+                               if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1]) && key_test_depth(&data, co)) {
                                        if(select && !(key->flag & PEK_SELECT)) {
                                                key->flag |= PEK_SELECT;
                                                point->flag |= PEP_EDIT_RECALC;
@@ -1604,7 +1645,7 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
                        VECCOPY(co, key->co);
                        mul_m4_v3(mat, co);
                        project_short(ar, co,vertco);
-                       if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1])) {
+                       if((vertco[0] != IS_CLIPPED) && lasso_inside(mcords,moves,vertco[0],vertco[1]) && key_test_depth(&data, co)) {
                                if(select && !(key->flag & PEK_SELECT)) {
                                        key->flag |= PEK_SELECT;
                                        point->flag |= PEP_EDIT_RECALC;
@@ -1618,7 +1659,7 @@ int PE_lasso_select(bContext *C, short mcords[][2], short moves, short select)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1652,7 +1693,7 @@ static int hide_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1676,7 +1717,7 @@ void PARTICLE_OT_hide(wmOperatorType *ot)
 
 /*************************** reveal operator **************************/
 
-static int reveal_exec(bContext *C, wmOperator *op)
+static int reveal_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Object *ob= CTX_data_active_object(C);
        Scene *scene= CTX_data_scene(C);
@@ -1694,7 +1735,7 @@ static int reveal_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(scene, ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1744,7 +1785,7 @@ static void select_less_keys(PEData *data, int point_index)
        }
 }
 
-static int select_less_exec(bContext *C, wmOperator *op)
+static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
 
@@ -1752,7 +1793,7 @@ static int select_less_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_less_keys);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1805,7 +1846,7 @@ static void select_more_keys(PEData *data, int point_index)
        }
 }
 
-static int select_more_exec(bContext *C, wmOperator *op)
+static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
 
@@ -1813,7 +1854,7 @@ static int select_more_exec(bContext *C, wmOperator *op)
        foreach_point(&data, select_more_keys);
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1832,7 +1873,7 @@ void PARTICLE_OT_select_more(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-static int select_inverse_exec(bContext *C, wmOperator *op)
+static int select_inverse_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
        PTCacheEdit *edit;
@@ -1850,7 +1891,7 @@ static int select_inverse_exec(bContext *C, wmOperator *op)
        }
 
        PE_update_selection(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1875,7 +1916,7 @@ static void rekey_particle(PEData *data, int pa_index)
 {
        PTCacheEdit *edit= data->edit;
        ParticleSystem *psys= edit->psys;
-       ParticleSimulationData sim = {data->scene, data->ob, edit->psys, NULL};
+       ParticleSimulationData sim= {0};
        ParticleData *pa= psys->particles + pa_index;
        PTCacheEditPoint *point = edit->points + pa_index;
        ParticleKey state;
@@ -1884,6 +1925,10 @@ static void rekey_particle(PEData *data, int pa_index)
        float dval, sta, end;
        int k;
 
+       sim.scene= data->scene;
+       sim.ob= data->ob;
+       sim.psys= edit->psys;
+
        pa->flag |= PARS_REKEY;
 
        key= new_keys= MEM_callocN(data->totrekey * sizeof(HairKey),"Hair re-key keys");
@@ -1941,7 +1986,7 @@ static int rekey_exec(bContext *C, wmOperator *op)
        
        recalc_lengths(data.edit);
        PE_update_object(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -1968,7 +2013,7 @@ static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float
 {
        PTCacheEdit *edit= PE_get_current(scene, ob);
        ParticleSystem *psys;
-       ParticleSimulationData sim = {scene, ob, edit ? edit->psys : NULL, NULL};
+       ParticleSimulationData sim= {0};
        ParticleData *pa;
        ParticleKey state;
        HairKey *new_keys, *key;
@@ -1979,6 +2024,10 @@ static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float
 
        psys = edit->psys;
 
+       sim.scene= scene;
+       sim.ob= ob;
+       sim.psys= psys;
+
        pa= psys->particles + pa_index;
 
        pa->flag |= PARS_REKEY;
@@ -2008,20 +2057,18 @@ static void rekey_particle_to_time(Scene *scene, Object *ob, int pa_index, float
 
 /************************* utilities **************************/
 
-static int remove_tagged_particles(Scene *scene, Object *ob, ParticleSystem *psys)
+static int remove_tagged_particles(Object *ob, ParticleSystem *psys, int mirror)
 {
        PTCacheEdit *edit = psys->edit;
-       ParticleEditSettings *pset= PE_settings(scene);
        ParticleData *pa, *npa=0, *new_pars=0;
        POINT_P;
        PTCacheEditPoint *npoint=0, *new_points=0;
        ParticleSystemModifierData *psmd;
-       int i, totpart, new_totpart= psys->totpart, removed= 0;
+       int i, new_totpart= psys->totpart, removed= 0;
 
-       if(pset->flag & PE_X_MIRROR) {
+       if(mirror) {
                /* mirror tags */
                psmd= psys_get_modifier(ob, psys);
-               totpart= psys->totpart;
 
                LOOP_TAGGED_POINTS {
                        PE_mirror_particle(ob, psmd->dm, psys, psys->particles + p, NULL);
@@ -2037,6 +2084,15 @@ static int remove_tagged_particles(Scene *scene, Object *ob, ParticleSystem *psy
                if(new_totpart) {
                        npa= new_pars= MEM_callocN(new_totpart * sizeof(ParticleData), "ParticleData array");
                        npoint= new_points= MEM_callocN(new_totpart * sizeof(PTCacheEditPoint), "PTCacheEditKey array");
+
+                       if(ELEM(NULL, new_pars, new_points)) {
+                                /* allocation error! */
+                               if(new_pars)
+                                       MEM_freeN(new_pars);
+                               if(new_points)
+                                       MEM_freeN(new_points);
+                               return 0;
+                       }
                }
 
                pa= psys->particles;
@@ -2079,17 +2135,17 @@ static int remove_tagged_particles(Scene *scene, Object *ob, ParticleSystem *psy
        return removed;
 }
 
-static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
+static void remove_tagged_keys(Object *ob, ParticleSystem *psys)
 {
        PTCacheEdit *edit= psys->edit;
-       ParticleEditSettings *pset= PE_settings(scene);
        ParticleData *pa;
        HairKey *hkey, *nhkey, *new_hkeys=0;
        POINT_P; KEY_K;
+       PTCacheEditKey *nkey, *new_keys;
        ParticleSystemModifierData *psmd;
        short new_totkey;
 
-       if(pset->flag & PE_X_MIRROR) {
+       if(pe_x_mirror(ob)) {
                /* mirror key tags */
                psmd= psys_get_modifier(ob, psys);
 
@@ -2110,7 +2166,7 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
                if(new_totkey < 2)
                        point->flag |= PEP_TAG;
        }
-       remove_tagged_particles(scene, ob, psys);
+       remove_tagged_particles(ob, psys, pe_x_mirror(ob));
 
        LOOP_POINTS {
                pa = psys->particles + p;
@@ -2121,9 +2177,10 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
                }
 
                if(new_totkey != pa->totkey) {
-                       hkey= pa->hair;
                        nhkey= new_hkeys= MEM_callocN(new_totkey*sizeof(HairKey), "HairKeys");
+                       nkey= new_keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
 
+                       hkey= pa->hair;
                        LOOP_KEYS {
                                while(key->flag & PEK_TAG && hkey < pa->hair + pa->totkey) {
                                        key++;
@@ -2132,29 +2189,36 @@ static void remove_tagged_keys(Scene *scene, Object *ob, ParticleSystem *psys)
 
                                if(hkey < pa->hair + pa->totkey) {
                                        VECCOPY(nhkey->co, hkey->co);
+                                       nhkey->editflag = hkey->editflag;
                                        nhkey->time= hkey->time;
                                        nhkey->weight= hkey->weight;
+                                       
+                                       nkey->co= nhkey->co;
+                                       nkey->time= &nhkey->time;
+                                       /* these can be copied from old edit keys */
+                                       nkey->flag = key->flag;
+                                       nkey->ftime = key->ftime;
+                                       nkey->length = key->length;
+                                       VECCOPY(nkey->world_co, key->world_co);
                                }
-                               hkey++;
+                               nkey++;
                                nhkey++;
+                               hkey++;
                        }
+
                        if(pa->hair)
                                MEM_freeN(pa->hair);
+
+                       if(point->keys)
+                               MEM_freeN(point->keys);
                        
                        pa->hair= new_hkeys;
+                       point->keys= new_keys;
 
                        point->totkey= pa->totkey= new_totkey;
 
-                       if(point->keys)
-                               MEM_freeN(point->keys);
-                       key= point->keys= MEM_callocN(new_totkey*sizeof(PTCacheEditKey), "particle edit keys");
-
-                       hkey = pa->hair;
-                       LOOP_KEYS {
-                               key->co= hkey->co;
-                               key->time= &hkey->time;
-                               hkey++;
-                       }
+                       /* flag for recalculating length */
+                       point->flag |= PEP_EDIT_RECALC;
                }
        }
 }
@@ -2166,7 +2230,7 @@ static void subdivide_particle(PEData *data, int pa_index)
 {
        PTCacheEdit *edit= data->edit;
        ParticleSystem *psys= edit->psys;
-       ParticleSimulationData sim = {data->scene, data->ob, edit->psys, NULL};
+       ParticleSimulationData sim= {0};
        ParticleData *pa= psys->particles + pa_index;
        PTCacheEditPoint *point = edit->points + pa_index;
        ParticleKey state;
@@ -2177,6 +2241,10 @@ static void subdivide_particle(PEData *data, int pa_index)
        short totnewkey=0;
        float endtime;
 
+       sim.scene= data->scene;
+       sim.ob= data->ob;
+       sim.psys= edit->psys;
+
        for(k=0, ekey=point->keys; k<pa->totkey-1; k++,ekey++) {
                if(ekey->flag&PEK_SELECT && (ekey+1)->flag&PEK_SELECT)
                        totnewkey++;
@@ -2239,7 +2307,7 @@ static void subdivide_particle(PEData *data, int pa_index)
        pa->flag &= ~PARS_REKEY;
 }
 
-static int subdivide_exec(bContext *C, wmOperator *op)
+static int subdivide_exec(bContext *C, wmOperator *UNUSED(op))
 {
        PEData data;
 
@@ -2248,7 +2316,7 @@ static int subdivide_exec(bContext *C, wmOperator *op)
        
        recalc_lengths(data.edit);
        PE_update_object(data.scene, data.ob, 1);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2273,7 +2341,6 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        Object *ob= CTX_data_active_object(C);
-       ParticleEditSettings *pset=PE_settings(scene);
        PTCacheEdit *edit= PE_get_current(scene, ob);
        ParticleSystem *psys = edit->psys;
        ParticleSystemModifierData *psmd;
@@ -2281,7 +2348,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
        KDTreeNearest nearest[10];
        POINT_P;
        float mat[4][4], co[3], threshold= RNA_float_get(op->ptr, "threshold");
-       int n, totn, removed, flag, totremoved;
+       int n, totn, removed, totremoved;
 
        if(psys->flag & PSYS_GLOBAL_HAIR)
                return OPERATOR_CANCELLED;
@@ -2327,10 +2394,7 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
                BLI_kdtree_free(tree);
 
                /* remove tagged particles - don't do mirror here! */
-               flag= pset->flag;
-               pset->flag &= ~PE_X_MIRROR;
-               remove_tagged_particles(scene, ob, psys);
-               pset->flag= flag;
+               remove_tagged_particles(ob, psys, 0);
                totremoved += removed;
        } while(removed);
 
@@ -2339,8 +2403,8 @@ static int remove_doubles_exec(bContext *C, wmOperator *op)
 
        BKE_reportf(op->reports, RPT_INFO, "Remove %d double particles.", totremoved);
 
-       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2362,9 +2426,58 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
        RNA_def_float(ot->srna, "threshold", 0.0002f, 0.0f, FLT_MAX, "Threshold", "Threshold distance withing which particles are removed", 0.00001f, 0.1f);
 }
 
+
+static int weight_set_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       ParticleEditSettings *pset= PE_settings(scene);
+       Object *ob= CTX_data_active_object(C);
+       PTCacheEdit *edit= PE_get_current(scene, ob);
+       ParticleSystem *psys = edit->psys;
+       POINT_P;
+       KEY_K;
+       HairKey *hkey;
+       float weight;
+       ParticleBrushData *brush= &pset->brush[pset->brushtype];
+    float factor= RNA_float_get(op->ptr, "factor");
+
+       weight= brush->strength;
+       edit= psys->edit;
+
+       LOOP_SELECTED_POINTS {
+               ParticleData *pa= psys->particles + p;
+
+               LOOP_SELECTED_KEYS {
+                       hkey= pa->hair + k;
+                       hkey->weight= interpf(weight, hkey->weight, factor);
+               }
+       }
+
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+
+       return OPERATOR_FINISHED;
+}
+
+void PARTICLE_OT_weight_set(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Weight Set";
+       ot->idname= "PARTICLE_OT_weight_set";
+
+       /* api callbacks */
+       ot->exec= weight_set_exec;
+       ot->poll= PE_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+    
+    RNA_def_float(ot->srna, "factor", 1, 0, 1, "Factor", "", 0, 1);
+}
+
 /************************ cursor drawing *******************************/
 
-static void brush_drawcursor(bContext *C, int x, int y, void *customdata)
+static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata))
 {
        ParticleEditSettings *pset= PE_settings(CTX_data_scene(C));
        ParticleBrushData *brush;
@@ -2399,7 +2512,7 @@ static void toggle_particle_cursor(bContext *C, int enable)
                pset->paintcursor = NULL;
        }
        else if(enable)
-               pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_3dview, brush_drawcursor, NULL);
+               pset->paintcursor= WM_paint_cursor_activate(CTX_wm_manager(C), PE_poll_view3d, brush_drawcursor, NULL);
 }
 
 /********************* radial control operator *********************/
@@ -2455,6 +2568,8 @@ static int brush_radial_control_exec(bContext *C, wmOperator *op)
        else if(mode == WM_RADIALCONTROL_STRENGTH)
                brush->strength= new_value;
 
+       WM_event_add_notifier(C, NC_WINDOW, NULL);
+
        return OPERATOR_FINISHED;
 }
 
@@ -2506,17 +2621,17 @@ static int delete_exec(bContext *C, wmOperator *op)
 
        if(type == DEL_KEY) {
                foreach_selected_key(&data, set_delete_particle_key);
-               remove_tagged_keys(data.scene, data.ob, data.edit->psys);
+               remove_tagged_keys(data.ob, data.edit->psys);
                recalc_lengths(data.edit);
        }
        else if(type == DEL_PARTICLE) {
                foreach_selected_point(&data, set_delete_particle);
-               remove_tagged_particles(data.scene, data.ob, data.edit->psys);
+               remove_tagged_particles(data.ob, data.edit->psys, pe_x_mirror(data.ob));
                recalc_lengths(data.edit);
        }
 
-       DAG_id_flush_update(&data.ob->id, OB_RECALC_DATA);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, data.ob);
+       DAG_id_tag_update(&data.ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, data.ob);
 
        return OPERATOR_FINISHED;
 }
@@ -2536,7 +2651,7 @@ void PARTICLE_OT_delete(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 
        /* properties */
-       RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys.");
+       ot->prop= RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PARTICLE, "Type", "Delete a full particle or only keys.");
 }
 
 /*************************** mirror operator **************************/
@@ -2561,7 +2676,7 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
        if(!psmd->dm)
                return;
 
-       mirrorfaces= mesh_get_x_mirror_faces(ob, NULL);
+       //BMESH_TODO mirrorfaces= mesh_get_x_mirror_faces(ob, NULL);
 
        if(!edit->mirror_cache)
                PE_update_mirror_cache(ob, psys);
@@ -2614,9 +2729,11 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
                newpa= psys->particles + totpart;
                newpoint= edit->points + totpart;
 
-               LOOP_VISIBLE_POINTS {
+               for(p=0, point=edit->points; p<totpart; p++, point++) {
                        pa = psys->particles + p;
 
+                       if(point->flag & PEP_HIDE)
+                               continue;
                        if(!(point->flag & PEP_TAG) || mirrorfaces[pa->num*2] == -1)
                                continue;
 
@@ -2664,7 +2781,7 @@ static void PE_mirror_x(Scene *scene, Object *ob, int tagged)
        MEM_freeN(mirrorfaces);
 }
 
-static int mirror_exec(bContext *C, wmOperator *op)
+static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Scene *scene= CTX_data_scene(C);
        Object *ob= CTX_data_active_object(C);
@@ -2673,8 +2790,8 @@ static int mirror_exec(bContext *C, wmOperator *op)
        PE_mirror_x(scene, ob, 0);
 
        update_world_cos(ob, edit);
-       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
-       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 
        return OPERATOR_FINISHED;
 }
@@ -2693,82 +2810,9 @@ void PARTICLE_OT_mirror(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-/*********************** set brush operator **********************/
-
-static EnumPropertyItem brush_type_items[]= {
-       {PE_BRUSH_NONE, "NONE", 0, "None", ""},
-       {PE_BRUSH_COMB, "COMB", 0, "Comb", ""},
-       {PE_BRUSH_SMOOTH, "SMOOTH", 0, "Smooth", ""},
-       {PE_BRUSH_ADD, "ADD", 0, "Add", ""},
-       {PE_BRUSH_LENGTH, "LENGTH", 0, "Length", ""},
-       {PE_BRUSH_PUFF, "PUFF", 0, "Puff", ""},
-       {PE_BRUSH_CUT, "CUT", 0, "Cut", ""},
-       {0, NULL, 0, NULL, NULL}
-};
-
-static int set_brush_exec(bContext *C, wmOperator *op)
-{
-       Scene *scene= CTX_data_scene(C);
-       ParticleEditSettings *pset= PE_settings(scene);
-
-       pset->brushtype= RNA_enum_get(op->ptr, "type");
-
-       return OPERATOR_FINISHED;
-}
-
-void PARTICLE_OT_brush_set(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Set Brush";
-       ot->idname= "PARTICLE_OT_brush_set";
-       
-       /* api callbacks */
-       ot->exec= set_brush_exec;
-       ot->invoke= WM_menu_invoke;
-       ot->poll= PE_poll;
-
-       /* properties */
-       RNA_def_enum(ot->srna, "type", brush_type_items, PE_BRUSH_NONE, "Type", "Brush type to select for editing.");
-}
-
-
-/*********************** set mode operator **********************/
-
-static EnumPropertyItem edit_type_items[]= {
-       {PE_TYPE_PARTICLES, "PARTICLES", 0, "Particles", ""},
-       {PE_TYPE_SOFTBODY, "SOFTBODY", 0, "Soft body", ""},
-       {PE_TYPE_CLOTH, "CLOTH", 0, "Cloth", ""},
-       {0, NULL, 0, NULL, NULL}
-};
-
-static int set_edit_mode_exec(bContext *C, wmOperator *op)
-{
-       Scene *scene= CTX_data_scene(C);
-       ParticleEditSettings *pset= PE_settings(scene);
-
-       pset->edittype= RNA_enum_get(op->ptr, "type");
-
-       return OPERATOR_FINISHED;
-}
-
-void PARTICLE_OT_edit_type_set(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Set Edit Type";
-       ot->idname= "PARTICLE_OT_edit_type_set";
-       
-       /* api callbacks */
-       ot->exec= set_edit_mode_exec;
-       ot->invoke= WM_menu_invoke;
-       ot->poll= PE_poll;
-
-       /* properties */
-       RNA_def_enum(ot->srna, "type", edit_type_items, PE_TYPE_PARTICLES, "Type", "Edit type to select for editing.");
-}
-
 /************************* brush edit callbacks ********************/
 
-static void brush_comb(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
+static void brush_comb(PEData *data, float UNUSED(mat[][4]), float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
 {
        ParticleEditSettings *pset= PE_settings(data->scene);
        float cvec[3], fac;
@@ -2911,7 +2955,16 @@ static void brush_puff(PEData *data, int point_index)
        PTCacheEditPoint *point = edit->points + point_index;
        KEY_K;
        float mat[4][4], imat[4][4];
-       float lastco[3], rootco[3] = {0.0f, 0.0f, 0.0f}, co[3], nor[3], kco[3], dco[3], fac=0.0f, length=0.0f;
+
+       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;
+       int puff_volume = 0;
+       int change= 0;
+
+       {
+               ParticleEditSettings *pset= PE_settings(data->scene);
+               ParticleBrushData *brush= &pset->brush[pset->brushtype];
+               puff_volume = brush->flag & PE_BRUSH_DATA_PUFF_VOLUME;
+       }
 
        if(psys && !(psys->flag & PSYS_GLOBAL_HAIR)) {
                psys_mat_hair_to_global(data->ob, data->dm, psys->part->from, psys->particles + point_index, mat);
@@ -2927,12 +2980,15 @@ static void brush_puff(PEData *data, int point_index)
                        /* find root coordinate and normal on emitter */
                        VECCOPY(co, key->co);
                        mul_m4_v3(mat, co);
+                       mul_v3_m4v3(kco, data->ob->imat, co); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
 
-                       point_index= BLI_kdtree_find_nearest(edit->emitter_field, co, NULL, NULL);
+                       point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
                        if(point_index == -1) return;
 
                        VECCOPY(rootco, co);
                        copy_v3_v3(nor, &edit->emitter_cosnos[point_index*6+3]);
+                       mul_mat3_m4_v3(data->ob->obmat, nor); /* normal into worldspace */
+
                        normalize_v3(nor);
                        length= 0.0f;
 
@@ -2942,27 +2998,104 @@ static void brush_puff(PEData *data, int point_index)
                                fac= -fac;
                }
                else {
-                       /* compute position as if hair was standing up straight */
+                       /* compute position as if hair was standing up straight.
+                        * */
                        VECCOPY(lastco, co);
                        VECCOPY(co, key->co);
                        mul_m4_v3(mat, co);
                        length += len_v3v3(lastco, co);
+                       if((data->select==0 || (key->flag & PEK_SELECT)) && !(key->flag & PEK_HIDE)) {
+                               VECADDFAC(kco, rootco, nor, length);
+
+                               /* blend between the current and straight position */
+                               VECSUB(dco, kco, co);
+                               VECADDFAC(co, co, dco, fac);
 
-                       VECADDFAC(kco, rootco, nor, length);
+                               /* re-use dco to compare before and after translation and add to the offset  */
+                               VECCOPY(dco, key->co);
 
-                       /* blend between the current and straight position */
-                       VECSUB(dco, kco, co);
-                       VECADDFAC(co, co, dco, fac);
+                               mul_v3_m4v3(key->co, imat, co);
 
-                       VECCOPY(key->co, co);
-                       mul_m4_v3(imat, key->co);
+                               if(puff_volume) {
+                                       /* accumulate the total distance moved to apply to unselected
+                                        * keys that come after */
+                                       ofs[0] += key->co[0] - dco[0];
+                                       ofs[1] += key->co[1] - dco[1];
+                                       ofs[2] += key->co[2] - dco[2];
+                               }
+                               change = 1;
+                       }
+                       else {
+
+                               if(puff_volume) {
+#if 0
+                                       /* this is simple but looks bad, adds annoying kinks */
+                                       add_v3_v3(key->co, ofs);
+#else
+                                       /* translate (not rotate) the rest of the hair if its not selected  */
+                                       if(ofs[0] || ofs[1] || ofs[2]) {
+#if 0                                  /* kindof works but looks worse then whats below */
+
+                                               /* Move the unselected point on a vector based on the
+                                                * hair direction and the offset */
+                                               float c1[3], c2[3];
+                                               VECSUB(dco, lastco, co);
+                                               mul_mat3_m4_v3(imat, dco); /* into particle space */
+
+                                               /* move the point allong a vector perpendicular to the
+                                                * hairs direction, reduces odd kinks, */
+                                               cross_v3_v3v3(c1, ofs, dco);
+                                               cross_v3_v3v3(c2, c1, dco);
+                                               normalize_v3(c2);
+                                               mul_v3_fl(c2, len_v3(ofs));
+                                               add_v3_v3(key->co, c2);
+#else
+                                               /* Move the unselected point on a vector based on the
+                                                * the normal of the closest geometry */
+                                               float oco[3], onor[3];
+                                               VECCOPY(oco, key->co);
+                                               mul_m4_v3(mat, oco);
+                                               mul_v3_m4v3(kco, data->ob->imat, oco); /* use 'kco' as the object space version of worldspace 'co', ob->imat is set before calling */
+
+                                               point_index= BLI_kdtree_find_nearest(edit->emitter_field, kco, NULL, NULL);
+                                               if(point_index != -1) {
+                                                       copy_v3_v3(onor, &edit->emitter_cosnos[point_index*6+3]);
+                                                       mul_mat3_m4_v3(data->ob->obmat, onor); /* normal into worldspace */
+                                                       mul_mat3_m4_v3(imat, onor); /* worldspace into particle space */
+                                                       normalize_v3(onor);
+
+
+                                                       mul_v3_fl(onor, len_v3(ofs));
+                                                       add_v3_v3(key->co, onor);
+                                               }
+#endif
+                                       }
+#endif
+                               }
+                       }
                }
        }
 
-       point->flag |= PEP_EDIT_RECALC;
+       if(change)
+               point->flag |= PEP_EDIT_RECALC;
 }
 
-static void brush_smooth_get(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
+
+static void brush_weight(PEData *data, float UNUSED(mat[][4]), float UNUSED(imat[][4]), int point_index, int key_index, PTCacheEditKey *UNUSED(key))
+{
+       /* roots have full weight allways */
+       if(key_index) {
+               PTCacheEdit *edit = data->edit;
+               ParticleSystem *psys = edit->psys;
+
+               ParticleData *pa= psys->particles + point_index;
+               pa->hair[key_index].weight = data->weightfac;
+
+               (data->edit->points + point_index)->flag |= PEP_EDIT_RECALC;
+       }
+}
+
+static void brush_smooth_get(PEData *data, float mat[][4], float UNUSED(imat[][4]), int UNUSED(point_index), int key_index, PTCacheEditKey *key)
 {      
        if(key_index) {
                float dvec[3];
@@ -2974,7 +3107,7 @@ static void brush_smooth_get(PEData *data, float mat[][4], float imat[][4], int
        }
 }
 
-static void brush_smooth_do(PEData *data, float mat[][4], float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
+static void brush_smooth_do(PEData *data, float UNUSED(mat[][4]), float imat[][4], int point_index, int key_index, PTCacheEditKey *key)
 {
        float vec[3], dvec[3];
        
@@ -3001,13 +3134,13 @@ static int brush_add(PEData *data, short number)
        ParticleSystem *psys= edit->psys;
        ParticleData *add_pars= MEM_callocN(number*sizeof(ParticleData),"ParticleData add");
        ParticleSystemModifierData *psmd= psys_get_modifier(ob,psys);
-       ParticleSimulationData sim = {scene, ob, psys, psmd};
+       ParticleSimulationData sim= {0};
        ParticleEditSettings *pset= PE_settings(scene);
        int i, k, n= 0, totpart= psys->totpart;
        float mco[2];
        short dmx= 0, dmy= 0;
        float co1[3], co2[3], min_d, imat[4][4];
-       float framestep, timestep= psys_get_timestep(&sim);
+       float framestep, timestep;
        short size= pset->brush[PE_BRUSH_ADD].size;
        short size2= size*size;
        DerivedMesh *dm=0;
@@ -3017,7 +3150,14 @@ static int brush_add(PEData *data, short number)
                return 0;
 
        BLI_srandom(psys->seed+data->mval[0]+data->mval[1]);
-       
+
+       sim.scene= scene;
+       sim.ob= ob;
+       sim.psys= psys;
+       sim.psmd= psmd;
+
+       timestep= psys_get_timestep(&sim);
+
        /* painting onto the deformed mesh, could be an option? */
        if(psmd->dm->deformedOnly)
                dm= psmd->dm;
@@ -3108,24 +3248,23 @@ static int brush_add(PEData *data, short number)
                        initialize_particle(&sim, pa,i);
                        reset_particle(&sim, pa, 0.0, 1.0);
                        point->flag |= PEP_EDIT_RECALC;
-                       if(pset->flag & PE_X_MIRROR)
+                       if(pe_x_mirror(ob))
                                point->flag |= PEP_TAG; /* signal for duplicate */
                        
                        framestep= pa->lifetime/(float)(pset->totaddkey-1);
 
                        if(tree) {
-                               HairKey *hkey;
-                               ParticleKey key[3];
+                               ParticleData *ppa;
+                               HairKey *thkey;
+                               ParticleKey key3[3];
                                KDTreeNearest ptn[3];
                                int w, maxw;
-                               float maxd, mind, dd, totw=0.0, weight[3];
+                               float maxd, totw=0.0, weight[3];
 
                                psys_particle_on_dm(psmd->dm,psys->part->from,pa->num,pa->num_dmcache,pa->fuv,pa->foffset,co1,0,0,0,0,0);
                                maxw= BLI_kdtree_find_n_nearest(tree,3,co1,NULL,ptn);
 
                                maxd= ptn[maxw-1].dist;
-                               mind= ptn[0].dist;
-                               dd= maxd - mind;
                                
                                for(w=0; w<maxw; w++) {
                                        weight[w]= (float)pow(2.0, (double)(-6.0f * ptn[w].dist / maxd));
@@ -3138,40 +3277,46 @@ static int brush_add(PEData *data, short number)
                                for(w=0; w<maxw; w++)
                                        weight[w] /= totw;
 
+                               ppa= psys->particles+ptn[0].index;
+
                                for(k=0; k<pset->totaddkey; k++) {
-                                       hkey= (HairKey*)pa->hair + k;
-                                       hkey->time= pa->time + k * framestep;
+                                       thkey= (HairKey*)pa->hair + k;
+                                       thkey->time= pa->time + k * framestep;
 
-                                       key[0].time= hkey->time/ 100.0f;
-                                       psys_get_particle_on_path(&sim, ptn[0].index, key, 0);
-                                       mul_v3_fl(key[0].co, weight[0]);
+                                       key3[0].time= thkey->time/ 100.0f;
+                                       psys_get_particle_on_path(&sim, ptn[0].index, key3, 0);
+                                       mul_v3_fl(key3[0].co, weight[0]);
+                                       
+                                       /* TODO: interpolatint the weight would be nicer */
+                                       thkey->weight= (ppa->hair+MIN2(k, ppa->totkey-1))->weight;
                                        
                                        if(maxw>1) {
-                                               key[1].time= key[0].time;
-                                               psys_get_particle_on_path(&sim, ptn[1].index, key + 1, 0);
-                                               mul_v3_fl(key[1].co, weight[1]);
-                                               VECADD(key[0].co, key[0].co, key[1].co);
+                                               key3[1].time= key3[0].time;
+                                               psys_get_particle_on_path(&sim, ptn[1].index, &key3[1], 0);
+                                               mul_v3_fl(key3[1].co, weight[1]);
+                                               VECADD(key3[0].co, key3[0].co, key3[1].co);
 
                                                if(maxw>2) {                                            
-                                                       key[2].time= key[0].time;
-                                                       psys_get_particle_on_path(&sim, ptn[2].index, key + 2, 0);
-                                                       mul_v3_fl(key[2].co, weight[2]);
-                                                       VECADD(key[0].co, key[0].co, key[2].co);
+                                                       key3[2].time= key3[0].time;
+                                                       psys_get_particle_on_path(&sim, ptn[2].index, &key3[2], 0);
+                                                       mul_v3_fl(key3[2].co, weight[2]);
+                                                       VECADD(key3[0].co, key3[0].co, key3[2].co);
                                                }
                                        }
 
                                        if(k==0)
-                                               VECSUB(co1, pa->state.co, key[0].co);
+                                               VECSUB(co1, pa->state.co, key3[0].co);
 
-                                       VECADD(hkey->co, key[0].co, co1);
+                                       VECADD(thkey->co, key3[0].co, co1);
 
-                                       hkey->time= key[0].time;
+                                       thkey->time= key3[0].time;
                                }
                        }
                        else {
                                for(k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
                                        VECADDFAC(hkey->co, pa->state.co, pa->state.vel, k * framestep * timestep);
                                        hkey->time += k * framestep;
+                                       hkey->weight = 1.f - (float)k/(float)(pset->totaddkey-1);
                                }
                        }
                        for(k=0, hkey=pa->hair; k<pset->totaddkey; k++, hkey++) {
@@ -3202,6 +3347,9 @@ typedef struct BrushEdit {
 
        int first;
        int lastmouse[2];
+
+       /* optional cached view settings to avoid setting on every mousemove */
+       PEData data;
 } BrushEdit;
 
 static int brush_edit_init(bContext *C, wmOperator *op)
@@ -3226,6 +3374,9 @@ static int brush_edit_init(bContext *C, wmOperator *op)
        bedit->ob= ob;
        bedit->edit= edit;
 
+       /* cache view depths and settings for re-use */
+       PE_set_view3d_data(C, &bedit->data);
+
        return 1;
 }
 
@@ -3250,7 +3401,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        RNA_float_get_array(itemptr, "mouse", mousef);
        mouse[0] = mousef[0];
        mouse[1] = mousef[1];
-       flip= RNA_boolean_get(itemptr, "flip");
+       flip= RNA_boolean_get(itemptr, "pen_flip");
 
        if(bedit->first) {
                bedit->lastmouse[0]= mouse[0];
@@ -3273,6 +3424,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        if(((pset->brushtype == PE_BRUSH_ADD) ?
                (sqrt(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0))
                || bedit->first) {
+               PEData data= bedit->data;
 
                view3d_operator_needs_opengl(C);
                selected= (short)count_selected_keys(scene, edit);
@@ -3280,13 +3432,10 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                switch(pset->brushtype) {
                        case PE_BRUSH_COMB:
                        {
-                               PEData data;
-
-                               PE_set_view3d_data(C, &data);
                                data.mval= mval;
                                data.rad= (float)brush->size;
 
-                               data.combfac= (float)(brush->strength - 50) / 50.0f;
+                               data.combfac= (brush->strength - 0.5f) * 2.0f;
                                if(data.combfac < 0.0f)
                                        data.combfac= 1.0f - 9.0f * data.combfac;
                                else
@@ -3302,20 +3451,17 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        }
                        case PE_BRUSH_CUT:
                        {
-                               PEData data;
-                               
                                if(edit->psys && edit->pathcache) {
-                                       PE_set_view3d_data(C, &data);
                                        data.mval= mval;
                                        data.rad= (float)brush->size;
-                                       data.cutfac= (float)(brush->strength / 100.0f);
+                                       data.cutfac= brush->strength;
 
                                        if(selected)
                                                foreach_selected_point(&data, brush_cut);
                                        else
                                                foreach_point(&data, brush_cut);
 
-                                       removed= remove_tagged_particles(scene, ob, edit->psys);
+                                       removed= remove_tagged_particles(ob, edit->psys, pe_x_mirror(ob));
                                        if(pset->flag & PE_KEEP_LENGTHS)
                                                recalc_lengths(edit);
                                }
@@ -3326,13 +3472,10 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        }
                        case PE_BRUSH_LENGTH:
                        {
-                               PEData data;
-                               
-                               PE_set_view3d_data(C, &data);
                                data.mval= mval;
                                
                                data.rad= (float)brush->size;
-                               data.growfac= (float)brush->strength / 5000.0f;
+                               data.growfac= brush->strength / 50.0f;
 
                                if(brush->invert ^ flip)
                                        data.growfac= 1.0f - data.growfac;
@@ -3347,15 +3490,13 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        }
                        case PE_BRUSH_PUFF:
                        {
-                               PEData data;
-
                                if(edit->psys) {
-                                       PE_set_view3d_data(C, &data);
                                        data.dm= psmd->dm;
                                        data.mval= mval;
                                        data.rad= (float)brush->size;
+                                       data.select= selected;
 
-                                       data.pufffac= (float)(brush->strength - 50) / 50.0f;
+                                       data.pufffac= (brush->strength - 0.5f) * 2.0f;
                                        if(data.pufffac < 0.0f)
                                                data.pufffac= 1.0f - 9.0f * data.pufffac;
                                        else
@@ -3370,13 +3511,10 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        }
                        case PE_BRUSH_ADD:
                        {
-                               PEData data;
-
                                if(edit->psys && edit->psys->part->from==PART_FROM_FACE) {
-                                       PE_set_view3d_data(C, &data);
                                        data.mval= mval;
 
-                                       added= brush_add(&data, brush->strength);
+                                       added= brush_add(&data, brush->count);
 
                                        if(pset->flag & PE_KEEP_LENGTHS)
                                                recalc_lengths(edit);
@@ -3387,16 +3525,13 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        }
                        case PE_BRUSH_SMOOTH:
                        {
-                               PEData data;
-
-                               PE_set_view3d_data(C, &data);
                                data.mval= mval;
                                data.rad= (float)brush->size;
 
                                data.vec[0]= data.vec[1]= data.vec[2]= 0.0f;
                                data.tot= 0;
 
-                               data.smoothfac= (float)(brush->strength / 100.0f);
+                               data.smoothfac= brush->strength;
 
                                invert_m4_m4(ob->imat, ob->obmat);
 
@@ -3407,6 +3542,20 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                                        foreach_mouse_hit_key(&data, brush_smooth_do, selected);
                                }
 
+                               break;
+                       }
+                       case PE_BRUSH_WEIGHT:
+                       {
+                               if(edit->psys) {
+                                       data.dm= psmd->dm;
+                                       data.mval= mval;
+                                       data.rad= (float)brush->size;
+
+                                       data.weightfac = brush->strength; /* note that this will never be zero */
+
+                                       foreach_mouse_hit_key(&data, brush_weight, selected);
+                               }
+
                                break;
                        }
                }
@@ -3414,17 +3563,17 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
                        recalc_lengths(edit);
 
                if(ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
-                       if(pset->brushtype == PE_BRUSH_ADD && (pset->flag & PE_X_MIRROR))
+                       if(pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob))
                                PE_mirror_x(scene, ob, 1);
 
                        update_world_cos(ob,edit);
                        psys_free_path_cache(NULL, edit);
-                       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+                       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
                }
                else
                        PE_update_object(scene, ob, 1);
 
-               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_SELECT, ob);
+               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
                
                bedit->lastmouse[0]= mouse[0];
                bedit->lastmouse[1]= mouse[1];
@@ -3434,7 +3583,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
        pset->flag |= lock_root;
 }
 
-static void brush_edit_exit(bContext *C, wmOperator *op)
+static void brush_edit_exit(wmOperator *op)
 {
        BrushEdit *bedit= op->customdata;
 
@@ -3451,7 +3600,7 @@ static int brush_edit_exec(bContext *C, wmOperator *op)
        }
        RNA_END;
 
-       brush_edit_exit(C, op);
+       brush_edit_exit(op);
 
        return OPERATOR_FINISHED;
 }
@@ -3469,7 +3618,7 @@ static void brush_edit_apply_event(bContext *C, wmOperator *op, wmEvent *event)
        RNA_collection_add(op->ptr, "stroke", &itemptr);
 
        RNA_float_set_array(&itemptr, "mouse", mouse);
-       RNA_boolean_set(&itemptr, "flip", event->shift != 0); // XXX hardcoded
+       RNA_boolean_set(&itemptr, "pen_flip", event->shift != 0); // XXX hardcoded
 
        /* apply */
        brush_edit_apply(C, op, &itemptr);
@@ -3493,7 +3642,7 @@ static int brush_edit_modal(bContext *C, wmOperator *op, wmEvent *event)
                case LEFTMOUSE:
                case MIDDLEMOUSE:
                case RIGHTMOUSE: // XXX hardcoded
-                       brush_edit_exit(C, op);
+                       brush_edit_exit(op);
                        return OPERATOR_FINISHED;
                case MOUSEMOVE:
                        brush_edit_apply_event(C, op, event);
@@ -3503,9 +3652,9 @@ static int brush_edit_modal(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-static int brush_edit_cancel(bContext *C, wmOperator *op)
+static int brush_edit_cancel(bContext *UNUSED(C), wmOperator *op)
 {
-       brush_edit_exit(C, op);
+       brush_edit_exit(op);
 
        return OPERATOR_CANCELLED;
 }
@@ -3521,7 +3670,7 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot)
        ot->invoke= brush_edit_invoke;
        ot->modal= brush_edit_modal;
        ot->cancel= brush_edit_cancel;
-       ot->poll= PE_poll_3dview;
+       ot->poll= PE_poll_view3d;
 
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
@@ -3652,7 +3801,7 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                        for(i=0; i<BPHYS_TOT_DATA; i++)
                                pm->data[i] = MEM_dupallocN(pm->data[i]);
 
-                       BKE_ptcache_mem_init_pointers(pm);
+                       BKE_ptcache_mem_pointers_init(pm);
 
                        LOOP_POINTS {
                                LOOP_KEYS {
@@ -3663,13 +3812,13 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
                                                key->time = &key->ftime;
                                        }
                                }
-                               BKE_ptcache_mem_incr_pointers(pm);
+                               BKE_ptcache_mem_pointers_incr(pm);
                        }
                }
        }
 }
 
-void PE_undo_push(Scene *scene, char *str)
+void PE_undo_push(Scene *scene, const char *str)
 {
        PTCacheEdit *edit= PE_get_current(scene, OBACT);
        PTCacheUndo *undo;
@@ -3740,7 +3889,17 @@ void PE_undo_step(Scene *scene, int step)
                }
        }
 
-       DAG_id_flush_update(&OBACT->id, OB_RECALC_DATA);
+       DAG_id_tag_update(&OBACT->id, OB_RECALC_DATA);
+}
+
+int PE_undo_valid(Scene *scene)
+{
+       PTCacheEdit *edit= PE_get_current(scene, OBACT);
+       
+       if(edit) {
+               return (edit->undo.last != edit->undo.first);
+       }
+       return 0;
 }
 
 static void PTCacheUndo_number(Scene *scene, PTCacheEdit *edit, int nr)
@@ -3814,15 +3973,15 @@ int PE_minmax(Scene *scene, float *min, float *max)
 {
        Object *ob= OBACT;
        PTCacheEdit *edit= PE_get_current(scene, ob);
-       ParticleSystem *psys = edit->psys;
+       ParticleSystem *psys;
        ParticleSystemModifierData *psmd = NULL;
        POINT_P; KEY_K;
        float co[3], mat[4][4];
        int ok= 0;
 
        if(!edit) return ok;
-       
-       if(psys)
+
+       if((psys = edit->psys))
                psmd= psys_get_modifier(ob, psys);
        else
                unit_m4(mat);
@@ -3866,6 +4025,9 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
        if(cache && cache->flag & PTCACHE_DISK_CACHE)
                return;
 
+       if(psys == NULL && cache->mem_cache.first == NULL)
+               return;
+
        if(!edit) {
                totpoint = psys ? psys->totpart : ((PTCacheMem*)cache->mem_cache.first)->totpoint;
 
@@ -3893,12 +4055,16 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
                                        key->co= hkey->co;
                                        key->time= &hkey->time;
                                        key->flag= hkey->editflag;
-                                       if(!(psys->flag & PSYS_GLOBAL_HAIR))
+                                       if(!(psys->flag & PSYS_GLOBAL_HAIR)) {
                                                key->flag |= PEK_USE_WCO;
+                                               hkey->editflag |= PEK_USE_WCO;
+                                       }
+
                                        hkey++;
                                }
                                pa++;
                        }
+                       update_world_cos(ob, edit);
                }
                else {
                        PTCacheMem *pm;
@@ -3912,17 +4078,9 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
                                totframe++;
 
                        for(pm=cache->mem_cache.first; pm; pm=pm->next) {
-                               BKE_ptcache_mem_init_pointers(pm);
-
                                LOOP_POINTS {
-                                       if(psys) {
-                                               pa = psys->particles + p;
-                                               if((pm->next && pm->next->frame < pa->time)
-                                                       || (pm->prev && pm->prev->frame >= pa->dietime)) {
-                                                               BKE_ptcache_mem_incr_pointers(pm);
-                                                               continue;
-                                                       }
-                                       }
+                                       if(BKE_ptcache_mem_pointers_seek(p, pm) == 0)
+                                               continue;
 
                                        if(!point->totkey) {
                                                key = point->keys = MEM_callocN(totframe*sizeof(PTCacheEditKey),"ParticleEditKeys");
@@ -3936,7 +4094,7 @@ static void PE_create_particle_edit(Scene *scene, Object *ob, PointCache *cache,
                                        key->rot = pm->cur[BPHYS_DATA_ROTATION];
                                        key->ftime = (float)pm->frame;
                                        key->time = &key->ftime;
-                                       BKE_ptcache_mem_incr_pointers(pm);
+                                       BKE_ptcache_mem_pointers_incr(pm);
 
                                        point->totkey++;
                                }
@@ -3968,14 +4126,21 @@ static int particle_edit_toggle_poll(bContext *C)
        return (ob->particlesystem.first || modifiers_findByType(ob, eModifierType_Cloth) || modifiers_findByType(ob, eModifierType_Softbody));
 }
 
-static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
+static int particle_edit_toggle_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Scene *scene= CTX_data_scene(C);
        Object *ob= CTX_data_active_object(C);
 
        if(!(ob->mode & OB_MODE_PARTICLE_EDIT)) {
+               PTCacheEdit *edit;
                ob->mode |= OB_MODE_PARTICLE_EDIT;
-               PE_create_current(scene, ob);
+               edit= PE_create_current(scene, ob);
+       
+               /* mesh may have changed since last entering editmode.
+                * note, this may have run before if the edit data was just created, so could avoid this and speed up a little */
+               if(edit && edit->psys)
+                       recalc_emitter_field(ob, edit->psys);
+               
                toggle_particle_cursor(C, 1);
                WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_PARTICLE, NULL);
        }
@@ -3985,7 +4150,7 @@ static int particle_edit_toggle_exec(bContext *C, wmOperator *op)
                WM_event_add_notifier(C, NC_SCENE|ND_MODE|NS_MODE_OBJECT, NULL);
        }
 
-       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 
        return OPERATOR_FINISHED;
 }
@@ -4007,7 +4172,7 @@ void PARTICLE_OT_particle_edit_toggle(wmOperatorType *ot)
 
 /************************ set editable operator ************************/
 
-static int clear_edited_exec(bContext *C, wmOperator *op)
+static int clear_edited_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Object *ob= CTX_data_active_object(C);
        ParticleSystem *psys = psys_get_current(ob);
@@ -4024,10 +4189,17 @@ static int clear_edited_exec(bContext *C, wmOperator *op)
                        psys->flag &= ~PSYS_EDITED;
 
                        psys_reset(psys, PSYS_RESET_DEPSGRAPH);
-                       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE_DATA, ob);
-                       DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+                       DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
                }
        }
+       else { /* some operation might have protected hair from editing so let's clear the flag */
+               psys->recalc |= PSYS_RECALC_RESET;
+               psys->flag &= ~PSYS_GLOBAL_HAIR;
+               psys->flag &= ~PSYS_EDITED;
+               WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
+               DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
+       }
 
        return OPERATOR_FINISHED;
 }
@@ -4046,39 +4218,3 @@ void PARTICLE_OT_edited_clear(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
-/*********************** specials menu **************************/
-
-static int specials_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
-{
-       Scene *scene= CTX_data_scene(C);
-       ParticleEditSettings *pset=PE_settings(scene);
-       uiPopupMenu *pup;
-       uiLayout *layout;
-
-       pup= uiPupMenuBegin(C, "Specials", 0);
-       layout= uiPupMenuLayout(pup);
-
-       uiItemO(layout, NULL, 0, "PARTICLE_OT_rekey");
-       if(pset->selectmode & SCE_SELECT_POINT) {
-               uiItemO(layout, NULL, 0, "PARTICLE_OT_subdivide");
-               uiItemO(layout, NULL, 0, "PARTICLE_OT_select_first");
-               uiItemO(layout, NULL, 0, "PARTICLE_OT_select_last");
-       }
-       uiItemO(layout, NULL, 0, "PARTICLE_OT_remove_doubles");
-
-       uiPupMenuEnd(C, pup);
-
-       return OPERATOR_CANCELLED;
-}
-
-void PARTICLE_OT_specials_menu(wmOperatorType *ot)
-{
-       /* identifiers */
-       ot->name= "Specials Menu";
-       ot->idname= "PARTICLE_OT_specials_menu";
-       
-       /* api callbacks */
-       ot->invoke= specials_menu_invoke;
-       ot->poll= PE_hair_poll;
-}
-