morphyface.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1999-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Wed Sep  8 15:38:42 1999
00010 // commit: $Id: morphyface.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/visx/morphyface.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED
00035 #define GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED
00036 
00037 #include "visx/morphyface.h"
00038 
00039 #include "geom/bezier4.h"
00040 #include "geom/rect.h"
00041 #include "geom/vec3.h"
00042 
00043 #include "gfx/bbox.h"
00044 #include "gfx/canvas.h"
00045 #include "gfx/gxscaler.h"
00046 #include "gfx/linestrip.h"
00047 
00048 #include "io/ioproxy.h"
00049 #include "io/reader.h"
00050 #include "io/writer.h"
00051 
00052 #include "rutz/algo.h"
00053 
00054 #include "rutz/trace.h"
00055 #include "rutz/debug.h"
00056 GVX_DBG_REGISTER
00057 
00058 using geom::vec2d;
00059 using geom::vec3d;
00060 
00062 //
00063 // File scope definitions
00064 //
00066 
00067 namespace
00068 {
00069   const io::version_id MFACE_SVID = 2;
00070 
00071   const unsigned int NUM_HAIR_POINTS = 15; // can't change this without
00072                                            // also changing "hair_widths"
00073                                            // below
00074   const double* getHairVertices(double top_width, double hair_width)
00075   {
00076     GVX_TRACE("getHairVertices");
00077 
00078     static double hair_vertices[NUM_HAIR_POINTS*4];
00079 
00080     static const double hair_widths[NUM_HAIR_POINTS] =
00081     {
00082       0.40, 0.90, 0.95,
00083       1.00, 0.97, 0.94,
00084       0.91, 0.88, 0.85,
00085       0.81, 0.78, 0.75,
00086       0.7, 0.6, 0.0
00087     };
00088 
00089     geom::bezier4 xbezier(-1.0, -top_width, top_width, 1.0);
00090     geom::bezier4 ybezier( 0.0,  4.0/3.0  , 4.0/3.0  , 0.0);
00091 
00092     for (unsigned int i = 0; i < NUM_HAIR_POINTS; ++i)
00093       {
00094         const double u = 0.5 + double(i)/double(2*NUM_HAIR_POINTS-2);
00095 
00096         const double x = xbezier.eval(u);
00097         const double y = ybezier.eval(u);
00098 
00099         const double tang_x = xbezier.eval_deriv(u);
00100         const double tang_y = ybezier.eval_deriv(u);
00101 
00102         // tangent vector = (tang_x, tang_y)
00103         // ==> normal vector = (-tang_y, tang_x)
00104         const double norm_x = -tang_y;
00105         const double norm_y = tang_x;
00106 
00107         // compute the factor needed to make a unit normal vector
00108         const double norm_factor = 1.0 / sqrt(norm_x*norm_x + norm_y*norm_y);
00109 
00110         const double hair_x_normal = norm_x * norm_factor;
00111         const double hair_y_normal = norm_y * norm_factor;
00112 
00113         const double width_factor = hair_widths[i]*hair_width;
00114 
00115         hair_vertices[4*i]   = x - hair_x_normal*width_factor; // inner x value
00116         hair_vertices[4*i+1] = y - hair_y_normal*width_factor; // inner y value
00117         hair_vertices[4*i+2] = x + hair_x_normal*width_factor; // outer x value
00118         hair_vertices[4*i+3] = y + hair_y_normal*width_factor; // outer y value
00119       }
00120 
00121     return &hair_vertices[0];
00122   }
00123 
00124   void drawHairStrip(Gfx::Canvas& canvas,
00125                      const double* vertices, double x_scale, double y_scale)
00126   {
00127     Gfx::MatrixSaver saver(canvas);
00128 
00129     canvas.scale(vec3d(x_scale, y_scale, 1.0));
00130 
00131     Gfx::QuadStripBlock block(canvas);
00132 
00133     for (unsigned int i = 0; i < NUM_HAIR_POINTS; ++i)
00134       {
00135         canvas.vertex2(vec2d(vertices[4*i  ], vertices[4*i+1]));
00136         canvas.vertex2(vec2d(vertices[4*i+2], vertices[4*i+3]));
00137       }
00138   }
00139 
00140 }
00141 
00143 //
00144 // MorphyFace member functions
00145 //
00147 
00149 // Creators
00151 
00152 const FieldMap& MorphyFace::classFields()
00153 {
00154   typedef MorphyFace MF;
00155 
00156   static const Field FIELD_ARRAY[] =
00157   {
00158     Field("category", &MF::itsMfaceCategory, 0, 0, 10, 1, Field::NEW_GROUP),
00159 
00160     Field("faceWidth", &MF::itsFaceWidth, 2.75, 1.5, 3.5, 0.1),
00161     Field("topWidth", &MF::itsTopWidth, 1.15, 0.05, 2.0, 0.05),
00162     Field("bottomWidth", &MF::itsBottomWidth, 1.0, 0.05, 2.0, 0.05),
00163     Field("topHeight", &MF::itsTopHeight, 3.8, 0.5, 5.0, 0.25),
00164     Field("bottomHeight", &MF::itsBottomHeight, -3.0, -5.0, -0.5, 0.25),
00165     Field("strokeWidth", &MF::itsStrokeWidth, 1.0, 0.1, 10.0, 0.1,
00166           Field::TRANSIENT),
00167     Field("lineJoin", &MF::itsLineJoin, true, false, true, true,
00168           Field::TRANSIENT | Field::BOOLEAN),
00169 
00170     Field("hairWidth", &MF::itsHairWidth, 0.20, 0.00, 0.5, 0.02, Field::NEW_GROUP),
00171     Field("hairStyle", &MF::itsHairStyle, 0, 0, 1, 1),
00172 
00173     Field("eyeYpos", &MF::itsEyeYpos, 0.375, -2.0, 2.0, 0.1, Field::NEW_GROUP),
00174     Field("eyeDistance", &MF::itsEyeDistance, 2.25, 0.0, 5.0, 0.25),
00175     Field("eyeHeight", &MF::itsEyeHeight, 0.9, 0.0, 2.0, 0.1),
00176     Field("eyeAspectRatio", &MF::itsEyeAspectRatio, 1.555556, 0.1, 5.0, 0.1),
00177 
00178     Field("pupilXpos", &MF::itsPupilXpos, 0.0, -0.5, 0.5, 0.05, Field::NEW_GROUP),
00179     Field("pupilYpos", &MF::itsPupilYpos, 0.0, -0.5, 0.5, 0.05),
00180     Field("pupilSize", &MF::itsPupilSize, 0.6, 0.0, 1.0, 0.05),
00181     Field("pupilDilation", &MF::itsPupilDilation,
00182           0.5, 0.05, 0.95, 0.05, Field::CHECKED),
00183 
00184     Field("eyebrowXpos", &MF::itsEyebrowXpos, 0.0, -0.5, 0.5, 0.02,
00185           Field::NEW_GROUP),
00186     Field("eyebrowYpos", &MF::itsEyebrowYpos, 0.5, 0.0, 1.5, 0.05),
00187     Field("eyebrowCurvature", &MF::itsEyebrowCurvature, 0.8, -2.0, 2.0, 0.1),
00188     Field("eyebrowAngle", &MF::itsEyebrowAngle, -5.0, -50.0, 50.0, 1.0),
00189     Field("eyebrowThickness", &MF::itsEyebrowThickness, 2.0, 0.1, 4.0, 0.1),
00190 
00191     Field("noseXpos", &MF::itsNoseXpos, 0.0, -1.0, 1.0, 0.05, Field::NEW_GROUP),
00192     Field("noseYpos", &MF::itsNoseYpos, -0.825, -2.0, 2.0, 0.1),
00193     Field("noseLength", &MF::itsNoseLength, 0.75, 0.0, 2.0, 0.1),
00194     Field("noseWidth", &MF::itsNoseWidth, 1.5, 0.0, 3.0, 0.1),
00195 
00196     Field("mouthXpos", &MF::itsMouthXpos, 0.0, -2.0, 2.0, 0.1, Field::NEW_GROUP),
00197     Field("mouthYpos", &MF::itsMouthYpos, -2.0, -3.0, 1.0, 0.1),
00198     Field("mouthWidth", &MF::itsMouthWidth, 2.5, 0.0, 5.0, 0.25),
00199     Field("mouthCurvature", &MF::itsMouthCurvature, 0.6, -2.0, 2.0, 0.1)
00200   };
00201 
00202   static FieldMap MFACE_FIELDS(FIELD_ARRAY, &GxShapeKit::classFields());
00203 
00204   return MFACE_FIELDS;
00205 }
00206 
00207 MorphyFace* MorphyFace::make()
00208 {
00209 GVX_TRACE("MorphyFace::make");
00210   return new MorphyFace;
00211 }
00212 
00213 MorphyFace::MorphyFace() :
00214   GxShapeKit(),
00215 
00216   itsMfaceCategory(0),
00217 
00218   itsFaceWidth(2.75),
00219   itsTopWidth(1.15),
00220   itsBottomWidth(1.0),
00221   itsTopHeight(3.8),
00222   itsBottomHeight(-3.0),
00223 
00224   itsHairWidth(0.15),
00225   itsHairStyle(0),
00226 
00227   itsEyeYpos(0.375),
00228   itsEyeDistance(2.25),
00229   itsEyeHeight(0.9),
00230   itsEyeAspectRatio(1.555556),
00231 
00232   itsPupilXpos(0.0),
00233   itsPupilYpos(0.0),
00234   itsPupilSize(0.6),
00235   itsPupilDilation(0.5),
00236 
00237   itsEyebrowXpos(0.0),
00238   itsEyebrowYpos(0.5),
00239   itsEyebrowCurvature(0.8),
00240   itsEyebrowAngle(-5),
00241   itsEyebrowThickness(2.0),
00242 
00243   itsNoseXpos(0.0),
00244   itsNoseYpos(-0.825),
00245   itsNoseLength(0.75),
00246   itsNoseWidth(1.5),
00247 
00248   itsMouthXpos(0.0),
00249   itsMouthYpos(-2.0),
00250   itsMouthWidth(2.5),
00251   itsMouthCurvature(0.6),
00252 
00253   itsStrokeWidth(3.0),
00254   itsLineJoin(true)
00255 {
00256 GVX_TRACE("MorphyFace::MorphyFace");
00257 
00258   setFieldMap(MorphyFace::classFields());
00259 
00260   setScalingMode(GxScaler::MAINTAIN_ASPECT_SCALING);
00261   setMaxDimension(1.0);
00262 }
00263 
00264 MorphyFace::~MorphyFace() throw()
00265 {
00266 GVX_TRACE("MorphyFace::~MorphyFace");
00267   // nothing to do
00268 }
00269 
00270 io::version_id MorphyFace::class_version_id() const
00271 {
00272 GVX_TRACE("MorphyFace::class_version_id");
00273   return MFACE_SVID;
00274 }
00275 
00276 void MorphyFace::read_from(io::reader& reader)
00277 {
00278 GVX_TRACE("MorphyFace::read_from");
00279 
00280   reader.ensure_version_id("MorphyFace", 2,
00281                            "Try cvs tag xml_conversion_20040526",
00282                            SRC_POS);
00283 
00284   readFieldsFrom(reader, classFields());
00285 
00286   reader.read_base_class("GxShapeKit", io::make_proxy<GxShapeKit>(this));
00287 }
00288 
00289 void MorphyFace::write_to(io::writer& writer) const
00290 {
00291 GVX_TRACE("MorphyFace::write_to");
00292 
00293   writer.ensure_output_version_id("MorphyFace", MFACE_SVID, 2,
00294                               "Try groovx0.8a4", SRC_POS);
00295 
00296   writeFieldsTo(writer, classFields(), MFACE_SVID);
00297 
00298   writer.write_base_class("GxShapeKit", io::make_const_proxy<GxShapeKit>(this));
00299 }
00300 
00301 
00303 // Actions
00305 
00306 void MorphyFace::grRender(Gfx::Canvas& canvas) const
00307 {
00308 GVX_TRACE("MorphyFace::grRender");
00309 
00310   Gfx::AttribSaver attribSaver(canvas);
00311 
00312   //canvas.enableAntialiasing();
00313 
00314   Gfx::LineStrip ls;
00315   ls.lineJoin(itsLineJoin);
00316 
00317   //
00318   // Draw eyes
00319   //
00320 
00321   const vec3d eye_ctrlpnts[4] =
00322   {
00323     vec3d(-3.0/7.0, 0.0,     0.0),
00324     vec3d(-2.0/7.0, 2.0/3.0, 0.0),
00325     vec3d( 2.0/7.0, 2.0/3.0, 0.0),
00326     vec3d( 4.0/7.0, 0.0,     0.0)
00327   };
00328 
00329   const int eye_subdivisions = 20;
00330 
00331   for (int left_right = -1; left_right < 2; left_right += 2)
00332     {
00333       Gfx::MatrixSaver msaver(canvas);
00334 
00335       // Do appropriate reflection
00336       canvas.scale(vec3d(left_right*1.0, 1.0, 1.0));
00337 
00338       // Move to the eye position
00339       canvas.translate(vec3d(rutz::abs(itsEyeDistance)/2.0,
00340                              itsEyeYpos, 0.0));
00341 
00342       // Draw eye outline
00343       {
00344         GVX_TRACE("MorphyFace::grRender-draw eye outline");
00345 
00346         const vec3d s1(itsEyeHeight*itsEyeAspectRatio,
00347                        -itsEyeHeight,
00348                        1.0);
00349         const vec3d s2(itsEyeHeight*itsEyeAspectRatio,
00350                        itsEyeHeight,
00351                        1.0);
00352 
00353         ls.closeLoop(true);
00354         ls.begin(canvas, 0.01*itsStrokeWidth);
00355         // Two bezier curves end to end, sharing common vertices in the middle
00356         ls.drawBezier4(s1*eye_ctrlpnts[3], s1*eye_ctrlpnts[2],
00357                        s1*eye_ctrlpnts[1], s1*eye_ctrlpnts[0],
00358                        eye_subdivisions, 1 /* skip the first vertex since
00359                                               it will be wrapped around to
00360                                               in the next bezier curve */);
00361         ls.drawBezier4(s2*eye_ctrlpnts[0], s2*eye_ctrlpnts[1],
00362                        s2*eye_ctrlpnts[2], s2*eye_ctrlpnts[3],
00363                        eye_subdivisions, 1 /* skip the first vertex since
00364                                               it was already covered by the
00365                                               previous bezier curve */);
00366         ls.end();
00367         ls.closeLoop(false);
00368       }
00369 
00370       // Draw eyebrow
00371       {
00372         GVX_TRACE("MorphyFace::grRender-draw eyebrow");
00373 
00374         Gfx::MatrixSaver msaver3(canvas);
00375 
00376         canvas.translate(vec3d(itsEyebrowXpos, itsEyebrowYpos, 0.0));
00377         canvas.rotate(vec3d::unit_z(), itsEyebrowAngle);
00378 
00379         Gfx::AttribSaver asaver(canvas);
00380 
00381         canvas.setLineWidth(itsEyebrowThickness);
00382 
00383         const vec3d s(itsEyeHeight*itsEyeAspectRatio,
00384                       itsEyeHeight*itsEyebrowCurvature,
00385                       1.0);
00386 
00387         ls.begin(canvas, 0.01*itsStrokeWidth*itsEyebrowThickness);
00388         ls.drawBezier4(s*eye_ctrlpnts[0], s*eye_ctrlpnts[1],
00389                        s*eye_ctrlpnts[2], s*eye_ctrlpnts[3],
00390                        eye_subdivisions);
00391         ls.end();
00392       }
00393 
00394       // Draw pupil
00395       {
00396         GVX_TRACE("MorphyFace::grRender-draw pupil");
00397 
00398         Gfx::MatrixSaver msaver4(canvas);
00399 
00400         canvas.translate(vec3d(left_right*itsPupilXpos,
00401                                itsPupilYpos, 0.0));
00402 
00403         double radius = 0.5 * itsPupilSize * itsEyeHeight;
00404 
00405         static const int num_slices = 20;
00406         static const int num_loops = 1;
00407 
00408         canvas.drawCircle(radius*rutz::abs(itsPupilDilation), radius, true,
00409                           num_slices, num_loops);
00410       }
00411     }
00412 
00413   //
00414   // Draw face outline.
00415   //
00416 
00417   ls.closeLoop(true);
00418   ls.begin(canvas, 0.015*itsStrokeWidth);
00419   ls.drawBezier4(vec3d(itsFaceWidth, 0.0, 0.0),
00420                  vec3d(itsBottomWidth*itsFaceWidth,
00421                        itsBottomHeight*4.0/3.0, 0.0),
00422                  vec3d(-itsBottomWidth*itsFaceWidth,
00423                        itsBottomHeight*4.0/3.0, 0.0),
00424                  vec3d(-itsFaceWidth, 0.0, 0.0),
00425                  50, 1);
00426   ls.drawBezier4(vec3d(-itsFaceWidth, 0.0, 0.0),
00427                  vec3d(-itsTopWidth*itsFaceWidth,
00428                        itsTopHeight*4.0/3.0, 0.0),
00429                  vec3d(itsTopWidth*itsFaceWidth,
00430                        itsTopHeight*4.0/3.0, 0.0),
00431                  vec3d(itsFaceWidth, 0.0, 0.0),
00432                  50, 1);
00433   ls.end();
00434   ls.closeLoop(false);
00435 
00436   //
00437   // Draw nose.
00438   //
00439 
00440   {
00441     GVX_TRACE("MorphyFace::grRender-draw nose");
00442 
00443     Gfx::MatrixSaver msaver5(canvas);
00444 
00445     canvas.translate(vec3d(itsNoseXpos, itsNoseYpos, 0.0));
00446 
00447     const vec2d s(rutz::abs(itsNoseWidth)/2.0,
00448                   rutz::abs(itsNoseLength));
00449 
00450     ls.begin(canvas, 0.01*itsStrokeWidth);
00451     ls.vertex(s*vec2d(-0.75, 0.5));
00452     ls.vertex(s*vec2d(-1.0,  0.0));
00453     ls.vertex(s*vec2d(-0.75, -0.333333));
00454     ls.vertex(s*vec2d(-0.25, -0.333333));
00455     ls.vertex(s*vec2d( 0.0, -0.5));   // CENTER
00456     ls.vertex(s*vec2d( 0.25, -0.333333));
00457     ls.vertex(s*vec2d( 0.75, -0.333333));
00458     ls.vertex(s*vec2d( 1.0,  0.0));
00459     ls.vertex(s*vec2d( 0.75, 0.5));
00460     ls.end();
00461   }
00462 
00463   //
00464   // Draw mouth.
00465   //
00466 
00467   {
00468     GVX_TRACE("MorphyFace::grRender-draw mouth");
00469 
00470     Gfx::MatrixSaver msaver6(canvas);
00471 
00472     canvas.translate(vec3d(itsMouthXpos, itsMouthYpos, 0.0));
00473 
00474     const vec3d s(itsMouthWidth, itsMouthCurvature, 1.0);
00475 
00476     ls.begin(canvas, 0.01*itsStrokeWidth);
00477     ls.drawBezier4(s*vec3d(-0.5,  0.5,      0.0),
00478                    s*vec3d(-0.2, -0.833333, 0.0),
00479                    s*vec3d( 0.2, -0.833333, 0.0),
00480                    s*vec3d( 0.5,  0.5,      0.0),
00481                    30);
00482     ls.end();
00483   }
00484 
00485   //
00486   // Draw hair.
00487   //
00488 
00489   {
00490     GVX_TRACE("MorphyFace::grRender-draw hair");
00491 
00492     Gfx::AttribSaver saver(canvas);
00493 
00494     canvas.setPolygonFill((itsHairStyle == 0));
00495 
00496     const double* hair_vertices = getHairVertices(itsTopWidth, itsHairWidth);
00497 
00498     // Draw right side
00499     drawHairStrip(canvas, hair_vertices, itsFaceWidth, itsTopHeight);
00500 
00501     // Draw left side
00502     drawHairStrip(canvas, hair_vertices, -itsFaceWidth, itsTopHeight);
00503   }
00504 }
00505 
00507 // Accessors
00509 
00510 void MorphyFace::grGetBoundingBox(Gfx::Bbox& bbox) const
00511 {
00512 GVX_TRACE("MorphyFace::grGetBoundingBox");
00513 
00514   geom::bezier4 xbezier_top(-1.0, -itsTopWidth, itsTopWidth, 1.0);
00515   geom::bezier4 xbezier_bottom(1.0, itsBottomWidth, -itsBottomWidth, -1.0);
00516 
00517   double top_width = xbezier_top.eval_max();
00518   double bottom_width = xbezier_bottom.eval_max();
00519 
00520   dbg_eval(3, top_width);   dbg_eval_nl(3, bottom_width);
00521 
00522   double max_width = rutz::max(1.0, rutz::max(top_width, bottom_width));
00523 
00524   dbg_eval_nl(3, max_width);
00525 
00526   const double l = -max_width      * itsFaceWidth * (1 + itsHairWidth);
00527   const double r =  max_width      * itsFaceWidth * (1 + itsHairWidth);
00528   const double t =  itsTopHeight * (1 + itsHairWidth);
00529   const double b =  itsBottomHeight;
00530 
00531   bbox.drawRect(geom::rect<double>::ltrb(l,t,r,b));
00532 }
00533 
00534 static const char __attribute__((used)) vcid_groovx_visx_morphyface_cc_utc20050626084017[] = "$Id: morphyface.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00535 #endif // !GROOVX_VISX_MORPHYFACE_CC_UTC20050626084017_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:42 2008 by Doxygen version 1.5.5.