home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / i / iv26_w_3.zip / EXAMPLES / IDRAW / SELECTIO.C < prev    next >
C/C++ Source or Header  |  1992-01-17  |  35KB  |  1,340 lines

  1. /*
  2.  * Copyright (c) 1987, 1988, 1989 Stanford University
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and its
  5.  * documentation for any purpose is hereby granted without fee, provided
  6.  * that the above copyright notice appear in all copies and that both that
  7.  * copyright notice and this permission notice appear in supporting
  8.  * documentation, and that the name of Stanford not be used in advertising or
  9.  * publicity pertaining to distribution of the software without specific,
  10.  * written prior permission.  Stanford makes no representations about
  11.  * the suitability of this software for any purpose.  It is provided "as is"
  12.  * without express or implied warranty.
  13.  *
  14.  * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  15.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  16.  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  17.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  18.  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  19.  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  20.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21.  */
  22.  
  23. /*
  24.  * $Header: selection.c,v 1.23 89/11/29 16:47:28 interran Exp $
  25.  * implements classes Selection and NPtSelection.
  26.  */
  27.  
  28. #include "ipaint.h"
  29. #include "istring.h"
  30. #include "listifont.h"
  31. #include "mapipaint.h"
  32. #include "selection.h"
  33. #include "state.h"
  34. #include <InterViews/rubcurve.h>
  35. #include <InterViews/transformer.h>
  36. #include <InterViews/Graphic/util.h>
  37. #include <iostream.h>
  38.  
  39. /*
  40.  * Define the start of data token.
  41.  */
  42.  
  43. const char* startdata = "%I";
  44. int Selection::versionnumber;
  45. char Selection::buf[BUFSIZE];
  46.  
  47. /*
  48.  * Selection starts off with no handles.
  49.  */
  50.  
  51. Selection::Selection (Graphic* gs) : (gs) {
  52.     handles = nil;
  53. }
  54.  
  55. /*
  56.  * Free storage allocated for the handles if any.
  57.  */
  58.  
  59. Selection::~Selection () {
  60.     DeleteHandles();
  61. }
  62.  
  63. /*
  64.  * Copy returns a copy of the Selection.
  65.  */
  66.  
  67. Graphic* Selection::Copy () {
  68.     return new Selection(this);
  69. }
  70.  
  71. /*
  72.  * HasChildren returns false so Idraw won't ungroup this Picture.
  73.  */
  74.  
  75. boolean Selection::HasChildren () {
  76.     return false;
  77. }
  78.  
  79. /*
  80.  * GetPaddedBox returns the Selection's smallest box with enough
  81.  * padding added to include its handles.
  82.  */
  83.  
  84. void Selection::GetPaddedBox (BoxObj& box) {
  85.     Picture::GetBox(box);
  86.     const int HDPAD = HDSIZE/2 + 1; /* how much to add to GetBox's size */
  87.     box.left -= HDPAD;
  88.     box.bottom -= HDPAD;
  89.     box.right += HDPAD;
  90.     box.top += HDPAD;
  91. }
  92.  
  93. /*
  94.  * DrawHandles tells the handles to draw themselves unless they've
  95.  * already drawn themselves.
  96.  */
  97.  
  98. void Selection::DrawHandles (Painter* rasterxor, Canvas* canvas) {
  99.     if (handles == nil) {
  100.     CreateHandles();
  101.     }
  102.     handles->SetPainter(rasterxor);
  103.     handles->SetCanvas(canvas);
  104.     handles->Draw();
  105. }
  106.  
  107. /*
  108.  * EraseHandles tells the handles to erase themselves unless they've
  109.  * already erased themselves.
  110.  */
  111.  
  112. void Selection::EraseHandles (Painter* rasterxor, Canvas* canvas) {
  113.     if (handles != nil) {
  114.     handles->SetPainter(rasterxor);
  115.     handles->SetCanvas(canvas);
  116.     handles->Erase();
  117.     }
  118. }
  119.  
  120. /*
  121.  * RedrawHandles knows for sure that no unerased handles remain on the
  122.  * screen, so it resets the handles to outline the Selection's
  123.  * possibly different shape and location before drawing the handles.
  124.  */
  125.  
  126. void Selection::RedrawHandles (Painter* rasterxor, Canvas* canvas) {
  127.     DeleteHandles();
  128.     DrawHandles(rasterxor, canvas);
  129. }
  130.  
  131. /*
  132.  * RedrawUnclippedHandles knows that some unerased handles probably
  133.  * remain on the screen so it can't reset the handles, but it can tell
  134.  * the handles to draw themselves whether or not they've already drawn
  135.  * themselves because the painter will clip the already drawn handles.
  136.  */
  137.  
  138. void Selection::RedrawUnclippedHandles (Painter* rasterxor, Canvas* canvas) {
  139.     if (handles == nil) {
  140.     CreateHandles();
  141.     }
  142.     handles->SetPainter(rasterxor);
  143.     handles->SetCanvas(canvas);
  144.     handles->Redraw();
  145. }
  146.  
  147. /*
  148.  * ResetHandles deletes the handles since the Selection may have moved
  149.  * out from under them.  Redrawing the handles will recreate them.
  150.  */
  151.  
  152. void Selection::ResetHandles () {
  153.     DeleteHandles();
  154. }
  155.  
  156. /*
  157.  * ShapedBy returns false since the Selection does not contain any
  158.  * points which both determine its shape and fall within the given
  159.  * distance of the given point.
  160.  */
  161.  
  162. boolean Selection::ShapedBy (Coord, Coord, float) {
  163.     return false;
  164. }
  165.  
  166. /*
  167.  * CreateShape creates and returns a Rubberband representing the
  168.  * Selection's shape for the user to reshape.
  169.  */
  170.  
  171. Rubberband* Selection::CreateShape (Coord, Coord) {
  172.     return nil;
  173. }
  174.  
  175. /*
  176.  * GetReshapedCopy creates and returns a copy of the Selection
  177.  * incorporating the change made to its shape.
  178.  */
  179.  
  180. Selection* Selection::GetReshapedCopy () {
  181.     return nil;
  182. }
  183.  
  184. /*
  185.  * Skip skips over tokens in the input stream until it reads a start
  186.  * of data token or reaches eof.
  187.  */
  188.  
  189. void Selection::Skip (istream& from) {
  190.     while (from >> buf && strcmp(buf, startdata) != 0) {
  191.     /* skip Postscript code */
  192.     }
  193. }
  194.  
  195. /*
  196.  * ReadVersion reads the drawing's version number.  Knowing the
  197.  * drawing's version number allows us to invoke backward compatibility
  198.  * code if necessary or detect an incompatibility ahead of time.
  199.  */
  200.  
  201. void Selection::ReadVersion (istream& from) {
  202.     Skip(from);
  203.     from >> buf;
  204.     if (strcmp(buf, "Idraw") == 0) {
  205.     from >> versionnumber;
  206.     } else {
  207.     versionnumber = ORIGINALVERSION;
  208.     }
  209.     if (versionnumber > TEXTOFFSETVERSION) {
  210.     fprintf(stderr, "warning: drawing version %d ", versionnumber);
  211.     fprintf(stderr, "newer than idraw version %d\n", TEXTOFFSETVERSION);
  212.     }
  213. }    
  214.  
  215. /*
  216.  * ReadGridSpacing reads the grid spacing used by the drawing and
  217.  * stores the new grid spacing value.  It must correct the default
  218.  * grid spacing it gives to old drawings for an implementation botch
  219.  * in InterViews 2.4 that calculated point's value incorrectly using
  220.  * 72.07/inch instead of inch/72.27 (it was a botch in TWO ways).
  221.  */
  222.  
  223. void Selection::ReadGridSpacing (istream& from, State* state) {
  224.     double g = state->GetGridSpacing();
  225.     if (versionnumber < GRIDSPACINGVERSION) {
  226.     const int oldspacing = 8;
  227.     const double oldpoints = 72.07/inches;
  228.     g = oldpoints * round(oldspacing * oldpoints);
  229.     } else {
  230.     from >> buf;
  231.     if (strcmp(buf, "Grid") == 0) {
  232.         from >> g;
  233.     }
  234.     }
  235.     state->SetGridSpacing(g);
  236. }
  237.  
  238. /*
  239.  * ReadGS reads data to initialize the graphic state for Selections
  240.  * which don't contain any text.
  241.  */
  242.  
  243. void Selection::ReadGS (istream& from, State* state) {
  244.     ReadBrush(from, state);
  245.     if (versionnumber >= FGANDBGCOLORVERSION) {
  246.     ReadFgColor(from, state);
  247.     ReadBgColor(from, state);
  248.     SetFont(nil);
  249.     } else if (versionnumber >= FGCOLORVERSION) {
  250.     ReadFgColor(from, state);
  251.     IColor* bg = state->GetMapIBgColor()->GetInitial();
  252.     SetColors(GetFgColor(), bg);
  253.     SetFont(nil);
  254.     } else {
  255.     IColor* fg = state->GetMapIFgColor()->GetInitial();
  256.     IColor* bg = state->GetMapIBgColor()->GetInitial();
  257.     SetColors(fg, bg);
  258.     ReadFont(from, state);
  259.     }
  260.     ReadPattern(from, state);
  261.     ReadTransformer(from);
  262. }
  263.  
  264. /*
  265.  * ReadPictGS reads data to initialize the graphic state for
  266.  * PictSelections which may contain some text.
  267.  */
  268.  
  269. void Selection::ReadPictGS (istream& from, State* state) {
  270.     ReadBrush(from, state);
  271.     if (versionnumber >= FGANDBGCOLORVERSION) {
  272.     ReadFgColor(from, state);
  273.     ReadBgColor(from, state);
  274.     } else if (versionnumber >= FGCOLORVERSION) {
  275.     ReadFgColor(from, state);
  276.     SetColors(GetFgColor(), nil);
  277.     } else {
  278.     SetColors(nil, nil);
  279.     }
  280.     ReadFont(from, state);
  281.     ReadPattern(from, state);
  282.     ReadTransformer(from);
  283. }
  284.  
  285. /*
  286.  * ReadTextGS reads data to initialize the graphic state for
  287.  * TextSelections which don't need a brush or pattern.
  288.  */
  289.  
  290. void Selection::ReadTextGS (istream& from, State* state) {
  291.     if (versionnumber >= FGCOLORVERSION) {
  292.     SetBrush(nil);
  293.     ReadFgColor(from, state);
  294.     SetColors(GetFgColor(), nil);
  295.     } else {
  296.     ReadBrush(from, state);
  297.     SetColors(state->GetMapIFgColor()->GetInitial(), nil);
  298.     }
  299.     ReadFont(from, state);
  300.     if (versionnumber < NONREDUNDANTVERSION) {
  301.     ReadPattern(from, state);
  302.     IPattern* pattern = (IPattern*) GetPattern();
  303.     float graylevel = pattern->GetGrayLevel();
  304.     const char* c = "Black";
  305.     int r = 0, g = 0, b = 0;
  306.     if (graylevel != 0 && graylevel != -1) {
  307.         if (graylevel == 1) {
  308.         c = "White";
  309.         r = g = b = 65535;
  310.         } else {
  311.         c = "Gray";
  312.         r = g = b = 49152;
  313.         }
  314.     }
  315.     SetColors(state->GetMapIFgColor()->FindOrAppend(c, r, g, b), nil);
  316.     } else {
  317.     SetPattern(nil);
  318.     }
  319.     ReadTransformer(from);
  320.     if (versionnumber < TEXTOFFSETVERSION) {
  321.     IFont* f = (IFont*) GetFont();
  322.     float x0, y0, x1, y1;
  323.     transform(0.0, 0.0, x0, y0);
  324.     transform(0.0, float(f->GetLineHt() - f->Height() - 1), x1, y1);
  325.     float dx = x1 - x0;
  326.     float dy = y1 - y0;
  327.     Translate(dx, dy);
  328.     }
  329. }
  330.  
  331. /*
  332.  * ReadBrush reads data to set the Selection's brush.
  333.  */
  334.  
  335. void Selection::ReadBrush (istream& from, State* state) {
  336.     Skip(from);
  337.     from >> buf;
  338.     if (buf[0] == 'b') {
  339.     char lookahead = 'u';
  340.     boolean undefined = false;
  341.     boolean none = false;
  342.     int p = 0;
  343.     int w = 0;
  344.     int l = false;
  345.     int r = false;
  346.  
  347.     from >> lookahead;
  348.     from.putback(lookahead);
  349.     switch (lookahead) {
  350.     case 'u':
  351.         undefined = true;
  352.         break;
  353.     case 'n':
  354.         none = true;
  355.         break;
  356.     default:
  357.         from >> p >> w >> l >> r;
  358.         break;
  359.     }
  360.  
  361.     if (undefined || !from.good()) {
  362.         SetBrush(nil);
  363.     } else {
  364.         MapIBrush* mb = state->GetMapIBrush();
  365.         IBrush* brush = mb->FindOrAppend(none, p, w, l, r);
  366.         SetBrush(brush);
  367.     }
  368.     }
  369. }
  370.  
  371. /*
  372.  * ReadFgColor reads data to set the Selection's foreground color.
  373.  */
  374.  
  375. void Selection::ReadFgColor (istream& from, State* state) {
  376.     Skip(from);
  377.     from >> buf;
  378.     if (buf[0] == 'c' &&
  379.     (buf[1] == 'f' || versionnumber < FGANDBGCOLORVERSION)
  380.     ) {
  381.     char lookahead = 'u';
  382.     boolean undefined = false;
  383.     char name[100];
  384.     float fr = 0, fg = 0, fb = 0;
  385.  
  386.     from >> lookahead;
  387.     from.putback(lookahead);
  388.     if (lookahead == 'u') {
  389.         undefined = true;
  390.     } else {
  391.         from >> name;
  392.         if (versionnumber >= FGANDBGCOLORVERSION) {
  393.         from >> fr >> fg >> fb;
  394.         }
  395.     }
  396.  
  397.     if (undefined || !from.good()) {
  398.         SetColors(nil, GetBgColor());
  399.     } else {
  400.         int r = round(fr * 0xffff);
  401.         int g = round(fg * 0xffff);
  402.         int b = round(fb * 0xffff);
  403.         MapIColor* mfg = state->GetMapIFgColor();
  404.         IColor* fgcolor = mfg->FindOrAppend(name, r, g, b);
  405.         SetColors(fgcolor, GetBgColor());
  406.     }
  407.     }
  408. }
  409.  
  410. /*
  411.  * ReadBgColor reads data to set the Selection's background color.
  412.  */
  413.  
  414. void Selection::ReadBgColor (istream& from, State* state) {
  415.     Skip(from);
  416.     from >> buf;
  417.     if (buf[0] == 'c' && buf[1] == 'b') {
  418.     char lookahead = 'u';
  419.     boolean undefined = false;
  420.     char name[100];
  421.     float fr = 0, fg = 0, fb = 0;
  422.  
  423.     from >> lookahead;
  424.     from.putback(lookahead);
  425.     if (lookahead == 'u') {
  426.         undefined = true;
  427.     } else {
  428.         from >> name >> fr >> fg >> fb;
  429.     }
  430.  
  431.     if (undefined || !from.good()) {
  432.         SetColors(GetFgColor(), nil);
  433.     } else {
  434.         int r = round(fr * 0xffff);
  435.         int g = round(fg * 0xffff);
  436.         int b = round(fb * 0xffff);
  437.         MapIColor* mbg = state->GetMapIBgColor();
  438.         IColor* bgcolor = mbg->FindOrAppend(name, r, g, b);
  439.         SetColors(GetFgColor(), bgcolor);
  440.     }
  441.     }
  442. }
  443.  
  444. /*
  445.  * ReadFont reads data to set the Selection's font.
  446.  */
  447.  
  448. void Selection::ReadFont (istream& from, State* state) {
  449.     Skip(from);
  450.     from >> buf;
  451.     if (buf[0] == 'f') {
  452.     char lookahead = 'u';
  453.     boolean undefined = false;
  454.     char name[100];
  455.     char printfont[100];
  456.     char printsize[100];
  457.  
  458.     from >> lookahead;
  459.     from.putback(lookahead);
  460.     if (lookahead == 'u') {
  461.         undefined = true;
  462.     } else {
  463.         from >> name;
  464.         from >> printfont;
  465.         from >> printsize;
  466.     }
  467.  
  468.     if (undefined || !from.good()) {
  469.         SetFont(nil);
  470.     } else {
  471.         MapIFont* mf = state->GetMapIFont();
  472.         char* pf = (versionnumber >= NONREDUNDANTVERSION) ?
  473.         &printfont[1] : printfont;
  474.         IFont* font = mf->FindOrAppend(name, pf, printsize);
  475.         SetFont(font);
  476.     }
  477.     }
  478. }
  479.  
  480. /*
  481.  * ReadPattern reads data to set the Selection's pattern.
  482.  */
  483.  
  484. void Selection::ReadPattern (istream& from, State* state) {
  485.     Skip(from);
  486.     from >> buf;
  487.     if (buf[0] == 'p') {
  488.     char lookahead = 'u';
  489.     boolean undefined = false;
  490.     boolean none = false;
  491.     float graylevel = 0;
  492.     int data[patternHeight];
  493.     int size = 0;
  494.  
  495.     from >> lookahead;
  496.     switch (lookahead) {
  497.     case 'u':
  498.         undefined = true;
  499.         break;
  500.     case 'n':
  501.         none = true;
  502.         break;
  503.     case '<':
  504.         graylevel = -1;
  505.         break;
  506.     default:
  507.         from.putback(lookahead);
  508.         break;
  509.     }
  510.  
  511.     if (!undefined && !none && graylevel != -1) {
  512.         if (versionnumber >= FGANDBGCOLORVERSION) {
  513.         from >> graylevel;
  514.         } else {
  515.         from >> data[0];
  516.         graylevel = CalcGrayLevel(data[0]);
  517.         }
  518.     } else if (graylevel == -1) {
  519.         for (int i = 0; from >> buf && i < patternHeight; i++) {
  520.         if (buf[0] == '>' || sscanf(buf, "%x", &data[i]) != 1) {
  521.             break;
  522.         }
  523.         }
  524.         if (buf[0] == '>') {
  525.         size = i;
  526.         } else {
  527.         undefined = true;
  528.         }
  529.     }
  530.  
  531.     if (undefined || !from.good()) {
  532.         SetPattern(nil);
  533.     } else {
  534.         MapIPattern* mp = state->GetMapIPattern();
  535.         IPattern* pattern = mp->FindOrAppend(none, graylevel, data, size);
  536.         SetPattern(pattern);
  537.     }
  538.     }
  539. }
  540.  
  541. /*
  542.  * ReadTransformer reads data to set the Selection's transformation
  543.  * matrix.
  544.  */
  545.  
  546. void Selection::ReadTransformer (istream& from) {
  547.     Skip(from);
  548.     from >> buf;
  549.     if (buf[0] == 't') {
  550.     char uorbracket = 'u';
  551.     boolean undefined = false;
  552.     float a00, a01, a10, a11, a20, a21;
  553.  
  554.     from >> uorbracket;
  555.     if (uorbracket == 'u') {
  556.         undefined = true;
  557.     } else {
  558.         if (versionnumber < NONREDUNDANTVERSION) {
  559.         from.putback(uorbracket);
  560.         }
  561.         from >> a00 >> a01 >> a10 >> a11 >> a20 >> a21;
  562.     }
  563.  
  564.     if (from.good() && !undefined) {
  565.         SetTransformer(new Transformer(a00, a01, a10, a11, a20, a21));
  566.     }
  567.     }
  568. }
  569.  
  570. /*
  571.  * CalcGrayLevel calculates a 4x4 bitmap's gray level on the printer.
  572.  * Since the gray level ranges from 0 = solid to 1 = clear,
  573.  * CalcGrayLevel counts the number of 0 bits in the bitmap and divides
  574.  * the sum by the total number of bits in the bitmap.
  575.  */
  576.  
  577. float Selection::CalcGrayLevel (int seed) {
  578.     const int numbits = 16;
  579.     int numzeros = 0;
  580.     for (int i = 0; i < numbits; i++) {
  581.     numzeros += !((seed >> i) & 0x1);
  582.     }
  583.     return float(numzeros) / numbits;
  584. }
  585.  
  586. /*
  587.  * WriteData writes everything needed to draw or reconstruct the
  588.  * Selection.
  589.  */
  590.  
  591. void Selection::WriteData (ostream& to) {
  592.     /* define it in your subclass */
  593. }
  594.  
  595. /*
  596.  * WriteVersion writes the drawing's version number.  Storing a
  597.  * version number with the drawing makes backward compatibility easier
  598.  * to support when the drawing format changes in the future.
  599.  */
  600.  
  601. void Selection::WriteVersion (ostream& to) {
  602.     to << startdata << " Idraw " << TEXTOFFSETVERSION << " ";
  603. }    
  604.  
  605. /*
  606.  * WriteGridSpacing writes the drawing's grid spacing.  Storing the
  607.  * grid spacing with the drawing ensures graphics will remain aligned
  608.  * to the grid they were aligned to before.
  609.  */
  610.  
  611. void Selection::WriteGridSpacing (ostream& to, State* state) {
  612.     to << "Grid " << state->GetGridSpacing() << " ";
  613. }    
  614.  
  615. /*
  616.  * WriteGS writes the graphic state for Selections that don't contain
  617.  * any text.
  618.  */
  619.  
  620. void Selection::WriteGS (ostream& to) {
  621.     WriteBrush(to);
  622.     WriteFgColor(to);
  623.     WriteBgColor(to);
  624.     WritePattern(to);
  625.     WriteTransformer(to);
  626. }
  627.  
  628. /*
  629.  * WritePictGS writes the graphic state for PictSelections which may
  630.  * contain some text.
  631.  */
  632.  
  633. void Selection::WritePictGS (ostream& to) {
  634.     WriteBrush(to);
  635.     WriteFgColor(to);
  636.     WriteBgColor(to);
  637.     WriteFont(to);
  638.     WritePattern(to);
  639.     WriteTransformer(to);
  640. }
  641.  
  642. /*
  643.  * WriteTextGS writes the graphic state for TextSelections which
  644.  * don't need a brush or pattern but do need a font.
  645.  */
  646.  
  647. void Selection::WriteTextGS (ostream& to) {
  648.     WriteFgColor(to);
  649.     WriteFont(to);
  650.     WriteTransformer(to);
  651. }
  652.  
  653. /*
  654.  * WriteBrush writes the Selection's brush.
  655.  */
  656.  
  657. void Selection::WriteBrush (ostream& to) {
  658.     IBrush* brush = (IBrush*) GetBrush();
  659.     if (brush == nil) {
  660.     to << startdata << " b u\n";
  661.     } else if (brush->None()) {
  662.     to << "none SetB " << startdata << " b n\n";
  663.     } else {
  664.     int p = brush->GetLinePattern();
  665.     to << startdata << " b " << p << "\n";
  666.     int w = brush->Width();
  667.     boolean l = brush->LeftArrow();
  668.     boolean r = brush->RightArrow();
  669.     to << w << " " << l << " " << r << " ";
  670.     const int* dashpat = brush->GetDashPattern();
  671.     int dashpatsize = brush->GetDashPatternSize();
  672.     int dashoffset = brush->GetDashOffset();
  673.     if (dashpatsize <= 0) {
  674.         to << "[] " << dashoffset << " ";
  675.     } else {
  676.         to << "[";
  677.         for (int i = 0; i < dashpatsize - 1; i++) {
  678.         to << dashpat[i] << " ";
  679.         }
  680.         to << dashpat[i] << "] " << dashoffset << " ";
  681.     }
  682.     to << "SetB\n";
  683.     }
  684. }
  685.  
  686. /*
  687.  * WriteFgColor writes the Selection's foreground color.
  688.  */
  689.  
  690. void Selection::WriteFgColor (ostream& to) {
  691.     IColor* fgcolor = (IColor*) GetFgColor();
  692.     if (fgcolor == nil) {
  693.     to << startdata << " cfg u\n";
  694.     } else {
  695.     const char* name = fgcolor->GetName();
  696.     to << startdata << " cfg " << name << "\n";
  697.     if (strcmp(name, "white") == 0 || strcmp(name, "White") == 0) {
  698.         to << "1 1 1 SetCFg\n";
  699.     } else {
  700.         int r, g, b;
  701.         fgcolor->Intensities(r, g, b);
  702.         float fr = float(r) / 0xffff;
  703.         float fg = float(g) / 0xffff;
  704.         float fb = float(b) / 0xffff; 
  705.         to << fr << " " << fg << " " << fb << " SetCFg\n";
  706.     }
  707.     }
  708. }
  709.  
  710. /*
  711.  * WriteBgColor writes the Selection's background color.
  712.  */
  713.  
  714. void Selection::WriteBgColor (ostream& to) {
  715.     IColor* bgcolor = (IColor*) GetBgColor();
  716.     if (bgcolor == nil) {
  717.     to << startdata << " cbg u\n";
  718.     } else {
  719.     const char* name = bgcolor->GetName();
  720.     to << startdata << " cbg " << name << "\n";
  721.     if (strcmp(name, "white") == 0 || strcmp(name, "White") == 0) {
  722.         to << "1 1 1 SetCBg\n";
  723.     } else {
  724.         int r, g, b;
  725.         bgcolor->Intensities(r, g, b);
  726.         float fr = float(r) / 0xffff;
  727.         float fg = float(g) / 0xffff;
  728.         float fb = float(b) / 0xffff; 
  729.         to << fr << " " << fg << " " << fb << " SetCBg\n";
  730.     }
  731.     }
  732. }
  733.  
  734. /*
  735.  * WriteFont writes the Selection's font.
  736.  */
  737.  
  738. void Selection::WriteFont (ostream& to) {
  739.     IFont* font = (IFont*) GetFont();
  740.     if (font == nil) {
  741.     to << startdata << " f u\n";
  742.     } else {
  743.     const char* name = font->GetName();
  744.     const char* pf = font->GetPrintFont();
  745.     const char* ps = font->GetPrintSize();
  746.     to << startdata << " f " << name << "\n";
  747.     to << "/" << pf << " " << ps << " SetF\n";
  748.     }
  749. }
  750.  
  751. /*
  752.  * WritePattern writes the Selection's pattern.
  753.  */
  754.  
  755. void Selection::WritePattern (ostream& to) {
  756.     IPattern* pattern = (IPattern*) GetPattern();
  757.     if (pattern == nil) {
  758.     to << startdata << " p u\n";
  759.     } else if (pattern->None()) {
  760.     to << "none SetP " << startdata << " p n\n";
  761.     } else if (pattern->GetSize() > 0) {
  762.     const int* data = pattern->GetData();
  763.     int size = pattern->GetSize();
  764.     to << startdata << " p\n";
  765.     to << "< ";
  766.     if (size <= 8) {
  767.         for (int i = 0; i < 8; i++) {
  768.         sprintf(buf, "%02x", data[i] & 0xff);
  769.         to << buf << " ";
  770.         }
  771.     } else {
  772.         for (int i = 0; i < patternHeight; i++) {
  773.         sprintf(buf, "%0*x", patternWidth/4, data[i]);
  774.         if (i != patternHeight - 2) {
  775.             to << buf << " ";
  776.         } else {
  777.             to << buf << "\n  ";
  778.         }
  779.         }
  780.     }
  781.     to << "> -1 SetP\n";
  782.     } else {
  783.     float graylevel = pattern->GetGrayLevel();
  784.     to << startdata << " p\n";
  785.     to << graylevel << " SetP\n";
  786.     }
  787. }
  788.  
  789. /*
  790.  * WriteTransformer writes the Selection's transformation matrix.
  791.  */
  792.  
  793. void Selection::WriteTransformer (ostream& to) {
  794.     Transformer* t = GetTransformer();
  795.     if (t == nil || *t == *identity) {
  796.     to << startdata << " t u\n";
  797.     } else {
  798.     float mat[6];
  799.     t->GetEntries(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
  800.     to << startdata << " t\n[ ";
  801.     for (int i = 0; i < 6; i++) {
  802.         to << (fabs(mat[i]) < 0.0001 ? 0.0 : mat[i]) << " ";
  803.     }
  804.     to << "] concat\n";
  805.     }
  806. }
  807.  
  808. /*
  809.  * CreateHandles creates handles outlining the Selection's shape.
  810.  */
  811.  
  812. void Selection::CreateHandles () {
  813.     const int N = 8;
  814.     const int RUBPT = 0;
  815.     Coord l, b, r, t, hx, hy, x[N], y[N];
  816.  
  817.     Picture::GetBox(l, b, r, t);
  818.     hx = (r + l)/2;
  819.     hy = (t + b)/2;
  820.     x[0] = l;    y[0] = b;
  821.     x[1] = hx;    y[1] = b;
  822.     x[2] = r;    y[2] = b;
  823.     x[3] = r;    y[3] = hy;
  824.     x[4] = r;    y[4] = t;
  825.     x[5] = hx;    y[5] = t;
  826.     x[6] = l;    y[6] = t;
  827.     x[7] = l;    y[7] = hy;
  828.  
  829.     handles = new RubberHandles(nil, nil, x, y, N, RUBPT, HDSIZE);
  830. }
  831.  
  832. /*
  833.  * DeleteHandles zeroes the handles to record it has none now.
  834.  */
  835.  
  836. void Selection::DeleteHandles () {
  837.     if (handles != nil) {
  838.     delete handles;
  839.     handles = nil;
  840.     }
  841. }
  842.  
  843. /*
  844.  * NPtSelection passes its argument to Selection.
  845.  */
  846.  
  847. NPtSelection::NPtSelection (Graphic* gs) : (gs) {
  848.     myname = "YouForgotToDefineMyName";
  849.     rubbervertex = nil;
  850. }
  851.  
  852. /*
  853.  * GetOriginal returns the points that were passed to the
  854.  * the NPtSelection subclass's constructor.
  855.  */
  856.  
  857. int NPtSelection::GetOriginal (const Coord*&, const Coord*&) {
  858.     return 0;
  859. }
  860.  
  861. /*
  862.  * ShapedBy returns true if any of the NPtSelection's points falls
  863.  * within the given distance of the given point.
  864.  */
  865.  
  866. boolean NPtSelection::ShapedBy (Coord px, Coord py, float maxdist) {
  867.     const Coord* ux;
  868.     const Coord* uy;
  869.     int n = GetOriginal(ux, uy);
  870.     Coord* x = new Coord[n];
  871.     Coord* y = new Coord[n];
  872.     CopyArray(ux, uy, n, x, y);
  873.     TotalTransform(x, y, n);
  874.     int closestpt = ClosestPoint(x, y, n, px, py);
  875.     boolean shapedby = Distance(x[closestpt], y[closestpt], px, py) <= maxdist;
  876.     delete x;
  877.     delete y;
  878.     return shapedby;
  879. }
  880.  
  881. /*
  882.  * CreateShape creates, stores, and returns a rubberband representing
  883.  * the NPtSelection's shape for the user to reshape.
  884.  */
  885.  
  886. Rubberband* NPtSelection::CreateShape (Coord px, Coord py) {
  887.     const Coord* ux;
  888.     const Coord* uy;
  889.     int n = GetOriginal(ux, uy);
  890.     Coord* x = new Coord[n];
  891.     Coord* y = new Coord[n];
  892.     CopyArray(ux, uy, n, x, y);
  893.     TotalTransform(x, y, n);
  894.     int rubpt = ClosestPoint(x, y, n, px, py);
  895.     rubbervertex = CreateRubberVertex(x, y, n, rubpt);
  896.     delete x;
  897.     delete y;
  898.     return rubbervertex;
  899. }
  900.  
  901. /*
  902.  * GetReshapedCopy creates and returns a copy of the NPtSelection
  903.  * incorporating the change made to its shape.
  904.  */
  905.  
  906. Selection* NPtSelection::GetReshapedCopy () {
  907.     Coord* x;
  908.     Coord* y;
  909.     int n;
  910.     int rubpt;
  911.     rubbervertex->GetCurrent(x, y, n, rubpt);
  912.     delete rubbervertex;
  913.  
  914.     InvTotalTransform(x, y, n);
  915.     Selection* reshaped = CreateReshapedCopy(x, y, n);
  916.     delete x;
  917.     delete y;
  918.     return reshaped;
  919. }
  920.  
  921. /*
  922.  * ReadPoints reads a set of points as efficiently as possible by
  923.  * using dynamic static buffers instead of mallocing on every call.
  924.  */
  925.  
  926. void NPtSelection::ReadPoints (istream& from, const Coord*& x, const Coord*& y,
  927. int& n) {
  928.     const int INITIALSIZE = 15;
  929.     static int sizepoints = 0;
  930.     static Coord* xcoords = nil;
  931.     static Coord* ycoords = nil;
  932.  
  933.     Skip(from);
  934.     from >> n;
  935.     if (n > sizepoints) {
  936.     delete xcoords;
  937.     delete ycoords;
  938.     sizepoints = max(n, INITIALSIZE);
  939.     xcoords = new Coord[sizepoints];
  940.     ycoords = new Coord[sizepoints];
  941.     }
  942.  
  943.     for (int i = 0; i < n; i++) {
  944.     if (versionnumber < NONREDUNDANTVERSION) {
  945.         Skip(from);
  946.     }
  947.     from >> xcoords[i] >> ycoords[i];
  948.     }
  949.  
  950.     x = xcoords;
  951.     y = ycoords;
  952. }
  953.  
  954. /*
  955.  * WriteData writes the NPtSelection's data and Postscript code to
  956.  * draw it.
  957.  */
  958.  
  959. void NPtSelection::WriteData (ostream& to) {
  960.     const Coord* x;
  961.     const Coord* y;
  962.     int n = GetOriginal(x, y);
  963.     to << "Begin " << startdata << " " << myname << "\n";
  964.     WriteGS(to);
  965.     to << startdata << " " << n << "\n";
  966.     for (int i = 0; i < n; i++) {
  967.     to << x[i] << " " << y[i] << "\n";
  968.     }
  969.     to << n << " " << myname << "\n";
  970.     to << "End\n\n";
  971. }
  972.  
  973. /*
  974.  * CreateRubberVertex creates and returns the right kind of
  975.  * RubberVertex to represent the NPtSelection's shape.
  976.  */
  977.  
  978. RubberVertex* NPtSelection::CreateRubberVertex (Coord*, Coord*, int, int) {
  979.     /* implement it in your subclass */
  980.     return nil;
  981. }
  982.  
  983. /*
  984.  * CreateReshapedCopy creates and returns a reshaped copy of itself
  985.  * using the passed points and its graphic state.
  986.  */
  987.  
  988. Selection* NPtSelection::CreateReshapedCopy (Coord*, Coord*, int) {
  989.     /* implement it in your subclass */
  990.     return nil;
  991. }
  992.  
  993. /*
  994.  * CreateHandles creates handles highlighting the NPtSelection's
  995.  * points.
  996.  */
  997.  
  998. void NPtSelection::CreateHandles () {
  999.     const int RUBPT = 0;
  1000.     const Coord* ux;
  1001.     const Coord* uy;
  1002.     int n = GetOriginal(ux, uy);
  1003.     Coord* x = new Coord[n];
  1004.     Coord* y = new Coord[n];
  1005.     CopyArray(ux, uy, n, x, y);
  1006.     TotalTransform(x, y, n);
  1007.     handles = new RubberHandles(nil, nil, x, y, n, RUBPT, HDSIZE);
  1008.     delete x;
  1009.     delete y;
  1010. }
  1011.  
  1012. /*
  1013.  * TotalTransform transforms the given points from the Selection's
  1014.  * coordinate system to the screen's coordinate system.
  1015.  */
  1016.  
  1017. void NPtSelection::TotalTransform (Coord* x, Coord* y, int n) {
  1018.     Transformer total;
  1019.  
  1020.     TotalTransformation(total);
  1021.     for (int i = 0; i < n; i++) {
  1022.     total.Transform(x[i], y[i]);
  1023.     }
  1024. }
  1025.  
  1026. /*
  1027.  * InvTotalTransform transforms the given points from the screen's
  1028.  * coordinate system to the NPtSelection's coordinate system.
  1029.  */
  1030.  
  1031. void NPtSelection::InvTotalTransform (Coord* x, Coord* y, int n) {
  1032.     Transformer total;
  1033.  
  1034.     TotalTransformation(total);
  1035.     for (int i = 0; i < n; i++) {
  1036.     total.InvTransform(x[i], y[i]);
  1037.     }
  1038. }
  1039.  
  1040. /*
  1041.  * ClosestPoint returns the index within the arrays of the closest
  1042.  * point to the given coordinates.
  1043.  */
  1044.  
  1045. int NPtSelection::ClosestPoint (Coord* x, Coord* y, int n, Coord px,
  1046. Coord py) {
  1047.     int closestpt = 0;
  1048.     float mindist = Distance(x[0], y[0], px, py);
  1049.     for (int i = 1; i < n; i++) {
  1050.     float dist = Distance(x[i], y[i], px, py);
  1051.     if (dist < mindist) {
  1052.         mindist = dist;
  1053.         closestpt = i;
  1054.     }
  1055.     }
  1056.     return closestpt;
  1057. }
  1058.  
  1059. /*
  1060.  * LeftAcont checks whether the left arrowhead contains the point.
  1061.  */
  1062.  
  1063. boolean NPtSelection::LeftAcont (Coord x0, Coord y0, Coord x1, Coord y1,
  1064. PointObj& po, Graphic* gs) {
  1065.     IBrush* brush = (IBrush*) gs->GetBrush();
  1066.     if (brush->LeftArrow()) {
  1067.     return ArrowHeadcont(x0, y0, x1, y1, po, gs);
  1068.     }
  1069.     return false;
  1070. }
  1071.  
  1072. /*
  1073.  * RightAcont checks whether the right arrowhead contains the point.
  1074.  */
  1075.  
  1076. boolean NPtSelection::RightAcont (Coord x0, Coord y0, Coord x1, Coord y1,
  1077. PointObj& po, Graphic* gs) {
  1078.     IBrush* brush = (IBrush*) gs->GetBrush();
  1079.     if (brush->RightArrow()) {
  1080.     return ArrowHeadcont(x0, y0, x1, y1, po, gs);
  1081.     }
  1082.     return false;
  1083. }
  1084.  
  1085. /*
  1086.  * LeftAints checks whether the left arrowhead intersects the area.
  1087.  */
  1088.  
  1089. boolean NPtSelection::LeftAints (Coord x0, Coord y0, Coord x1, Coord y1,
  1090. BoxObj& userb, Graphic* gs) {
  1091.     IBrush* brush = (IBrush*) gs->GetBrush();
  1092.     if (brush->LeftArrow()) {
  1093.     return ArrowHeadints(x0, y0, x1, y1, userb, gs);
  1094.     }
  1095.     return false;
  1096. }
  1097.  
  1098. /*
  1099.  * RightAints checks whether the right arrowhead intersects the area.
  1100.  */
  1101.  
  1102. boolean NPtSelection::RightAints (Coord x0, Coord y0, Coord x1, Coord y1,
  1103. BoxObj& userb, Graphic* gs) {
  1104.     IBrush* brush = (IBrush*) gs->GetBrush();
  1105.     if (brush->RightArrow()) {
  1106.     return ArrowHeadints(x0, y0, x1, y1, userb, gs);
  1107.     }
  1108.     return false;
  1109. }
  1110.  
  1111. /*
  1112.  * drawLeftA draws the left arrowhead if it should be drawn.
  1113.  */
  1114.  
  1115. void NPtSelection::drawLeftA (Coord x0, Coord y0, Coord x1, Coord y1,
  1116. Canvas* c, Graphic* gs) {
  1117.     IBrush* brush = (IBrush*) gs->GetBrush();
  1118.     if (brush->LeftArrow()) {
  1119.     drawArrowHead(x0, y0, x1, y1, c, gs);
  1120.     }
  1121. }
  1122.  
  1123. /*
  1124.  * drawRightA draws the right arrowhead if it should be drawn.
  1125.  */
  1126.  
  1127. void NPtSelection::drawRightA (Coord x0, Coord y0, Coord x1, Coord y1,
  1128. Canvas* c, Graphic* gs) {
  1129.     IBrush* brush = (IBrush*) gs->GetBrush();
  1130.     if (brush->RightArrow()) {
  1131.     drawArrowHead(x0, y0, x1, y1, c, gs);
  1132.     }
  1133. }
  1134.  
  1135. /*
  1136.  * Define an arrow head with its tip at the origin and its tail on the
  1137.  * negative x axis so we can rotate the tail about the origin to the
  1138.  * right angle and translate the tip to the right point.
  1139.  */
  1140.  
  1141. static const int ARROWN = 3;
  1142. static Coord arrowx[ARROWN] = {0, -ARROWHEIGHT,  -ARROWHEIGHT};
  1143. static Coord arrowy[ARROWN] = {0, ARROWWIDTH/2, -ARROWWIDTH/2};
  1144. static Coord arrowconvx[ARROWN + 1];
  1145. static Coord arrowconvy[ARROWN + 1];
  1146.  
  1147. /*
  1148.  * MergeArrowHeadTol returns a tolerance to use around the graphic's
  1149.  * extent that includes the arrowhead's size.
  1150.  */
  1151.  
  1152. float NPtSelection::MergeArrowHeadTol (float tol, Graphic* gs) {
  1153.     IBrush* brush = (IBrush*) gs->GetBrush();
  1154.     if (brush->LeftArrow() || brush->RightArrow()) {
  1155.     float magnif = 1;
  1156.     Transformer* view = getRoot()->GetTransformer();
  1157.     if (view != nil && !view->Rotated()) { /* rot breaks magnif calc */
  1158.         float fpx0, fpy0, fpx1, fpy1;
  1159.         view->Transform(0.0, 0.0, fpx0, fpy0);
  1160.         view->Transform(1.0, 1.0, fpx1, fpy1);
  1161.         magnif = fpy1 - fpy0;
  1162.     }
  1163.     float arrowtol = 0.5 * ARROWWIDTH * points * magnif;
  1164.     tol = max(arrowtol, tol);
  1165.     }
  1166.     return tol;
  1167. }
  1168.  
  1169. /*
  1170.  * ArrowHeadcont returns true if the arrowhead contains the point.
  1171.  * The arrowhead may be filled or unfilled depending on the pattern.
  1172.  */
  1173.  
  1174. boolean NPtSelection::ArrowHeadcont (Coord x0, Coord y0, Coord x1, Coord y1,
  1175. PointObj& po, Graphic* gs) {
  1176.     boolean contains = false;
  1177.     IPattern* pattern = (IPattern*) gs->GetPattern();
  1178.  
  1179.     SetCTM(x0, y0, x1, y1, gs, !pattern->None());
  1180.     PointObj pt(&po);
  1181.     invTransform(pt.x, pt.y, gs);
  1182.  
  1183.     if (pattern->None()) {
  1184.     MultiLineObj ml(arrowx, arrowy, ARROWN);
  1185.     LineObj l(arrowx[ARROWN-1], arrowy[ARROWN-1], arrowx[0], arrowy[0]);
  1186.     contains = ml.Contains(pt) || l.Contains(pt);
  1187.     } else {
  1188.     FillPolygonObj fp(arrowx, arrowy, ARROWN);
  1189.     contains = fp.Contains(pt);
  1190.     }
  1191.  
  1192.     RestoreCTM(gs);
  1193.     return contains;
  1194. }
  1195.  
  1196. /*
  1197.  * ArrowHeadints returns true if the arrowhead intersects the area.
  1198.  * The arrowhead may be filled or unfilled depending on the pattern.
  1199.  */
  1200.  
  1201. boolean NPtSelection::ArrowHeadints (Coord x0, Coord y0, Coord x1, Coord y1,
  1202. BoxObj& userb, Graphic* gs) {
  1203.     boolean intersects = false;
  1204.     IPattern* pattern = (IPattern*) gs->GetPattern();
  1205.  
  1206.     SetCTM(x0, y0, x1, y1, gs, !pattern->None());
  1207.     transformList(arrowx, arrowy, ARROWN, arrowconvx, arrowconvy, gs);
  1208.  
  1209.     if (pattern->None()) {
  1210.     arrowconvx[ARROWN] = arrowconvx[0];
  1211.     arrowconvy[ARROWN] = arrowconvy[0];
  1212.     MultiLineObj ml(arrowconvx, arrowconvy, ARROWN + 1);
  1213.     intersects = ml.Intersects(userb);
  1214.     } else {
  1215.     FillPolygonObj fp(arrowconvx, arrowconvy, ARROWN);
  1216.     intersects = fp.Intersects(userb);
  1217.     }
  1218.  
  1219.     RestoreCTM(gs);
  1220.     return intersects;
  1221. }
  1222.  
  1223. /*
  1224.  * drawArrowHead draws the arrowhead.
  1225.  */
  1226.  
  1227. void NPtSelection::drawArrowHead (Coord x0, Coord y0, Coord x1, Coord y1,
  1228. Canvas* c, Graphic* gs) {
  1229.     IPattern* pattern = (IPattern*) gs->GetPattern();
  1230.     if (!pattern->None()) {
  1231.     SetCTM(x0, y0, x1, y1, gs, true);
  1232.     update(gs);
  1233.     pFillPolygon(c, arrowx, arrowy, ARROWN);
  1234.     RestoreCTM(gs);
  1235.     }
  1236.  
  1237.     IBrush* brush = (IBrush*) gs->GetBrush();
  1238.     if (!brush->None()) {
  1239.     SetCTM(x0, y0, x1, y1, gs, false);
  1240.     update(gs);
  1241.     pPolygon(c, arrowx, arrowy, ARROWN);
  1242.     RestoreCTM(gs);
  1243.     }
  1244. }
  1245.  
  1246. /*
  1247.  * SetCTM stores gs's former transformation matrix and overwrites it
  1248.  * with a new one defined to scale the arrowhead to its proper size
  1249.  * and align it with the line.  The matrix includes the graphic's
  1250.  * topmost parent's scaling but no other parents' scaling so the
  1251.  * arrowhead will change size when we zoom the view but stay the same
  1252.  * size when we scale the line.  New argument patternfill special-
  1253.  * cases filling arrowhead to include the outermost edge of the
  1254.  * outline drawn by the brush so dashed brushes won't expose white
  1255.  * space where there's no pattern fill.
  1256.  */
  1257.  
  1258. static Transformer* origCTM;
  1259. static Transformer* arrowCTM;
  1260.  
  1261. void NPtSelection::SetCTM (Coord x0, Coord y0, Coord x1, Coord y1,
  1262. Graphic* gs, boolean patternfill) {
  1263.     if (arrowCTM == nil) {
  1264.     arrowCTM = new Transformer;
  1265.     arrowCTM->Reference();
  1266.     }
  1267.     *arrowCTM = *identity;
  1268.  
  1269.     if (patternfill) {
  1270.     IBrush* brush = (IBrush*) gs->GetBrush();
  1271.     float bw = brush->Width();
  1272.     float padtip = sqrt(ARROWHEIGHT*ARROWHEIGHT +
  1273.                 0.25*ARROWWIDTH*ARROWWIDTH) * bw / ARROWWIDTH;
  1274.     float padtail = bw / 2;
  1275.     float patternscale = (ARROWHEIGHT + padtip + padtail) / ARROWHEIGHT;
  1276.     arrowCTM->Scale(patternscale, patternscale);
  1277.     arrowCTM->Translate(padtip, 0.);
  1278.     }
  1279.  
  1280.     arrowCTM->Scale(point, point);
  1281.     Transformer* view = getRoot()->GetTransformer();
  1282.     if (view != nil && !view->Rotated()) { /* rot breaks magnif calc */
  1283.     float fpx0, fpy0, fpx1, fpy1;
  1284.     view->Transform(0.0, 0.0, fpx0, fpy0);
  1285.     view->Transform(1.0, 1.0, fpx1, fpy1);
  1286.     float arrowxmag = fpx1 - fpx0;
  1287.     float arrowymag = fpy1 - fpy0;
  1288.     if (arrowxmag == arrowymag) { /* won't scale arrows in BrushView */
  1289.         arrowCTM->Scale(arrowxmag, arrowymag);
  1290.     }
  1291.     }
  1292.  
  1293.     float tipx, tipy, tailx, taily;
  1294.     transform(float(x0), float(y0), tipx, tipy, gs);
  1295.     transform(float(x1), float(y1), tailx, taily, gs);
  1296.     float angle = Slope(tipx - tailx, tipy - taily);
  1297.     arrowCTM->Rotate(angle);
  1298.     arrowCTM->Translate(tipx, tipy);
  1299.  
  1300.     origCTM = gs->GetTransformer();
  1301.     if (origCTM != nil) {
  1302.     origCTM->Reference();
  1303.     }
  1304.     gs->SetTransformer(arrowCTM);
  1305. }
  1306.  
  1307. /*
  1308.  * RestoreCTM restores gs's original transformation matrix.
  1309.  */
  1310.  
  1311. void NPtSelection::RestoreCTM (Graphic* gs) {
  1312.     gs->SetTransformer(origCTM);
  1313.     Unref(origCTM);
  1314. }
  1315.  
  1316. /*
  1317.  * sign returns 1 if the number's nonnegative, -1 if it's negative.
  1318.  */
  1319.  
  1320. inline float sign (float num) {
  1321.     return (num >= 0.) ? 1. : -1.;
  1322. }
  1323.  
  1324. /*
  1325.  * Slope returns the number of degrees in the given slope.
  1326.  */
  1327.  
  1328. float NPtSelection::Slope (float dx, float dy) {
  1329.     float angle = 0.;
  1330.     if (dx == 0.) {
  1331.         angle = sign(dy) * 90.;
  1332.     } else {
  1333.     angle = degrees(atan(dy/dx));
  1334.     if (dx < 0.) {
  1335.         angle += sign(dy) * 180.;
  1336.     }
  1337.     }
  1338.     return angle;
  1339. }
  1340.