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 >
Wrap
C/C++ Source or Header
|
1992-01-17
|
35KB
|
1,340 lines
/*
* Copyright (c) 1987, 1988, 1989 Stanford University
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of Stanford not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. Stanford makes no representations about
* the suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
* IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* $Header: selection.c,v 1.23 89/11/29 16:47:28 interran Exp $
* implements classes Selection and NPtSelection.
*/
#include "ipaint.h"
#include "istring.h"
#include "listifont.h"
#include "mapipaint.h"
#include "selection.h"
#include "state.h"
#include <InterViews/rubcurve.h>
#include <InterViews/transformer.h>
#include <InterViews/Graphic/util.h>
#include <iostream.h>
/*
* Define the start of data token.
*/
const char* startdata = "%I";
int Selection::versionnumber;
char Selection::buf[BUFSIZE];
/*
* Selection starts off with no handles.
*/
Selection::Selection (Graphic* gs) : (gs) {
handles = nil;
}
/*
* Free storage allocated for the handles if any.
*/
Selection::~Selection () {
DeleteHandles();
}
/*
* Copy returns a copy of the Selection.
*/
Graphic* Selection::Copy () {
return new Selection(this);
}
/*
* HasChildren returns false so Idraw won't ungroup this Picture.
*/
boolean Selection::HasChildren () {
return false;
}
/*
* GetPaddedBox returns the Selection's smallest box with enough
* padding added to include its handles.
*/
void Selection::GetPaddedBox (BoxObj& box) {
Picture::GetBox(box);
const int HDPAD = HDSIZE/2 + 1; /* how much to add to GetBox's size */
box.left -= HDPAD;
box.bottom -= HDPAD;
box.right += HDPAD;
box.top += HDPAD;
}
/*
* DrawHandles tells the handles to draw themselves unless they've
* already drawn themselves.
*/
void Selection::DrawHandles (Painter* rasterxor, Canvas* canvas) {
if (handles == nil) {
CreateHandles();
}
handles->SetPainter(rasterxor);
handles->SetCanvas(canvas);
handles->Draw();
}
/*
* EraseHandles tells the handles to erase themselves unless they've
* already erased themselves.
*/
void Selection::EraseHandles (Painter* rasterxor, Canvas* canvas) {
if (handles != nil) {
handles->SetPainter(rasterxor);
handles->SetCanvas(canvas);
handles->Erase();
}
}
/*
* RedrawHandles knows for sure that no unerased handles remain on the
* screen, so it resets the handles to outline the Selection's
* possibly different shape and location before drawing the handles.
*/
void Selection::RedrawHandles (Painter* rasterxor, Canvas* canvas) {
DeleteHandles();
DrawHandles(rasterxor, canvas);
}
/*
* RedrawUnclippedHandles knows that some unerased handles probably
* remain on the screen so it can't reset the handles, but it can tell
* the handles to draw themselves whether or not they've already drawn
* themselves because the painter will clip the already drawn handles.
*/
void Selection::RedrawUnclippedHandles (Painter* rasterxor, Canvas* canvas) {
if (handles == nil) {
CreateHandles();
}
handles->SetPainter(rasterxor);
handles->SetCanvas(canvas);
handles->Redraw();
}
/*
* ResetHandles deletes the handles since the Selection may have moved
* out from under them. Redrawing the handles will recreate them.
*/
void Selection::ResetHandles () {
DeleteHandles();
}
/*
* ShapedBy returns false since the Selection does not contain any
* points which both determine its shape and fall within the given
* distance of the given point.
*/
boolean Selection::ShapedBy (Coord, Coord, float) {
return false;
}
/*
* CreateShape creates and returns a Rubberband representing the
* Selection's shape for the user to reshape.
*/
Rubberband* Selection::CreateShape (Coord, Coord) {
return nil;
}
/*
* GetReshapedCopy creates and returns a copy of the Selection
* incorporating the change made to its shape.
*/
Selection* Selection::GetReshapedCopy () {
return nil;
}
/*
* Skip skips over tokens in the input stream until it reads a start
* of data token or reaches eof.
*/
void Selection::Skip (istream& from) {
while (from >> buf && strcmp(buf, startdata) != 0) {
/* skip Postscript code */
}
}
/*
* ReadVersion reads the drawing's version number. Knowing the
* drawing's version number allows us to invoke backward compatibility
* code if necessary or detect an incompatibility ahead of time.
*/
void Selection::ReadVersion (istream& from) {
Skip(from);
from >> buf;
if (strcmp(buf, "Idraw") == 0) {
from >> versionnumber;
} else {
versionnumber = ORIGINALVERSION;
}
if (versionnumber > TEXTOFFSETVERSION) {
fprintf(stderr, "warning: drawing version %d ", versionnumber);
fprintf(stderr, "newer than idraw version %d\n", TEXTOFFSETVERSION);
}
}
/*
* ReadGridSpacing reads the grid spacing used by the drawing and
* stores the new grid spacing value. It must correct the default
* grid spacing it gives to old drawings for an implementation botch
* in InterViews 2.4 that calculated point's value incorrectly using
* 72.07/inch instead of inch/72.27 (it was a botch in TWO ways).
*/
void Selection::ReadGridSpacing (istream& from, State* state) {
double g = state->GetGridSpacing();
if (versionnumber < GRIDSPACINGVERSION) {
const int oldspacing = 8;
const double oldpoints = 72.07/inches;
g = oldpoints * round(oldspacing * oldpoints);
} else {
from >> buf;
if (strcmp(buf, "Grid") == 0) {
from >> g;
}
}
state->SetGridSpacing(g);
}
/*
* ReadGS reads data to initialize the graphic state for Selections
* which don't contain any text.
*/
void Selection::ReadGS (istream& from, State* state) {
ReadBrush(from, state);
if (versionnumber >= FGANDBGCOLORVERSION) {
ReadFgColor(from, state);
ReadBgColor(from, state);
SetFont(nil);
} else if (versionnumber >= FGCOLORVERSION) {
ReadFgColor(from, state);
IColor* bg = state->GetMapIBgColor()->GetInitial();
SetColors(GetFgColor(), bg);
SetFont(nil);
} else {
IColor* fg = state->GetMapIFgColor()->GetInitial();
IColor* bg = state->GetMapIBgColor()->GetInitial();
SetColors(fg, bg);
ReadFont(from, state);
}
ReadPattern(from, state);
ReadTransformer(from);
}
/*
* ReadPictGS reads data to initialize the graphic state for
* PictSelections which may contain some text.
*/
void Selection::ReadPictGS (istream& from, State* state) {
ReadBrush(from, state);
if (versionnumber >= FGANDBGCOLORVERSION) {
ReadFgColor(from, state);
ReadBgColor(from, state);
} else if (versionnumber >= FGCOLORVERSION) {
ReadFgColor(from, state);
SetColors(GetFgColor(), nil);
} else {
SetColors(nil, nil);
}
ReadFont(from, state);
ReadPattern(from, state);
ReadTransformer(from);
}
/*
* ReadTextGS reads data to initialize the graphic state for
* TextSelections which don't need a brush or pattern.
*/
void Selection::ReadTextGS (istream& from, State* state) {
if (versionnumber >= FGCOLORVERSION) {
SetBrush(nil);
ReadFgColor(from, state);
SetColors(GetFgColor(), nil);
} else {
ReadBrush(from, state);
SetColors(state->GetMapIFgColor()->GetInitial(), nil);
}
ReadFont(from, state);
if (versionnumber < NONREDUNDANTVERSION) {
ReadPattern(from, state);
IPattern* pattern = (IPattern*) GetPattern();
float graylevel = pattern->GetGrayLevel();
const char* c = "Black";
int r = 0, g = 0, b = 0;
if (graylevel != 0 && graylevel != -1) {
if (graylevel == 1) {
c = "White";
r = g = b = 65535;
} else {
c = "Gray";
r = g = b = 49152;
}
}
SetColors(state->GetMapIFgColor()->FindOrAppend(c, r, g, b), nil);
} else {
SetPattern(nil);
}
ReadTransformer(from);
if (versionnumber < TEXTOFFSETVERSION) {
IFont* f = (IFont*) GetFont();
float x0, y0, x1, y1;
transform(0.0, 0.0, x0, y0);
transform(0.0, float(f->GetLineHt() - f->Height() - 1), x1, y1);
float dx = x1 - x0;
float dy = y1 - y0;
Translate(dx, dy);
}
}
/*
* ReadBrush reads data to set the Selection's brush.
*/
void Selection::ReadBrush (istream& from, State* state) {
Skip(from);
from >> buf;
if (buf[0] == 'b') {
char lookahead = 'u';
boolean undefined = false;
boolean none = false;
int p = 0;
int w = 0;
int l = false;
int r = false;
from >> lookahead;
from.putback(lookahead);
switch (lookahead) {
case 'u':
undefined = true;
break;
case 'n':
none = true;
break;
default:
from >> p >> w >> l >> r;
break;
}
if (undefined || !from.good()) {
SetBrush(nil);
} else {
MapIBrush* mb = state->GetMapIBrush();
IBrush* brush = mb->FindOrAppend(none, p, w, l, r);
SetBrush(brush);
}
}
}
/*
* ReadFgColor reads data to set the Selection's foreground color.
*/
void Selection::ReadFgColor (istream& from, State* state) {
Skip(from);
from >> buf;
if (buf[0] == 'c' &&
(buf[1] == 'f' || versionnumber < FGANDBGCOLORVERSION)
) {
char lookahead = 'u';
boolean undefined = false;
char name[100];
float fr = 0, fg = 0, fb = 0;
from >> lookahead;
from.putback(lookahead);
if (lookahead == 'u') {
undefined = true;
} else {
from >> name;
if (versionnumber >= FGANDBGCOLORVERSION) {
from >> fr >> fg >> fb;
}
}
if (undefined || !from.good()) {
SetColors(nil, GetBgColor());
} else {
int r = round(fr * 0xffff);
int g = round(fg * 0xffff);
int b = round(fb * 0xffff);
MapIColor* mfg = state->GetMapIFgColor();
IColor* fgcolor = mfg->FindOrAppend(name, r, g, b);
SetColors(fgcolor, GetBgColor());
}
}
}
/*
* ReadBgColor reads data to set the Selection's background color.
*/
void Selection::ReadBgColor (istream& from, State* state) {
Skip(from);
from >> buf;
if (buf[0] == 'c' && buf[1] == 'b') {
char lookahead = 'u';
boolean undefined = false;
char name[100];
float fr = 0, fg = 0, fb = 0;
from >> lookahead;
from.putback(lookahead);
if (lookahead == 'u') {
undefined = true;
} else {
from >> name >> fr >> fg >> fb;
}
if (undefined || !from.good()) {
SetColors(GetFgColor(), nil);
} else {
int r = round(fr * 0xffff);
int g = round(fg * 0xffff);
int b = round(fb * 0xffff);
MapIColor* mbg = state->GetMapIBgColor();
IColor* bgcolor = mbg->FindOrAppend(name, r, g, b);
SetColors(GetFgColor(), bgcolor);
}
}
}
/*
* ReadFont reads data to set the Selection's font.
*/
void Selection::ReadFont (istream& from, State* state) {
Skip(from);
from >> buf;
if (buf[0] == 'f') {
char lookahead = 'u';
boolean undefined = false;
char name[100];
char printfont[100];
char printsize[100];
from >> lookahead;
from.putback(lookahead);
if (lookahead == 'u') {
undefined = true;
} else {
from >> name;
from >> printfont;
from >> printsize;
}
if (undefined || !from.good()) {
SetFont(nil);
} else {
MapIFont* mf = state->GetMapIFont();
char* pf = (versionnumber >= NONREDUNDANTVERSION) ?
&printfont[1] : printfont;
IFont* font = mf->FindOrAppend(name, pf, printsize);
SetFont(font);
}
}
}
/*
* ReadPattern reads data to set the Selection's pattern.
*/
void Selection::ReadPattern (istream& from, State* state) {
Skip(from);
from >> buf;
if (buf[0] == 'p') {
char lookahead = 'u';
boolean undefined = false;
boolean none = false;
float graylevel = 0;
int data[patternHeight];
int size = 0;
from >> lookahead;
switch (lookahead) {
case 'u':
undefined = true;
break;
case 'n':
none = true;
break;
case '<':
graylevel = -1;
break;
default:
from.putback(lookahead);
break;
}
if (!undefined && !none && graylevel != -1) {
if (versionnumber >= FGANDBGCOLORVERSION) {
from >> graylevel;
} else {
from >> data[0];
graylevel = CalcGrayLevel(data[0]);
}
} else if (graylevel == -1) {
for (int i = 0; from >> buf && i < patternHeight; i++) {
if (buf[0] == '>' || sscanf(buf, "%x", &data[i]) != 1) {
break;
}
}
if (buf[0] == '>') {
size = i;
} else {
undefined = true;
}
}
if (undefined || !from.good()) {
SetPattern(nil);
} else {
MapIPattern* mp = state->GetMapIPattern();
IPattern* pattern = mp->FindOrAppend(none, graylevel, data, size);
SetPattern(pattern);
}
}
}
/*
* ReadTransformer reads data to set the Selection's transformation
* matrix.
*/
void Selection::ReadTransformer (istream& from) {
Skip(from);
from >> buf;
if (buf[0] == 't') {
char uorbracket = 'u';
boolean undefined = false;
float a00, a01, a10, a11, a20, a21;
from >> uorbracket;
if (uorbracket == 'u') {
undefined = true;
} else {
if (versionnumber < NONREDUNDANTVERSION) {
from.putback(uorbracket);
}
from >> a00 >> a01 >> a10 >> a11 >> a20 >> a21;
}
if (from.good() && !undefined) {
SetTransformer(new Transformer(a00, a01, a10, a11, a20, a21));
}
}
}
/*
* CalcGrayLevel calculates a 4x4 bitmap's gray level on the printer.
* Since the gray level ranges from 0 = solid to 1 = clear,
* CalcGrayLevel counts the number of 0 bits in the bitmap and divides
* the sum by the total number of bits in the bitmap.
*/
float Selection::CalcGrayLevel (int seed) {
const int numbits = 16;
int numzeros = 0;
for (int i = 0; i < numbits; i++) {
numzeros += !((seed >> i) & 0x1);
}
return float(numzeros) / numbits;
}
/*
* WriteData writes everything needed to draw or reconstruct the
* Selection.
*/
void Selection::WriteData (ostream& to) {
/* define it in your subclass */
}
/*
* WriteVersion writes the drawing's version number. Storing a
* version number with the drawing makes backward compatibility easier
* to support when the drawing format changes in the future.
*/
void Selection::WriteVersion (ostream& to) {
to << startdata << " Idraw " << TEXTOFFSETVERSION << " ";
}
/*
* WriteGridSpacing writes the drawing's grid spacing. Storing the
* grid spacing with the drawing ensures graphics will remain aligned
* to the grid they were aligned to before.
*/
void Selection::WriteGridSpacing (ostream& to, State* state) {
to << "Grid " << state->GetGridSpacing() << " ";
}
/*
* WriteGS writes the graphic state for Selections that don't contain
* any text.
*/
void Selection::WriteGS (ostream& to) {
WriteBrush(to);
WriteFgColor(to);
WriteBgColor(to);
WritePattern(to);
WriteTransformer(to);
}
/*
* WritePictGS writes the graphic state for PictSelections which may
* contain some text.
*/
void Selection::WritePictGS (ostream& to) {
WriteBrush(to);
WriteFgColor(to);
WriteBgColor(to);
WriteFont(to);
WritePattern(to);
WriteTransformer(to);
}
/*
* WriteTextGS writes the graphic state for TextSelections which
* don't need a brush or pattern but do need a font.
*/
void Selection::WriteTextGS (ostream& to) {
WriteFgColor(to);
WriteFont(to);
WriteTransformer(to);
}
/*
* WriteBrush writes the Selection's brush.
*/
void Selection::WriteBrush (ostream& to) {
IBrush* brush = (IBrush*) GetBrush();
if (brush == nil) {
to << startdata << " b u\n";
} else if (brush->None()) {
to << "none SetB " << startdata << " b n\n";
} else {
int p = brush->GetLinePattern();
to << startdata << " b " << p << "\n";
int w = brush->Width();
boolean l = brush->LeftArrow();
boolean r = brush->RightArrow();
to << w << " " << l << " " << r << " ";
const int* dashpat = brush->GetDashPattern();
int dashpatsize = brush->GetDashPatternSize();
int dashoffset = brush->GetDashOffset();
if (dashpatsize <= 0) {
to << "[] " << dashoffset << " ";
} else {
to << "[";
for (int i = 0; i < dashpatsize - 1; i++) {
to << dashpat[i] << " ";
}
to << dashpat[i] << "] " << dashoffset << " ";
}
to << "SetB\n";
}
}
/*
* WriteFgColor writes the Selection's foreground color.
*/
void Selection::WriteFgColor (ostream& to) {
IColor* fgcolor = (IColor*) GetFgColor();
if (fgcolor == nil) {
to << startdata << " cfg u\n";
} else {
const char* name = fgcolor->GetName();
to << startdata << " cfg " << name << "\n";
if (strcmp(name, "white") == 0 || strcmp(name, "White") == 0) {
to << "1 1 1 SetCFg\n";
} else {
int r, g, b;
fgcolor->Intensities(r, g, b);
float fr = float(r) / 0xffff;
float fg = float(g) / 0xffff;
float fb = float(b) / 0xffff;
to << fr << " " << fg << " " << fb << " SetCFg\n";
}
}
}
/*
* WriteBgColor writes the Selection's background color.
*/
void Selection::WriteBgColor (ostream& to) {
IColor* bgcolor = (IColor*) GetBgColor();
if (bgcolor == nil) {
to << startdata << " cbg u\n";
} else {
const char* name = bgcolor->GetName();
to << startdata << " cbg " << name << "\n";
if (strcmp(name, "white") == 0 || strcmp(name, "White") == 0) {
to << "1 1 1 SetCBg\n";
} else {
int r, g, b;
bgcolor->Intensities(r, g, b);
float fr = float(r) / 0xffff;
float fg = float(g) / 0xffff;
float fb = float(b) / 0xffff;
to << fr << " " << fg << " " << fb << " SetCBg\n";
}
}
}
/*
* WriteFont writes the Selection's font.
*/
void Selection::WriteFont (ostream& to) {
IFont* font = (IFont*) GetFont();
if (font == nil) {
to << startdata << " f u\n";
} else {
const char* name = font->GetName();
const char* pf = font->GetPrintFont();
const char* ps = font->GetPrintSize();
to << startdata << " f " << name << "\n";
to << "/" << pf << " " << ps << " SetF\n";
}
}
/*
* WritePattern writes the Selection's pattern.
*/
void Selection::WritePattern (ostream& to) {
IPattern* pattern = (IPattern*) GetPattern();
if (pattern == nil) {
to << startdata << " p u\n";
} else if (pattern->None()) {
to << "none SetP " << startdata << " p n\n";
} else if (pattern->GetSize() > 0) {
const int* data = pattern->GetData();
int size = pattern->GetSize();
to << startdata << " p\n";
to << "< ";
if (size <= 8) {
for (int i = 0; i < 8; i++) {
sprintf(buf, "%02x", data[i] & 0xff);
to << buf << " ";
}
} else {
for (int i = 0; i < patternHeight; i++) {
sprintf(buf, "%0*x", patternWidth/4, data[i]);
if (i != patternHeight - 2) {
to << buf << " ";
} else {
to << buf << "\n ";
}
}
}
to << "> -1 SetP\n";
} else {
float graylevel = pattern->GetGrayLevel();
to << startdata << " p\n";
to << graylevel << " SetP\n";
}
}
/*
* WriteTransformer writes the Selection's transformation matrix.
*/
void Selection::WriteTransformer (ostream& to) {
Transformer* t = GetTransformer();
if (t == nil || *t == *identity) {
to << startdata << " t u\n";
} else {
float mat[6];
t->GetEntries(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
to << startdata << " t\n[ ";
for (int i = 0; i < 6; i++) {
to << (fabs(mat[i]) < 0.0001 ? 0.0 : mat[i]) << " ";
}
to << "] concat\n";
}
}
/*
* CreateHandles creates handles outlining the Selection's shape.
*/
void Selection::CreateHandles () {
const int N = 8;
const int RUBPT = 0;
Coord l, b, r, t, hx, hy, x[N], y[N];
Picture::GetBox(l, b, r, t);
hx = (r + l)/2;
hy = (t + b)/2;
x[0] = l; y[0] = b;
x[1] = hx; y[1] = b;
x[2] = r; y[2] = b;
x[3] = r; y[3] = hy;
x[4] = r; y[4] = t;
x[5] = hx; y[5] = t;
x[6] = l; y[6] = t;
x[7] = l; y[7] = hy;
handles = new RubberHandles(nil, nil, x, y, N, RUBPT, HDSIZE);
}
/*
* DeleteHandles zeroes the handles to record it has none now.
*/
void Selection::DeleteHandles () {
if (handles != nil) {
delete handles;
handles = nil;
}
}
/*
* NPtSelection passes its argument to Selection.
*/
NPtSelection::NPtSelection (Graphic* gs) : (gs) {
myname = "YouForgotToDefineMyName";
rubbervertex = nil;
}
/*
* GetOriginal returns the points that were passed to the
* the NPtSelection subclass's constructor.
*/
int NPtSelection::GetOriginal (const Coord*&, const Coord*&) {
return 0;
}
/*
* ShapedBy returns true if any of the NPtSelection's points falls
* within the given distance of the given point.
*/
boolean NPtSelection::ShapedBy (Coord px, Coord py, float maxdist) {
const Coord* ux;
const Coord* uy;
int n = GetOriginal(ux, uy);
Coord* x = new Coord[n];
Coord* y = new Coord[n];
CopyArray(ux, uy, n, x, y);
TotalTransform(x, y, n);
int closestpt = ClosestPoint(x, y, n, px, py);
boolean shapedby = Distance(x[closestpt], y[closestpt], px, py) <= maxdist;
delete x;
delete y;
return shapedby;
}
/*
* CreateShape creates, stores, and returns a rubberband representing
* the NPtSelection's shape for the user to reshape.
*/
Rubberband* NPtSelection::CreateShape (Coord px, Coord py) {
const Coord* ux;
const Coord* uy;
int n = GetOriginal(ux, uy);
Coord* x = new Coord[n];
Coord* y = new Coord[n];
CopyArray(ux, uy, n, x, y);
TotalTransform(x, y, n);
int rubpt = ClosestPoint(x, y, n, px, py);
rubbervertex = CreateRubberVertex(x, y, n, rubpt);
delete x;
delete y;
return rubbervertex;
}
/*
* GetReshapedCopy creates and returns a copy of the NPtSelection
* incorporating the change made to its shape.
*/
Selection* NPtSelection::GetReshapedCopy () {
Coord* x;
Coord* y;
int n;
int rubpt;
rubbervertex->GetCurrent(x, y, n, rubpt);
delete rubbervertex;
InvTotalTransform(x, y, n);
Selection* reshaped = CreateReshapedCopy(x, y, n);
delete x;
delete y;
return reshaped;
}
/*
* ReadPoints reads a set of points as efficiently as possible by
* using dynamic static buffers instead of mallocing on every call.
*/
void NPtSelection::ReadPoints (istream& from, const Coord*& x, const Coord*& y,
int& n) {
const int INITIALSIZE = 15;
static int sizepoints = 0;
static Coord* xcoords = nil;
static Coord* ycoords = nil;
Skip(from);
from >> n;
if (n > sizepoints) {
delete xcoords;
delete ycoords;
sizepoints = max(n, INITIALSIZE);
xcoords = new Coord[sizepoints];
ycoords = new Coord[sizepoints];
}
for (int i = 0; i < n; i++) {
if (versionnumber < NONREDUNDANTVERSION) {
Skip(from);
}
from >> xcoords[i] >> ycoords[i];
}
x = xcoords;
y = ycoords;
}
/*
* WriteData writes the NPtSelection's data and Postscript code to
* draw it.
*/
void NPtSelection::WriteData (ostream& to) {
const Coord* x;
const Coord* y;
int n = GetOriginal(x, y);
to << "Begin " << startdata << " " << myname << "\n";
WriteGS(to);
to << startdata << " " << n << "\n";
for (int i = 0; i < n; i++) {
to << x[i] << " " << y[i] << "\n";
}
to << n << " " << myname << "\n";
to << "End\n\n";
}
/*
* CreateRubberVertex creates and returns the right kind of
* RubberVertex to represent the NPtSelection's shape.
*/
RubberVertex* NPtSelection::CreateRubberVertex (Coord*, Coord*, int, int) {
/* implement it in your subclass */
return nil;
}
/*
* CreateReshapedCopy creates and returns a reshaped copy of itself
* using the passed points and its graphic state.
*/
Selection* NPtSelection::CreateReshapedCopy (Coord*, Coord*, int) {
/* implement it in your subclass */
return nil;
}
/*
* CreateHandles creates handles highlighting the NPtSelection's
* points.
*/
void NPtSelection::CreateHandles () {
const int RUBPT = 0;
const Coord* ux;
const Coord* uy;
int n = GetOriginal(ux, uy);
Coord* x = new Coord[n];
Coord* y = new Coord[n];
CopyArray(ux, uy, n, x, y);
TotalTransform(x, y, n);
handles = new RubberHandles(nil, nil, x, y, n, RUBPT, HDSIZE);
delete x;
delete y;
}
/*
* TotalTransform transforms the given points from the Selection's
* coordinate system to the screen's coordinate system.
*/
void NPtSelection::TotalTransform (Coord* x, Coord* y, int n) {
Transformer total;
TotalTransformation(total);
for (int i = 0; i < n; i++) {
total.Transform(x[i], y[i]);
}
}
/*
* InvTotalTransform transforms the given points from the screen's
* coordinate system to the NPtSelection's coordinate system.
*/
void NPtSelection::InvTotalTransform (Coord* x, Coord* y, int n) {
Transformer total;
TotalTransformation(total);
for (int i = 0; i < n; i++) {
total.InvTransform(x[i], y[i]);
}
}
/*
* ClosestPoint returns the index within the arrays of the closest
* point to the given coordinates.
*/
int NPtSelection::ClosestPoint (Coord* x, Coord* y, int n, Coord px,
Coord py) {
int closestpt = 0;
float mindist = Distance(x[0], y[0], px, py);
for (int i = 1; i < n; i++) {
float dist = Distance(x[i], y[i], px, py);
if (dist < mindist) {
mindist = dist;
closestpt = i;
}
}
return closestpt;
}
/*
* LeftAcont checks whether the left arrowhead contains the point.
*/
boolean NPtSelection::LeftAcont (Coord x0, Coord y0, Coord x1, Coord y1,
PointObj& po, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->LeftArrow()) {
return ArrowHeadcont(x0, y0, x1, y1, po, gs);
}
return false;
}
/*
* RightAcont checks whether the right arrowhead contains the point.
*/
boolean NPtSelection::RightAcont (Coord x0, Coord y0, Coord x1, Coord y1,
PointObj& po, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->RightArrow()) {
return ArrowHeadcont(x0, y0, x1, y1, po, gs);
}
return false;
}
/*
* LeftAints checks whether the left arrowhead intersects the area.
*/
boolean NPtSelection::LeftAints (Coord x0, Coord y0, Coord x1, Coord y1,
BoxObj& userb, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->LeftArrow()) {
return ArrowHeadints(x0, y0, x1, y1, userb, gs);
}
return false;
}
/*
* RightAints checks whether the right arrowhead intersects the area.
*/
boolean NPtSelection::RightAints (Coord x0, Coord y0, Coord x1, Coord y1,
BoxObj& userb, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->RightArrow()) {
return ArrowHeadints(x0, y0, x1, y1, userb, gs);
}
return false;
}
/*
* drawLeftA draws the left arrowhead if it should be drawn.
*/
void NPtSelection::drawLeftA (Coord x0, Coord y0, Coord x1, Coord y1,
Canvas* c, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->LeftArrow()) {
drawArrowHead(x0, y0, x1, y1, c, gs);
}
}
/*
* drawRightA draws the right arrowhead if it should be drawn.
*/
void NPtSelection::drawRightA (Coord x0, Coord y0, Coord x1, Coord y1,
Canvas* c, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->RightArrow()) {
drawArrowHead(x0, y0, x1, y1, c, gs);
}
}
/*
* Define an arrow head with its tip at the origin and its tail on the
* negative x axis so we can rotate the tail about the origin to the
* right angle and translate the tip to the right point.
*/
static const int ARROWN = 3;
static Coord arrowx[ARROWN] = {0, -ARROWHEIGHT, -ARROWHEIGHT};
static Coord arrowy[ARROWN] = {0, ARROWWIDTH/2, -ARROWWIDTH/2};
static Coord arrowconvx[ARROWN + 1];
static Coord arrowconvy[ARROWN + 1];
/*
* MergeArrowHeadTol returns a tolerance to use around the graphic's
* extent that includes the arrowhead's size.
*/
float NPtSelection::MergeArrowHeadTol (float tol, Graphic* gs) {
IBrush* brush = (IBrush*) gs->GetBrush();
if (brush->LeftArrow() || brush->RightArrow()) {
float magnif = 1;
Transformer* view = getRoot()->GetTransformer();
if (view != nil && !view->Rotated()) { /* rot breaks magnif calc */
float fpx0, fpy0, fpx1, fpy1;
view->Transform(0.0, 0.0, fpx0, fpy0);
view->Transform(1.0, 1.0, fpx1, fpy1);
magnif = fpy1 - fpy0;
}
float arrowtol = 0.5 * ARROWWIDTH * points * magnif;
tol = max(arrowtol, tol);
}
return tol;
}
/*
* ArrowHeadcont returns true if the arrowhead contains the point.
* The arrowhead may be filled or unfilled depending on the pattern.
*/
boolean NPtSelection::ArrowHeadcont (Coord x0, Coord y0, Coord x1, Coord y1,
PointObj& po, Graphic* gs) {
boolean contains = false;
IPattern* pattern = (IPattern*) gs->GetPattern();
SetCTM(x0, y0, x1, y1, gs, !pattern->None());
PointObj pt(&po);
invTransform(pt.x, pt.y, gs);
if (pattern->None()) {
MultiLineObj ml(arrowx, arrowy, ARROWN);
LineObj l(arrowx[ARROWN-1], arrowy[ARROWN-1], arrowx[0], arrowy[0]);
contains = ml.Contains(pt) || l.Contains(pt);
} else {
FillPolygonObj fp(arrowx, arrowy, ARROWN);
contains = fp.Contains(pt);
}
RestoreCTM(gs);
return contains;
}
/*
* ArrowHeadints returns true if the arrowhead intersects the area.
* The arrowhead may be filled or unfilled depending on the pattern.
*/
boolean NPtSelection::ArrowHeadints (Coord x0, Coord y0, Coord x1, Coord y1,
BoxObj& userb, Graphic* gs) {
boolean intersects = false;
IPattern* pattern = (IPattern*) gs->GetPattern();
SetCTM(x0, y0, x1, y1, gs, !pattern->None());
transformList(arrowx, arrowy, ARROWN, arrowconvx, arrowconvy, gs);
if (pattern->None()) {
arrowconvx[ARROWN] = arrowconvx[0];
arrowconvy[ARROWN] = arrowconvy[0];
MultiLineObj ml(arrowconvx, arrowconvy, ARROWN + 1);
intersects = ml.Intersects(userb);
} else {
FillPolygonObj fp(arrowconvx, arrowconvy, ARROWN);
intersects = fp.Intersects(userb);
}
RestoreCTM(gs);
return intersects;
}
/*
* drawArrowHead draws the arrowhead.
*/
void NPtSelection::drawArrowHead (Coord x0, Coord y0, Coord x1, Coord y1,
Canvas* c, Graphic* gs) {
IPattern* pattern = (IPattern*) gs->GetPattern();
if (!pattern->None()) {
SetCTM(x0, y0, x1, y1, gs, true);
update(gs);
pFillPolygon(c, arrowx, arrowy, ARROWN);
RestoreCTM(gs);
}
IBrush* brush = (IBrush*) gs->GetBrush();
if (!brush->None()) {
SetCTM(x0, y0, x1, y1, gs, false);
update(gs);
pPolygon(c, arrowx, arrowy, ARROWN);
RestoreCTM(gs);
}
}
/*
* SetCTM stores gs's former transformation matrix and overwrites it
* with a new one defined to scale the arrowhead to its proper size
* and align it with the line. The matrix includes the graphic's
* topmost parent's scaling but no other parents' scaling so the
* arrowhead will change size when we zoom the view but stay the same
* size when we scale the line. New argument patternfill special-
* cases filling arrowhead to include the outermost edge of the
* outline drawn by the brush so dashed brushes won't expose white
* space where there's no pattern fill.
*/
static Transformer* origCTM;
static Transformer* arrowCTM;
void NPtSelection::SetCTM (Coord x0, Coord y0, Coord x1, Coord y1,
Graphic* gs, boolean patternfill) {
if (arrowCTM == nil) {
arrowCTM = new Transformer;
arrowCTM->Reference();
}
*arrowCTM = *identity;
if (patternfill) {
IBrush* brush = (IBrush*) gs->GetBrush();
float bw = brush->Width();
float padtip = sqrt(ARROWHEIGHT*ARROWHEIGHT +
0.25*ARROWWIDTH*ARROWWIDTH) * bw / ARROWWIDTH;
float padtail = bw / 2;
float patternscale = (ARROWHEIGHT + padtip + padtail) / ARROWHEIGHT;
arrowCTM->Scale(patternscale, patternscale);
arrowCTM->Translate(padtip, 0.);
}
arrowCTM->Scale(point, point);
Transformer* view = getRoot()->GetTransformer();
if (view != nil && !view->Rotated()) { /* rot breaks magnif calc */
float fpx0, fpy0, fpx1, fpy1;
view->Transform(0.0, 0.0, fpx0, fpy0);
view->Transform(1.0, 1.0, fpx1, fpy1);
float arrowxmag = fpx1 - fpx0;
float arrowymag = fpy1 - fpy0;
if (arrowxmag == arrowymag) { /* won't scale arrows in BrushView */
arrowCTM->Scale(arrowxmag, arrowymag);
}
}
float tipx, tipy, tailx, taily;
transform(float(x0), float(y0), tipx, tipy, gs);
transform(float(x1), float(y1), tailx, taily, gs);
float angle = Slope(tipx - tailx, tipy - taily);
arrowCTM->Rotate(angle);
arrowCTM->Translate(tipx, tipy);
origCTM = gs->GetTransformer();
if (origCTM != nil) {
origCTM->Reference();
}
gs->SetTransformer(arrowCTM);
}
/*
* RestoreCTM restores gs's original transformation matrix.
*/
void NPtSelection::RestoreCTM (Graphic* gs) {
gs->SetTransformer(origCTM);
Unref(origCTM);
}
/*
* sign returns 1 if the number's nonnegative, -1 if it's negative.
*/
inline float sign (float num) {
return (num >= 0.) ? 1. : -1.;
}
/*
* Slope returns the number of degrees in the given slope.
*/
float NPtSelection::Slope (float dx, float dy) {
float angle = 0.;
if (dx == 0.) {
angle = sign(dy) * 90.;
} else {
angle = degrees(atan(dy/dx));
if (dx < 0.) {
angle += sign(dy) * 180.;
}
}
return angle;
}