X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=synfig-core%2Ftrunk%2Fsrc%2Fmodules%2Fmod_particle%2Fplant.cpp;h=3abe14674f31ae68ae8e32a5553ef6de90b456c2;hb=756c0d29ac1742f231e6615f9a577e574e35a4af;hp=1af642c49ac7df4eab9a987ffa27c177b487200f;hpb=534d634ec3ace72b406bde4f847ac1eb506fe484;p=synfig.git diff --git a/synfig-core/trunk/src/modules/mod_particle/plant.cpp b/synfig-core/trunk/src/modules/mod_particle/plant.cpp index 1af642c..3abe146 100644 --- a/synfig-core/trunk/src/modules/mod_particle/plant.cpp +++ b/synfig-core/trunk/src/modules/mod_particle/plant.cpp @@ -1,11 +1,12 @@ /* === S Y N F I G ========================================================= */ /*! \file plant.cpp -** \brief Template +** \brief Implementation of the "Plant" layer ** ** $Id$ ** ** \legal ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley +** Copyright (c) 2007, 2008 Chris Moore ** ** This package is free software; you can redistribute it and/or ** modify it under the terms of the GNU General Public License as @@ -64,9 +65,9 @@ using namespace etl; SYNFIG_LAYER_INIT(Plant); SYNFIG_LAYER_SET_NAME(Plant,"plant"); -SYNFIG_LAYER_SET_LOCAL_NAME(Plant,_("Plant")); -SYNFIG_LAYER_SET_CATEGORY(Plant,_("Other")); -SYNFIG_LAYER_SET_VERSION(Plant,"0.1"); +SYNFIG_LAYER_SET_LOCAL_NAME(Plant,N_("Plant")); +SYNFIG_LAYER_SET_CATEGORY(Plant,N_("Other")); +SYNFIG_LAYER_SET_VERSION(Plant,"0.2"); SYNFIG_LAYER_SET_CVS_ID(Plant,"$Id$"); /* === P R O C E D U R E S ================================================= */ @@ -75,12 +76,15 @@ SYNFIG_LAYER_SET_CVS_ID(Plant,"$Id$"); Plant::Plant(): + offset(0,0), split_angle(Angle::deg(10)), gravity(0,-0.1), velocity(0.3), - perp_velocity(0.3), + perp_velocity(0.0), step(0.01), - sprouts(10) + sprouts(10), + version(version__), + use_width(true) { bounding_rect=Rect::zero(); random_factor=0.2; @@ -106,6 +110,7 @@ Plant::Plant(): needs_sync_=true; sync(); size_as_alpha=false; + reverse=true; } void @@ -178,6 +183,7 @@ Plant::sync()const { Mutex::Lock lock(mutex); if (!needs_sync_) return; + time_t start_time; time(&start_time); particle_list.clear(); bounding_rect=Rect::zero(); @@ -205,6 +211,9 @@ Plant::sync()const // loop through the bline; seg counts the blines as we do so; stop before iter is the last bline in the list for(;next!=bline.end();iter=next++,seg++) { + float iterw=iter->get_width(); // the width value of the iter vertex + float nextw=next->get_width(); // the width value of the next vertex + float width; // the width at an intermediate position curve.p1()=iter->get_vertex(); curve.t1()=iter->get_tangent2(); curve.p2()=next->get_vertex(); @@ -215,6 +224,7 @@ Plant::sync()const Real f; int i=0, branch_count = 0, steps = round_to_int(1.0/step); + if (steps < 1) steps = 1; for(f=0.0;f<1.0;f+=step,i++) { Point point(curve(f)); @@ -225,7 +235,7 @@ Plant::sync()const bounding_rect.expand(point); - Real stunt_growth(random_factor * random(Random::SMOOTH_COSINE,i,f+seg,0.0f,0.0f)/2.0+0.5); + Real stunt_growth(random_factor * (random(Random::SMOOTH_COSINE,i,f+seg,0.0f,0.0f)/2.0+0.5)); stunt_growth*=stunt_growth; if((((i+1)*sprouts + steps/2) / steps) > branch_count) { @@ -234,6 +244,14 @@ Plant::sync()const branch_velocity[0] += random_factor * random(Random::SMOOTH_COSINE, 1, f*splits, 0.0f, 0.0f); branch_velocity[1] += random_factor * random(Random::SMOOTH_COSINE, 2, f*splits, 0.0f, 0.0f); + if (use_width) + { + width = iterw+(nextw-iterw)*f; // calculate the width based on the current position + + branch_velocity[0] *= width; // scale the velocity accordingly to the current width + branch_velocity[1] *= width; + } + branch_count++; branch(i, 0, 0, // time stunt_growth, // stunt growth @@ -242,6 +260,10 @@ Plant::sync()const } } + time_t end_time; time(&end_time); + if (end_time-start_time > 4) + synfig::info("Plant::sync() constructed %d particles in %d seconds\n", + particle_list.size(), int(end_time-start_time)); needs_sync_=false; } @@ -262,18 +284,32 @@ Plant::set_param(const String & param, const ValueBase &value) needs_sync_=true; return true; } + IMPORT(offset); IMPORT_PLUS(split_angle,needs_sync_=true); IMPORT_PLUS(gravity,needs_sync_=true); IMPORT_PLUS(gradient,needs_sync_=true); IMPORT_PLUS(velocity,needs_sync_=true); IMPORT_PLUS(perp_velocity,needs_sync_=true); - IMPORT_PLUS(step,needs_sync_=true); - IMPORT_PLUS(splits,needs_sync_=true); + IMPORT_PLUS(step,{ + needs_sync_ = true; + if (step <= 0) + step=0.01; // user is probably clueless - give a good default + else if (step < 0.00001) + step=0.00001; // 100K should be enough for anyone + else if (step > 1) + step=1; + }); + IMPORT_PLUS(splits,{ + needs_sync_=true; + if (splits < 1) + splits = 1; + }); IMPORT_PLUS(sprouts,needs_sync_=true); IMPORT_PLUS(random_factor,needs_sync_=true); IMPORT_PLUS(drag,needs_sync_=true); IMPORT(size); IMPORT(size_as_alpha); + IMPORT(reverse); return Layer_Composite::set_param(param,value); } @@ -308,6 +344,7 @@ Plant::get_param(const String& param)const if(param=="seed") return random.get_seed(); EXPORT(bline); + EXPORT(offset); EXPORT(split_angle); EXPORT(gravity); EXPORT(velocity); @@ -321,9 +358,12 @@ Plant::get_param(const String& param)const EXPORT(size); EXPORT(size_as_alpha); + EXPORT(reverse); EXPORT_NAME(); - EXPORT_VERSION(); + + if(param=="Version" || param=="version" || param=="version__") + return version; return Layer_Composite::get_param(param); } @@ -336,10 +376,14 @@ Plant::get_param_vocab()const ret.push_back(ParamDesc("bline") .set_local_name(_("Vertices")) .set_description(_("A list of BLine Points")) - //.set_origin("offset") + .set_origin("offset") //.set_scalar("width") ); + ret.push_back(ParamDesc("offset") + .set_local_name(_("Offset")) + ); + ret.push_back(ParamDesc("gradient") .set_local_name(_("Gradient")) .set_description(_("Gradient to be used for coloring the plant")) @@ -377,6 +421,11 @@ Plant::get_param_vocab()const .set_description(_("If enabled, the alpha channel from the gradient is multiplied by the stem size, and an alpha of 1.0 is used when rendering")) ); + ret.push_back(ParamDesc("reverse") + .set_local_name(_("Reverse")) + .set_description(_("If enabled, render the plant in the opposite direction")) + ); + ret.push_back(ParamDesc("step") .set_local_name(_("Step")) .set_description(_("Measure of the distance between points when rendering")) @@ -411,6 +460,17 @@ Plant::get_param_vocab()const } bool +Plant::set_version(const String &ver) +{ + version = ver; + + if (version == "0.1") + use_width = false; + + return true; +} + +bool Plant::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const { bool ret(context.accelerated_render(surface,quality,renddesc,cb)); @@ -421,8 +481,8 @@ Plant::accelerated_render(Context context,Surface *surface,int quality, const Re dest_surface.set_wh(surface->get_w(),surface->get_h()); dest_surface.clear(); - const Point tl(renddesc.get_tl()); - const Point br(renddesc.get_br()); + const Point tl(renddesc.get_tl()-offset); + const Point br(renddesc.get_br()-offset); const int w(renddesc.get_w()); const int h(renddesc.get_h()); @@ -434,104 +494,216 @@ Plant::accelerated_render(Context context,Surface *surface,int quality, const Re const Real pw = (br[0] - tl[0]) / w; const Real ph = (br[1] - tl[1]) / h; + if (isinf(pw) || isinf(ph)) + return true; + if(needs_sync_==true) sync(); - std::vector::reverse_iterator iter; - const float size_factor(1); - float radius(size_factor*size*sqrt(1.0f/(abs(pw)*abs(ph)))), temp_radius; - - if(radius>1.0f) + if (particle_list.begin() != particle_list.end()) { - radius*=1.0; // what does this do? + std::vector::iterator iter; + Particle *particle; + + float radius(size*sqrt(1.0f/(abs(pw)*abs(ph)))); + int x1,y1,x2,y2; - for(iter=particle_list.rbegin();iter!=particle_list.rend();++iter) + + if (reverse) iter = particle_list.end(); + else iter = particle_list.begin(); + + while (true) { - temp_radius = radius; - float radius(temp_radius); - Color color(iter->color); + if (reverse) particle = &(*(iter-1)); + else particle = &(*iter); + + float scaled_radius(radius); + Color color(particle->color); if(size_as_alpha) { - radius*=color.get_a(); + scaled_radius*=color.get_a(); color.set_a(1); } - // calculate the box that this particle will be drawn as - x1=ceil_to_int((iter->point[0]-tl[0])/pw-(radius*0.5)); - y1=ceil_to_int((iter->point[1]-tl[1])/ph-(radius*0.5)); - x2=x1+round_to_int(radius); - y2=y1+round_to_int(radius); - - // if the box is entirely off the canvas, go to the next particle - if(x1>=surface_width || y1>=surface_height || x2<0 || y2<0) continue; - - // adjust the box so it's entirely on the canvas - if(x2>=surface_width) x2=surface_width; - if(y2>=surface_height) y2=surface_height; - if(x1<0) x1=0; - if(y1<0) y1=0; + // previously, radius was multiplied by sqrt(step)*12 only if + // the radius came out at less than 1 (pixel): + // if (radius<=1.0f) radius*=sqrt(step)*12.0f; + // seems a little arbitrary - does it help? - int w(min(round_to_int(radius),x2-x1)); - int h(min(round_to_int(radius),y2-y1)); - - if(w<=0 || h<=0) - continue; - - Surface::alpha_pen surface_pen(dest_surface.get_pen(x1,y1),1.0f); - dest_surface.fill(color,surface_pen,w,h); - } - } - else - { - //radius/=0.01; - radius*=sqrt(step)*12.0f; - for(iter=particle_list.rbegin();iter!=particle_list.rend();++iter) - { - temp_radius = radius; - float radius(temp_radius); - Color color(iter->color); - if(size_as_alpha) + // calculate the box that this particle will be drawn as + float x1f=(particle->point[0]-tl[0])/pw-(scaled_radius*0.5); + float x2f=(particle->point[0]-tl[0])/pw+(scaled_radius*0.5); + float y1f=(particle->point[1]-tl[1])/ph-(scaled_radius*0.5); + float y2f=(particle->point[1]-tl[1])/ph+(scaled_radius*0.5); + x1=ceil_to_int(x1f); + x2=ceil_to_int(x2f)-1; + y1=ceil_to_int(y1f); + y2=ceil_to_int(y2f)-1; + + // if the box isn't entirely off the canvas, draw it + if(x1<=surface_width && y1<=surface_height && x2>=0 && y2>=0) { - radius*=color.get_a(); - color.set_a(1); + float x1e=x1-x1f, x2e=x2f-x2, y1e=y1-y1f, y2e=y2f-y2; + // printf("x1e %.4f x2e %.4f y1e %.4f y2e %.4f\n", x1e, x2e, y1e, y2e); + + // adjust the box so it's entirely on the canvas + if(x1<=0) { x1=0; x1e=0; } + if(y1<=0) { y1=0; y1e=0; } + if(x2>=surface_width) { x2=surface_width; x2e=0; } + if(y2>=surface_height) { y2=surface_height; y2e=0; } + + int w(x2-x1), h(y2-y1); + + Surface::alpha_pen surface_pen(dest_surface.get_pen(x1,y1),1.0f); + if(w>0 && h>0) + dest_surface.fill(color,surface_pen,w,h); + + /* the rectangle doesn't cross any vertical pixel boundaries so we don't + * need to draw any top or bottom edges + */ + if(x2point[0]-tl[0])/pw-1.499999f); - if (x < 0) - if (x == -1) left = true; else continue; - else if (x > surface_width-2) - if (x == surface_width-1) right = true; else continue; - - int y=ceil_to_int((iter->point[1]-tl[1])/ph-1.499999f); - if (y < 0) - if (y == -1) top = true; else continue; - else if (y > surface_height-2) - if (y == surface_height-1) bottom = true; else continue; - - // calculate how much of the point is at (x) and how much at (x+1) - float x1=((iter->point[0]-tl[0])/pw-0.5f-x)*radius, x0=radius-x1; - - // calculate how much of the point is at (y) and how much at (y+1) - float y1=((iter->point[1]-tl[1])/ph-0.5f-y)*radius, y0=radius-y1; - - Surface::alpha_pen surface_pen(dest_surface.get_pen(x,y),1.0f); - - // | x0 | x1 - // ---+-----+----- - // y0 | 1st | 2nd - // ---+-----+----- - // y1 | 4th | 3rd - - if (!left && !top) { surface_pen.set_alpha(x0*y0); surface_pen.put_value(color); } - surface_pen.inc_x(); - if (!right && !top) { surface_pen.set_alpha(x1*y0); surface_pen.put_value(color); } - surface_pen.inc_y(); - if (!right && !bottom) { surface_pen.set_alpha(x1*y1); surface_pen.put_value(color); } - surface_pen.dec_x(); - if (!left && !bottom) { surface_pen.set_alpha(x0*y1); surface_pen.put_value(color); } + if (reverse) + { + if (--iter == particle_list.begin()) + break; + } + else + { + if (++iter == particle_list.end()) + break; + } } }