// -------------------------------------------------------------------- // Ipelet for several cute little functions, like // precise scale and rotate, precise boxes, marking circle centers // -------------------------------------------------------------------- /* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2007 Otfried Cheong Ipe 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. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe 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 Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ipelib.h" // -------------------------------------------------------------------- class GoodiesIpelet : public Ipelet { public: virtual int IpelibVersion() const { return IPELIB_VERSION; } virtual int NumFunctions() const { return 12; } virtual const char *Label() const { return "Goodies"; } virtual const char *SubLabel(int function) const; virtual void Run(int function, IpePage *page, IpeletHelper *helper); }; // -------------------------------------------------------------------- const char * const sublabel[] = { "Mirror horizontal", "Mirror vertical", "Turn 90 degrees", "Turn 180 degrees", "Turn 270 degrees", "Precise rotate", "Precise stretch", "Insert precise box", "Insert bounding box", "Insert media box", "Mark circle center", "Make parabolas" }; const char *GoodiesIpelet::SubLabel(int function) const { return sublabel[function]; } // -------------------------------------------------------------------- void MarkCircleCenter(IpePage *page, IpeletHelper *helper) { IpePage::iterator it = page->PrimarySelection(); if (it == page->end()) { helper->Message("Nothing selected"); return; } IpePath *obj = it->Object()->AsPath(); if (!obj || obj->NumSubPaths() > 1 || obj->SubPath(0)->Type() != IpeSubPath::EEllipse) { helper->Message("Primary selection is not a circle"); return; } IpeMatrix m = obj->Matrix() * obj->SubPath(0)->AsEllipse()->Matrix(); IpeMark *mark = new IpeMark(helper->Attributes(), m.Translation()); page->push_back(IpePgObject(IpePgObject::ESecondary, helper->CurrentLayer(), mark)); } // -------------------------------------------------------------------- void BoundingBox(IpePage *page, IpeletHelper *helper) { IpeRect box; for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) { if (it->Select()) box.AddRect(it->BBox()); } IpePath *obj = new IpePath(helper->Attributes(), box); page->push_back(IpePgObject(IpePgObject::ESecondary, helper->CurrentLayer(), obj)); } void PreciseTransform(IpePage *page, IpeletHelper *helper, int mode) { if (!page->HasSelection()) { helper->Message("Nothing selected"); return; } // check whether any object is pinned for (IpePage::iterator it = page->begin(); it != page->end(); ++it) { if (it->Select() && it->Object()->pinned()) { helper->Message("Cannot transform pinned objects"); return; } } IpeString str; if (mode > 4 && !helper->GetString((mode == 6) ? "Enter stretch factors" : "Enter angle in degrees", str)) return; IpeMatrix tfm; switch (mode) { case 0: // Mirror horizontal tfm = IpeLinear(-1, 0, 0, 1); break; case 1: // Mirror vertical tfm = IpeLinear(1, 0, 0, -1); break; case 2: // Turn 90 degrees tfm = IpeLinear(0, -1, 1, 0); break; case 3: // Turn 180 degrees tfm = IpeLinear(-1, 0, 0, -1); break; case 4: // Turn 270 degrees tfm = IpeLinear(0, 1, -1, 0); break; case 5: // Precise rotate tfm = IpeLinear(IpeAngle::Degrees(IpeLex(str).GetDouble())); break; case 6: { // Precise stretch IpeLex lex(str); double sx, sy; lex >> sx >> sy; tfm = IpeLinear(sx, 0, 0, sy); } break; } IpeVector origin; const IpeSnapData &sd = helper->SnapData(); if (sd.iWithAxes) { origin = sd.iOrigin; } else { IpeRect box; for (IpePage::const_iterator it = page->begin(); it != page->end(); ++it) { if (it->Select()) box.AddRect(it->BBox()); } origin = 0.5 * (box.Min() + box.Max()); } tfm = IpeMatrix(origin) * tfm * IpeMatrix(-origin); for (IpePage::iterator it = page->begin(); it != page->end(); ++it) { if (it->Select()) it->Transform(tfm); } } const double dpmm = 72.0 / 25.4; void PreciseBox(IpePage *page, IpeletHelper *helper) { IpeString str; if (!helper->GetString("Enter size in mm (width height)", str)) return; IpeLex lex(str); double sx, sy; lex >> sx >> sy; IpeRect box(IpeVector::Zero, IpeVector(sx * dpmm, sy * dpmm)); IpePath *obj = new IpePath(helper->Attributes(), box); page->push_back(IpePgObject(IpePgObject::ESecondary, helper->CurrentLayer(), obj)); } void MediaBox(IpePage *page, IpeletHelper *helper) { IpePath *obj = new IpePath(helper->Attributes(), helper->Document()->layout().paper()); page->push_back(IpePgObject(IpePgObject::ESecondary, helper->CurrentLayer(), obj)); } // -------------------------------------------------------------------- class ParabolaVisitor : public IpeVisitor { public: virtual void VisitMark(const IpeMark *obj); public: std::vector iPos; }; void ParabolaVisitor::VisitMark(const IpeMark *obj) { iPos.push_back(obj->Matrix() * obj->Position()); } void Parabola(IpePage *page, IpeletHelper *helper) { IpePage::iterator it = page->PrimarySelection(); if (it == page->end()) { helper->Message("Nothing selected"); return; } const IpePath *p = it->Object()->AsPath(); if (!p || p->NumSubPaths() > 1 || p->SubPath(0)->Type() != IpeSubPath::ESegments || p->SubPath(0)->AsSegs()->NumSegments() > 1 || p->SubPath(0)->AsSegs()->Segment(0).Type() != IpePathSegment::ESegment) { helper->Message("Primary selection is not a line segment"); return; } ParabolaVisitor visitor; for (IpePage::iterator it = page->begin(); it != page->end(); ++it) { if (it->Select() == IpePgObject::ESecondary) { visitor(*it); } } IpePathSegment ps = p->SubPath(0)->AsSegs()->Segment(0); IpeVector p0 = p->Matrix() * ps.CP(0); IpeVector p1 = p->Matrix() * ps.CP(1); // tfm maps the positive x-axis to p0..p1 IpeMatrix tfm = IpeMatrix(p0) * IpeLinear((p1 - p0).Angle()); IpeMatrix inv = tfm.Inverse(); // treat x-interval from 0 to xmax double xmax = (p1 - p0).Len(); for (uint i = 0; i < visitor.iPos.size(); ++i) { IpeVector mrk = inv * visitor.iPos[i]; double a = -mrk.iX; double b = xmax - mrk.iX; // the following are the three control points for the unit parabola // between x = a and x = b IpeVector q0(a, a*a); IpeVector q1(0.5*(a + b), a*b); IpeVector q2(b, b*b); // create parabola IpeSegmentSubPath *sp = new IpeSegmentSubPath; sp->AppendQuad(q0, q1, q2); IpePath *obj = new IpePath(helper->Attributes()); obj->AddSubPath(sp); // now stretch in y-direction and move double stretch = 2.0 * mrk.iY; IpeVector offs(mrk.iX, mrk.iY / 2.0); IpeMatrix m = tfm * IpeMatrix(offs) * IpeLinear(1, 0, 0, 1.0/stretch); obj->SetMatrix(m); page->push_back(IpePgObject(IpePgObject::ESecondary, helper->CurrentLayer(), obj)); } } // -------------------------------------------------------------------- void GoodiesIpelet::Run(int function, IpePage *page, IpeletHelper *helper) { switch (function) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: PreciseTransform(page, helper, function); break; case 7: PreciseBox(page, helper); break; case 8: BoundingBox(page, helper); break; case 9: MediaBox(page, helper); break; case 10: MarkCircleCenter(page, helper); break; case 11: Parabola(page, helper); break; default: break; } } // -------------------------------------------------------------------- IPELET_DECLARE Ipelet *NewIpelet() { return new GoodiesIpelet; } // --------------------------------------------------------------------