/* This file is part of patharea, a Filter plugin for Adobe(R) Illustrator(R) Copyright (C) 1995-2010 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 #include "aipluginmain.h" #include "str.h" #include "version.h" AIBlockSuite *sBlock; AIMatchingArtSuite *sMatch; AIFixedMathSuite *sMath; #ifdef kAIRealMathSuite AIRealMathSuite *sRealMath; #endif AIArtSuite *sArt; AIPathSuite *sPath; AIMdMemorySuite *sMdMemory; ADMBasicSuite *sADMBasic; AIFloat length, area, totalarea; 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) ) ){ admbasic->MessageAlert("patharea " VERSION_STR "\n" "Copyright (C) 1995-2011 Toby Thain \n" "http://www.telegraphics.com.au\n"); 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]; 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++) if(e = do_art(m, *p)) break; /* here's a good place to update progress bar */ sMdMemory->MdMemoryDisposeHandle((AIMdMemoryHandle)matches); sprintf(s,"Selected paths:\nLength = %g pt = %.2f mm = %.3f in\nArea = %g sq.mm = %g sq.in", length, length*(25.4/72.), length/72., totalarea*(645.16/5184.), totalarea/5184.); sADMBasic->MessageAlert(s); } release_suites( m ); } return e; }