Subversion Repositories filter_foundry

Rev

Rev 193 | Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*  
  2.     This file is part of Curvaceous, a BŽzier drawing demo
  3.     Copyright (C) 1992-2006 Toby Thain, toby@telegraphics.com.au
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by  
  7.     the Free Software Foundation; either version 2 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License  
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. */
  19.  
  20. /* copyright (C) Toby Thain 1992-94 */
  21.  
  22. #include <fixmath.h> // X2Fix
  23. #include <fp.h> // sqrt,scalb
  24. #include <quickdraw.h>
  25. #include <toolutils.h> // FixMul
  26.  
  27. #include "curveto.h"
  28. #include "misc.h" // SGN
  29.  
  30. pt fpt(int x,int y){ pt p;
  31.         p.x = x<<16;
  32.         p.y = y<<16;
  33.         return p;
  34. }
  35.  
  36. void mid(pt *a,pt *b,pt *c){
  37.         a->x = (b->x + c->x)/2;
  38.         a->y = (b->y + c->y)/2;
  39. }
  40.  
  41. void
  42. curve_bounds_fix(pt c[],Fixed *l,Fixed *t,Fixed *r,Fixed *b){ int i; Fixed j;
  43.         *r = *l = c->x;
  44.         *b = *t = c->y;
  45.         for(i=3;++c,i--;){
  46.                 if((j = c->x) < *l)
  47.                         *l = j;
  48.                 else if(j > *r)
  49.                         *r = j;
  50.                 if((j = c->y) < *t)
  51.                         *t = j;
  52.                 else if(j > *b)
  53.                         *b = j;
  54.         }
  55. }
  56.  
  57. void
  58. curve_bounds(pt c[],Rect *rr){ Fixed l,t,r,b;
  59.         curve_bounds_fix(c,&l,&t,&r,&b);
  60.         rr->right = I(r);
  61.         rr->left = I(l);
  62.         rr->bottom = I(b);
  63.         rr->top = I(t);
  64. }
  65.  
  66. #define D(p) (dp = FixMul(dx,(p).y - c->y) + FixMul(dy,c->x - (p).x), FixMul(dp,dp) < dd)
  67.  
  68. void divide_curve(pt c[],pt z[]){
  69.         /* divide the curve into two; calculate the control points for each half */
  70.         pt f;
  71.  
  72.         z[0] = c[0];
  73.         z[6] = c[3];
  74.         mid(z+1,c,c+1);
  75.         mid(&f,c+1,c+2);
  76.         mid(z+5,c+2,c+3);
  77.         mid(z+2,z+1,&f);
  78.         mid(z+4,&f,z+5);
  79.         mid(z+3,z+2,z+4);
  80. }
  81.  
  82. #define DOT(A,B) (FixMul(A.x,B.x)+FixMul(A.y,B.y))
  83. #define FLT(x) scalb((x),-16)
  84.  
  85. int sgn(int x){
  86.         return SGN(x);
  87. }
  88.  
  89. double sqr(double_t x){
  90.         return x*x;
  91. }
  92.  
  93. int curve_step(pt c[],pt z[]){
  94.         pt perp,c1,c2,midpt;
  95.        
  96.         perp.x = c[0].y - c[3].y; perp.y = c[3].x - c[0].x; // a vector perpendicular to c[0]->c[3]
  97.         c1.x = c[1].x - c[0].x; c1.y = c[1].y - c[0].y; // c[0]->1st control point
  98.         c2.x = c[2].x - c[3].x; c2.y = c[2].y - c[3].y; // c[3]->2nd control point
  99.        
  100.         divide_curve(c,z);
  101.         mid(&midpt,&c[0],&c[3]);
  102.  
  103.         midpt.x -= z[3].x; midpt.y -= z[3].y;
  104.  
  105.         if(
  106.                 sgn(DOT(perp,c1))==sgn(DOT(perp,c2)) && // control points on same side
  107.                 (sqr(FLT(midpt.x))+sqr(FLT(midpt.y)))<1. // doing this calc in fixed point IS PRONE TO overflow!
  108.         ){
  109.                 /* MoveTo(P(c[0])); Line(P(perp));
  110.                 MoveTo(P(c[0])); Line(P(c1));
  111.                 MoveTo(P(c[3])); Line(P(c2));
  112.                 MoveTo(P(c[0])); */
  113.                
  114.                 LineTo(P(c[3]));
  115.                 return false;
  116.         }else
  117.                 return true;
  118. }
  119.  
  120. void curveto(pt c[]){ pt z[7];// draw the entire curve described by c[0..3]
  121.         if(curve_step(c,z)){ /* the curve was not flat enough - divide into two */
  122.                 curveto(z);
  123.                 curveto(z+3);
  124.         }
  125. }
  126.  
  127. int curve_step2(pt c[],pt z[],int d){
  128.         if( ( abs(I(c[0].x)-I(c[3].x))<2 && abs(I(c[0].y)-I(c[3].y))<2 ) /*|| !d*/ ){
  129.                 MoveTo(P(c[0]));
  130.                 Line(0,0);
  131.                 return false;
  132.         }else{
  133.                 divide_curve(c,z);
  134.                 return true;
  135.         }      
  136. }
  137.  
  138. void curveto2(pt c[],int d){ pt z[7];// draw the entire curve described by c[0..3]
  139.         if(curve_step2(c,z,d)){ /* the curve was not flat enough - divide into two */
  140.                 curveto2(z,d-1);
  141.                 curveto2(z+3,d-1);
  142.         }
  143. }
  144.  
  145. void
  146. curveto_rgn(pt c[],RgnHandle rgn){ // optimised to draw only parts of the curve inside rgn
  147.         Rect r;
  148.         pt z[7];
  149.         RgnHandle rgn2;
  150.        
  151.         curve_bounds(c,&r);
  152.         // should add some slop to the above rectangle
  153.         if(RectInRgn(&r,rgn)){ /* any part of the curve is within "rgn" */
  154.                 MoveTo(P(*c));
  155.                 RectRgn(rgn2 = NewRgn(),&r);
  156.                 DiffRgn(rgn2,rgn,rgn2);
  157.                 if(EmptyRgn(rgn2)) /* the curve is entirely inside "rgn" */
  158.                         curveto(c); /* so we can draw the whole curve without checking further */
  159.                 else /* only part of the curve is inside "rgn" */
  160.                         if(curve_step(c,z)){ /* subdivide further */
  161.                                 curveto_rgn(z,rgn); /* do each half */
  162.                                 curveto_rgn(z+3,rgn);
  163.                         }
  164.                 DisposeRgn(rgn2);
  165.         }
  166. }
  167.  
  168. Boolean curve_pick_step(pt c[],pt *p,Fixed tol,Fixed *param,Fixed p0,Fixed p1){
  169.                 // note: tolerance parameter is pixels squared
  170.         Fixed l,t,r,b,ax,ay,dx,dy,dd,dp,half,tt;
  171.        
  172.         curve_bounds_fix(c,&l,&t,&r,&b);
  173.         if(p->x > (l-tol) && p->x < (r+tol) && p->y > (t-tol) && p->y < (b+tol)){
  174.                 dx = c[3].x - c->x;
  175.                 dy = c[3].y - c->y;
  176.                 dd = FixMul(dx,dx) + FixMul(dy,dy);
  177.                 if(D(c[1]) && D(c[2])){ /* D(p) is the "flatness" criterion - test both control points */
  178.                         // the curve is "flat" enough to test the point against the line c[0]--c[3]
  179.  
  180.                         // a -> vector from c[0] to p = (ax,ay)
  181.                         ax = p->x - c->x;
  182.                         ay = p->y - c->y;
  183.                         // b -> vector from c[0] to c[3] = (dx,dy)
  184.  
  185.                         // distance of a's projection along vector perpendicular to b (call it B)
  186.                         // = |a| x cos theta = a.B / |B|
  187.                         // squared distance = (a.B)^2 / |B|^2
  188.                         // |B|^2 is known due to Pythagoras' theorem ( = dd = dx^2 + dy^2)
  189.                         // is this distance less than the tolerance?
  190.                        
  191.                         dp = FixMul(ax,-dy) + FixMul(ay,dx); // == a.B
  192.                         if(             (FixMul(ax,ax) + FixMul(ay,ay)) < FixMul(tol,tol)
  193.                                 ||      ( FixMul(dp,dp) < FixMul(FixMul(tol,tol),dd)
  194.                                         && (dp = FixMul(ax,dx) + FixMul(ay,dy)) > 0
  195.                                         && (FixMul(p->x - c[3].x,dx) + FixMul(p->y - c[3].y,dy)) < 0 ) ){ // dp == a.b
  196.  
  197.                                 tt = FixDiv(dp,dd);
  198.                                 *param = p0 + FixMul(tt,p1 - p0);
  199.                                
  200.                                 // update target point with actual closest point
  201.                                 p->x = c->x + FixMul(tt,dx);
  202.                                 p->y = c->y + FixMul(tt,dy);
  203.                                 return true;
  204.                         }else
  205.                                 return false;
  206.                 }else{ pt z[7];
  207.                         divide_curve(c,z);
  208.                         half = (p0+p1)/2;
  209.                         return curve_pick_step(z,p,tol,param,p0,half) || curve_pick_step(z+3,p,tol,param,half,p1);
  210.                 }
  211.         }else
  212.                 return false;
  213. }
  214.  
  215. Boolean curve_pick(pt c[],pt *p,Fixed tol,Fixed *t){
  216.         return curve_pick_step(c,p,tol,t,F(0),F(1));
  217. }
  218.  
  219. void mark(pt *p,Boolean f){
  220.         Rect r;
  221.         r.bottom = (r.top = I(p->y) - 2) + 4;
  222.         r.right = (r.left = I(p->x) - 2) + 4;
  223.         f ? PaintRect(&r) : FrameRect(&r);
  224. }
  225.