/* This file is part of patharea, a Filter plugin for Adobe Illustrator Copyright (C) 1995-2005 Toby Thain, toby@telegraphics.com.au This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 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 */ #include "common.h" // must come before aipluginmain.h AIBlockSuite *sBlock; AIMatchingArtSuite *sMatch; AIFixedMathSuite *sMath; #ifdef kAIRealMathSuite AIRealMathSuite *sRealMath; #endif AIArtSuite *sArt; AIPathSuite *sPath; AIMdMemorySuite *sMdMemory; ADMBasicSuite *sADMBasic; AIFloat length,area,totalarea,baseline; #include "aipluginmain.h" #include "str.h" #include "version.h" AIErr acquire_suites( SPMessageData *m ) { AIErr e; (e = acq_suite(m,kAIBlockSuite,kAIBlockVersion,(ppsuite)&sBlock) ) || (e = acq_suite(m,kAIMatchingArtSuite,kAIMatchingArtVersion,(ppsuite)&sMatch) ) || (e = acq_suite(m,kAIFixedMathSuite,kAIFixedMathVersion,(ppsuite)&sMath) ) #ifdef kAIRealMathSuite || (e = acq_suite(m,kAIRealMathSuite,kAIRealMathVersion,(ppsuite)&sRealMath) ) #endif || (e = acq_suite(m,kAIPathSuite,kAIPathVersion,(ppsuite)&sPath) ) || (e = acq_suite(m,kAIArtSuite,kAIArtVersion,(ppsuite)&sArt) ) || (e = acq_suite(m,kAIMdMemorySuite,kAIMdMemoryVersion,(ppsuite)&sMdMemory) ) || (e = acq_suite(m,kADMBasicSuite,kADMBasicSuiteVersion,(ppsuite)&sADMBasic) ) ; return e; } AIErr release_suites( SPMessageData *m ) { m->basic->ReleaseSuite(kAIBlockSuite,kAIBlockVersion); m->basic->ReleaseSuite(kAIMatchingArtSuite,kAIMatchingArtVersion); m->basic->ReleaseSuite(kAIFixedMathSuite,kAIFixedMathVersion); #ifdef kAIRealMathSuite m->basic->ReleaseSuite(kAIRealMathSuite,kAIRealMathVersion); #endif m->basic->ReleaseSuite(kAIPathSuite,kAIPathVersion); m->basic->ReleaseSuite(kAIArtSuite,kAIArtVersion); m->basic->ReleaseSuite(kAIMdMemorySuite,kAIMdMemoryVersion); m->basic->ReleaseSuite(kADMBasicSuite,kADMBasicSuiteVersion); return kNoErr; } #define FILTER_CATEGORY "Telegraphics" #define FILTER_TITLE "Path area" AIErr plugin_startup ( SPInterfaceMessage *im ) { AIErr e; PlatformAddFilterData fd; AIFilterHandle fh; AIFilterSuite *sfilter; unsigned char cstr[sizeof(FILTER_CATEGORY)+1],tstr[sizeof(FILTER_TITLE)+1]; if( !(e = acq_suite(&im->d,kAIFilterSuite,kAIFilterVersion,(ppsuite)&sfilter) ) ){ // gcc (mingw32) doesn't support "\p" Pascal string constants fd.category = myc2pstrcpy(cstr,FILTER_CATEGORY); fd.title = myc2pstrcpy(tstr,FILTER_TITLE); if( e = sfilter->AddFilter( im->d.self,"telegraphics-patharea",&fd,0,&fh) ) dbg("plugin_startup: AddFilter failed"); im->d.basic->ReleaseSuite(kAIFilterSuite,kAIFilterVersion); } return e; } AIErr plugin_shutdown( SPInterfaceMessage *im ) { return kNoErr; } AIErr plugin_about( SPInterfaceMessage *im ) { AIErr e; ADMBasicSuite *admbasic; if( !(e = acq_suite(&im->d,kADMBasicSuite,kADMBasicSuiteVersion,(ppsuite)&admbasic) ) ){ char s[0x100]; sprintf(s,"patharea %s\nCopyright © 1995-2006 Toby Thain \nhttp://www.telegraphics.com.au",VERSION_STR); admbasic->MessageAlert(s); im->d.basic->ReleaseSuite(kADMBasicSuite,kADMBasicSuiteVersion); } return e; } /*---------*/ #define DOT(A,B) ( FLT(A.h)*FLT(B.h) + FLT(A.v)*FLT(B.v) ) void mid(pt *a,pt *b,pt *c){ a->h = (b->h + c->h)/2; a->v = (b->v + c->v)/2; } void divide_curve(pt c[],pt z[]){ /* divide the curve into two; calculate the control points for each half */ pt f; z[0] = c[0]; z[6] = c[3]; mid(z+1,c,c+1); mid(&f,c+1,c+2); mid(z+5,c+2,c+3); mid(z+2,z+1,&f); mid(z+4,&f,z+5); mid(z+3,z+2,z+4); } /*---------*/ void docurve(SPMessageData *m,pt c[],int addlength,int depth){ pt z[7],c1,c2,c3,perp,midpt; c1.h = c[1].h - c[0].h; c1.v = c[1].v - c[0].v; c2.h = c[2].h - c[3].h; c2.v = c[2].v - c[3].v; c3.h = c[3].h - c[0].h; c3.v = c[3].v - c[0].v; perp.h = -c3.v; perp.v = c3.h; // a vector perpendicular to c[0]->c[3] divide_curve(c,z); mid(&midpt,c,c+3); // check that c[1] & c[2] are on the same side of the line c[0]--c[3] (use dot product) if( !depth || ( DOT(perp,c1)*DOT(perp,c2) >= 0. && POINTCLOSE(&midpt,z+3,CURVE_TOLERANCE) ) ){ if(addlength) length += FLT(LENGTH(c3.h,c3.v)); area += (FLT(c[3].h - c[0].h)*FLT(c[3].v + c[0].v))/2.; }else{ docurve(m,z,addlength,depth-1); // z[0..3] describe first half of untransformed curve docurve(m,z+3,addlength,depth-1); // z[3..6] describe second half } } AIErr do_art( SPMessageData *m,AIArtHandle path ){ short count; int i; AIErr e = kNoErr; AIPathSegment *in_segs,*seg; AIBoolean closed; pt c[4]; if( !(e = sPath->GetPathClosed ( path,&closed )) && !(e = sPath->GetPathSegmentCount( path,&count )) && count && !(e = sBlock->AllocateBlock(sizeof(AIPathSegment)*count,(void**)&in_segs)) ){ if( !(e = sPath->GetPathSegments( path,0,count,in_segs )) ){ if(count>1){ area = 0.; for (seg = in_segs, i = count; --i; seg++) { c[0] = seg->p; c[1] = seg->out; c[2] = seg[1].in; c[3] = seg[1].p; docurve(m,c,true,MAX_DEPTH); } // whether path is closed or not, assume a "closing" segment // from last node back to first. This is always added to area // but only accumulated into length if path is closed. c[0] = seg->p; c[1] = seg->out; c[2] = in_segs->in; c[3] = in_segs->p; docurve(m,c,closed,MAX_DEPTH); // accumulate area into total if(area<0.) totalarea -= area; /* path was counterclockwise (or possibly self-intersecting) */ else totalarea += area; } } sBlock->DisposeBlock(in_segs); } return e; } AIErr filter_go( AIFilterMessage *fm ) { SPMessageData *m = &fm->d; AIArtHandle **matches,*p; long i,count; AIErr e = kNoErr; AIMatchingArtSpec spec;// = {kPathArt,kArtSelected,kArtSelected}; char s[0x100]; AIRECT bounds; void *deref; if( !(e = acquire_suites(m)) ){ spec.type = kPathArt; spec.whichAttr = spec.attr = kArtSelected; if( !(e = sMatch->GetMatchingArt( &spec,1,&matches,&count )) && matches ) { sMdMemory->MdMemoryLock((AIMdMemoryHandle)matches,&deref); length = totalarea = 0.; for (i = count,p = (AIArtHandle*)deref; i--; p++){ sArt->GetArtBounds(*p,&bounds); baseline = bounds.bottom - 1.; // "The bounds of a path are precise to within a point." if(e = do_art(m,*p)) break; /* here's a good place to update progress bar */ } sprintf(s,"Selected paths:\nLength = %gpt = %.2fmm = %.3fin\nArea = %gsqmm = %gsqin", length, length*(25.4/72.), length/72., totalarea*(645.16/5184.), totalarea/5184.); sADMBasic->MessageAlert(s); sMdMemory->MdMemoryDisposeHandle((AIMdMemoryHandle)matches); } release_suites( m ); } return e; }