1 /* === S I N F G =========================================================== */
2 /*! \file sphere_distort.cpp
3 ** \brief Sphere Distort File
5 ** $Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
14 ** You may not copy, print, publish, or in any
15 ** other way distribute this software without
16 ** a prior written agreement with
17 ** the copyright holder.
20 /* ========================================================================= */
22 /* === H E A D E R S ======================================================= */
31 #include "sphere_distort.h"
32 #include <sinfg/string.h>
33 #include <sinfg/time.h>
34 #include <sinfg/context.h>
35 #include <sinfg/paramdesc.h>
36 #include <sinfg/renddesc.h>
37 #include <sinfg/surface.h>
38 #include <sinfg/value.h>
39 #include <sinfg/valuenode.h>
40 #include <sinfg/transform.h>
42 #include <sinfg/curve_helper.h>
46 /* === U S I N G =========================================================== */
50 using namespace sinfg;
52 /* === M A C R O S ========================================================= */
55 const double PI = 3.14159265;
61 TYPE_DISTH = 1, //axe the horizontal axis
62 TYPE_DISTV = 2, //axe the vertical axis
66 /* === G L O B A L S ======================================================= */
68 SINFG_LAYER_INIT(Layer_SphereDistort);
69 SINFG_LAYER_SET_NAME(Layer_SphereDistort,"spherize");
70 SINFG_LAYER_SET_LOCAL_NAME(Layer_SphereDistort,_("Spherize"));
71 SINFG_LAYER_SET_CATEGORY(Layer_SphereDistort,_("Distortions"));
72 SINFG_LAYER_SET_VERSION(Layer_SphereDistort,"0.2");
73 SINFG_LAYER_SET_CVS_ID(Layer_SphereDistort,"$Id: sphere_distort.cpp,v 1.2 2005/01/24 05:00:18 darco Exp $");
75 /* === P R O C E D U R E S ================================================= */
77 /* === M E T H O D S ======================================================= */
79 /* === E N T R Y P O I N T ================================================= */
81 Layer_SphereDistort::Layer_SphereDistort()
82 :Layer_Composite(1.0,Color::BLEND_STRAIGHT),
93 Layer_SphereDistort::set_param(const String & param, const ValueBase &value)
95 IMPORT_PLUS(center,sync());
96 IMPORT_PLUS(radius,sync());
98 IMPORT_AS(percent,"amount");
103 if(dynamic_param_list().count("percent"))
105 connect_dynamic_param("amount",dynamic_param_list().find("percent")->second);
106 disconnect_dynamic_param("percent");
107 sinfg::warning("Layer_SphereDistort::::set_param(): Updated valuenode connection to use the new \"amount\" parameter.");
110 sinfg::warning("Layer_SphereDistort::::set_param(): The parameter \"segment_list\" is deprecated. Use \"bline\" instead.");
117 Layer_SphereDistort::get_param(const String ¶m)const
122 EXPORT_AS(percent,"amount");
132 Layer_SphereDistort::sync()
137 Layer_SphereDistort::get_param_vocab()const
141 ret.push_back(ParamDesc("center")
142 .set_local_name(_("Position"))
145 ret.push_back(ParamDesc("radius")
146 .set_local_name(_("Radius"))
147 .set_origin("center")
151 ret.push_back(ParamDesc("amount")
152 .set_local_name(_("Amount"))
153 .set_is_distance(false)
156 ret.push_back(ParamDesc("clip")
157 .set_local_name(_("Clip"))
160 ret.push_back(ParamDesc("type")
161 .set_local_name(_("Distort Type"))
162 .set_description(_("The direction of the distortion"))
164 .add_enum_value(TYPE_NORMAL,"normal",_("Spherize"))
165 .add_enum_value(TYPE_DISTH,"honly",_("Vertical Bar"))
166 .add_enum_value(TYPE_DISTV,"vonly",_("Horizontal Bar"))
173 Spherical Distortion: maps an image onto a ellipsoid of some sort
175 so the image coordinate (i.e. distance away from the center)
176 will determine how things get mapped
178 so with the radius and position the mapping would go as follows
180 r = (pos - center) / radius clamped to [-1,1]
182 if it's outside of that range then it's not distorted
183 but if it's inside of that range then it goes as follows
185 angle = r * pi/2 (-pi/2,pi/2)
187 newr = cos(angle)*radius
189 the inverse of this is (which is actually what we'd be transforming it from
194 inline float spherify(float f)
196 if(f > -1 && f < 1 && f!=0)
197 return sinf(f*(PI/2));
201 inline float unspherify(float f)
203 if(f > -1 && f < 1 && f!=0)
204 return asin(f)/(PI/2);
208 Point sphtrans(const Point &p, const Point ¢er, const float &radius,
209 const Real &percent, int type, bool& clipped)
211 const Vector v = (p - center) / radius;
214 const float t = percent;
218 if(type == TYPE_NORMAL)
220 const float m = v.mag();
223 if(m <= -1 || m >= 1)
233 lerp = (t*unspherify(m) + (1-t)*m);
236 lerp = ((1+t)*m - t*spherify(m));
239 const float d = lerp*radius;
240 newp = center + v*(d/m);
243 else if(type == TYPE_DISTH)
246 if(v[0] <= -1 || v[0] >= 1)
256 lerp = (t*unspherify(v[0]) + (1-t)*v[0]);
259 lerp = ((1+t)*v[0] - t*spherify(v[0]));
262 newp[0] = center[0] + lerp*radius;
265 else if(type == TYPE_DISTV)
268 if(v[1] <= -1 || v[1] >= 1)
279 lerp = (t*unspherify(v[1]) + (1-t)*v[1]);
282 lerp = ((1+t)*v[1] - t*spherify(v[1]));
285 newp[1] = center[1] + lerp*radius;
291 inline Point sphtrans(const Point &p, const Point ¢er, const Real &radius,
292 const Real &percent, int type)
295 return sphtrans(p, center, radius, percent, type, tmp);
299 Layer_SphereDistort::hit_check(sinfg::Context context, const sinfg::Point &pos)const
302 Point point(sphtrans(pos,center,radius,percent,type,clipped));
305 return context.hit_check(point);
309 Layer_SphereDistort::get_color(Context context, const Point &pos)const
312 Point point(sphtrans(pos,center,radius,percent,type,clipped));
314 return Color::alpha();
315 return context.get_color(point);
319 bool Layer_SphereDistort::accelerated_render(Context context,Surface *surface,int quality, const RendDesc &renddesc, ProgressCallback *cb)const
321 /* Things to consider:
322 1) Block expansion for distortion (ouch... quality level??)
323 2) Bounding box clipping
324 3) Super sampling for better visual quality (based on the quality level?)
325 4) Interpolation type for sampling (based on quality level?)
327 //things to defer until after
328 super sampling, non-linear interpolation
331 //bounding box reject
335 sphr.set_point(center[0]-radius,center[1]-radius);
336 sphr.expand(center[0]+radius,center[1]+radius);
338 //get the bounding box of the transform
341 //and the bounding box of the rendering
342 windr.set_point(renddesc.get_tl()[0],renddesc.get_tl()[1]);
343 windr.expand(renddesc.get_br()[0],renddesc.get_br()[1]);
345 //test bounding boxes for collision
346 if( (type == TYPE_NORMAL && !intersect(sphr,windr)) ||
347 (type == TYPE_DISTH && (sphr.minx >= windr.maxx || windr.minx >= sphr.maxx)) ||
348 (type == TYPE_DISTV && (sphr.miny >= windr.maxy || windr.miny >= sphr.maxy)) )
350 //sinfg::warning("Spherize: Bounding box reject");
351 return context.accelerated_render(surface,quality,renddesc,cb);
354 //sinfg::warning("Spherize: Bounding box accept");
357 //Ok, so we overlap some... now expand the window for rendering
358 RendDesc r = renddesc;
360 Real pw = renddesc.get_pw(),ph = renddesc.get_ph();
362 int nl=0,nt=0,nr=0,nb=0, nw=0,nh=0;
363 Point tl = renddesc.get_tl(), br = renddesc.get_br();
366 //must enlarge window by pixel coordinates so go!
368 //need to figure out closest and farthest point and distort THOSE
370 Point origin[4] = {tl,tl,br,br};
371 Vector v[4] = {Vector(0,br[1]-tl[1]),
372 Vector(br[0]-tl[0],0),
373 Vector(0,tl[1]-br[1]),
374 Vector(tl[0]-br[0],0)};
380 //expandr.set_point(tl[0],tl[1]);
381 //expandr.expand(br[0],br[1]);
383 //sinfg::warning("Spherize: Loop through lines and stuff");
384 for(int i=0; i<4; ++i)
386 //sinfg::warning("Spherize: %d", i);
387 Vector p_o = center-origin[i];
389 //project onto left line
390 t = (p_o*v[i])/v[i].mag_squared();
393 if(t < 0) t = 0; if(t > 1) t = 1;
395 close = origin[i] + v[i]*t;
397 //now get transforms and expand the rectangle to accomodate
398 Point p = sphtrans(close,center,radius,percent,type);
399 expandr.expand(p[0],p[1]);
400 p = sphtrans(origin[i],center,radius,percent,type);
401 expandr.expand(p[0],p[1]);
402 p = sphtrans(origin[i]+v[i],center,radius,percent,type);
403 expandr.expand(p[0],p[1]);
406 /*sinfg::warning("Spherize: Bounding box (%f,%f)-(%f,%f)",
407 expandr.minx,expandr.miny,expandr.maxx,expandr.maxy);*/
409 //now that we have the bouding rectangle of ALL the pixels (should be...)
410 //order it so that it's in the same orientation as the tl,br pair
412 //sinfg::warning("Spherize: Organize like tl,br");
413 Point ntl(0,0),nbr(0,0);
418 ntl[0] = expandr.minx;
419 nbr[0] = expandr.maxx;
423 ntl[0] = expandr.maxx;
424 nbr[0] = expandr.minx;
430 ntl[1] = expandr.miny;
431 nbr[1] = expandr.maxy;
435 ntl[1] = expandr.maxy;
436 nbr[1] = expandr.miny;
439 //now expand the window as needed
440 Vector temp = ntl-tl;
443 nl = (int)(temp[0]/pw)-1;
444 nt = (int)(temp[1]/ph)-1;
447 nr = (int)(temp[0]/pw)+1;
448 nb = (int)(temp[1]/ph)+1;
450 nw = renddesc.get_w() + nr - nl;
451 nh = renddesc.get_h() + nb - nt;
453 //sinfg::warning("Spherize: Setting subwindow (%d,%d) (%d,%d) (%d,%d)",nl,nt,nr,nb,nw,nh);
454 r.set_subwindow(nl,nt,nw,nh);
457 nw = r.get_w(), nh = r.get_h();
461 //sinfg::warning("Spherize: render background");
462 if(!context.accelerated_render(&background,quality,r,cb))
464 sinfg::warning("SphereDistort: Layer below failed");
468 //now distort and check to make sure we aren't overshooting our bounds here
469 int w = renddesc.get_w(), h = renddesc.get_h();
470 surface->set_wh(w,h);
472 Point sample = tl, sub = tl, trans(0,0);
475 Real invpw = 1/pw, invph = 1/ph;
476 Surface::pen p = surface->begin();
478 Point rtl = r.get_tl();
480 //sinfg::warning("Spherize: About to transform");
482 for(y = 0; y < h; ++y, sample[1] += ph, p.inc_y())
485 for(x = 0; x < w; ++x, sub[0] += pw, p.inc_x())
488 trans=sphtrans(sub,center,radius,percent,type,clipped);
491 p.put_value(Color::alpha());
495 xs = (trans[0]-rtl[0])*invpw;
496 ys = (trans[1]-rtl[1])*invph;
498 if(!(xs >= 0 && xs < nw && ys >= 0 && ys < nh))
500 //sinfg::warning("Spherize: we failed to account for %f,%f",xs,ys);
501 p.put_value(context.get_color(trans));//Color::alpha());
505 //sample at that pixel location based on the quality
506 if(quality <= 4) //cubic
508 p.put_value(background.cubic_sample(xs,ys));
509 }else if(quality <= 5) //cosine
511 p.put_value(background.cosine_sample(xs,ys));
512 }else if(quality <= 6) //linear
514 p.put_value(background.linear_sample(xs,ys));
517 p.put_value(background[round_to_int(ys)][round_to_int(xs)]);
527 class Spherize_Trans : public Transform
529 etl::handle<const Layer_SphereDistort> layer;
531 Spherize_Trans(const Layer_SphereDistort* x):Transform(x->get_guid()),layer(x) { }
533 sinfg::Vector perform(const sinfg::Vector& x)const
535 return sphtrans(x,layer->center,layer->radius,-layer->percent,layer->type);
538 sinfg::Vector unperform(const sinfg::Vector& x)const
540 return sphtrans(x,layer->center,layer->radius,layer->percent,layer->type);
544 etl::handle<Transform>
545 Layer_SphereDistort::get_transform()const
547 return new Spherize_Trans(this);
551 Layer_SphereDistort::get_bounding_rect()const
553 Rect bounds(Rect::full_plane());