/* * Smithsonian Astrophysical Observatory, Cambridge, MA, USA * This code has been modified under the terms listed below and is made * available under the same terms. */ /* * Copyright 1993-2004 George A Howlett. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include "bltGraph.h" #include "bltGrMarkerLine.h" #include "bltGrMarkerOption.h" #include "bltGrMisc.h" #include "bltGrDef.h" #include "bltConfig.h" #include "bltGrPSOutput.h" using namespace Blt; #define BOUND(x, lo, hi) (((x) > (hi)) ? (hi) : ((x) < (lo)) ? (lo) : (x)) static Tk_OptionSpec optionSpecs[] = { {TK_OPTION_CUSTOM, "-bindtags", "bindTags", "BindTags", "Line all", -1, Tk_Offset(LineMarkerOptions, tags), TK_OPTION_NULL_OK, &listObjOption, 0}, {TK_OPTION_CUSTOM, "-cap", "cap", "Cap", "butt", -1, Tk_Offset(LineMarkerOptions, capStyle), 0, &capStyleObjOption, 0}, {TK_OPTION_CUSTOM, "-coords", "coords", "Coords", NULL, -1, Tk_Offset(LineMarkerOptions, worldPts), TK_OPTION_NULL_OK, &coordsObjOption, 0}, {TK_OPTION_CUSTOM, "-dashes", "dashes", "Dashes", NULL, -1, Tk_Offset(LineMarkerOptions, dashes), TK_OPTION_NULL_OK, &dashesObjOption, 0}, {TK_OPTION_PIXELS, "-dashoffset", "dashOffset", "DashOffset", "0", -1, Tk_Offset(LineMarkerOptions, dashes.offset), 0, NULL, 0}, {TK_OPTION_STRING, "-element", "element", "Element", NULL, -1, Tk_Offset(LineMarkerOptions, elemName), TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_COLOR, "-fill", "fill", "Fill", NULL, -1, Tk_Offset(LineMarkerOptions, fillColor), TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_CUSTOM, "-join", "join", "Join", "miter", -1, Tk_Offset(LineMarkerOptions, joinStyle), 0, &joinStyleObjOption, 0}, {TK_OPTION_PIXELS, "-linewidth", "lineWidth", "LineWidth", "1", -1, Tk_Offset(LineMarkerOptions, lineWidth), 0, NULL, 0}, {TK_OPTION_BOOLEAN, "-hide", "hide", "Hide", "no", -1, Tk_Offset(LineMarkerOptions, hide), 0, NULL, 0}, {TK_OPTION_CUSTOM, "-mapx", "mapX", "MapX", "x", -1, Tk_Offset(LineMarkerOptions, xAxis), 0, &xAxisObjOption, 0}, {TK_OPTION_CUSTOM, "-mapy", "mapY", "MapY", "y", -1, Tk_Offset(LineMarkerOptions, yAxis), 0, &yAxisObjOption, 0}, {TK_OPTION_COLOR, "-outline", "outline", "Outline", STD_NORMAL_FOREGROUND, -1, Tk_Offset(LineMarkerOptions, outlineColor), TK_OPTION_NULL_OK, NULL, 0}, {TK_OPTION_BOOLEAN, "-under", "under", "Under", "no", -1, Tk_Offset(LineMarkerOptions, drawUnder), 0, NULL, CACHE}, {TK_OPTION_PIXELS, "-xoffset", "xOffset", "XOffset", "0", -1, Tk_Offset(LineMarkerOptions, xOffset), 0, NULL, 0}, {TK_OPTION_PIXELS, "-yoffset", "yOffset", "YOffset", "0", -1, Tk_Offset(LineMarkerOptions, yOffset), 0, NULL, 0}, {TK_OPTION_END, NULL, NULL, NULL, NULL, 0, -1, 0, 0, 0} }; LineMarker::LineMarker(Graph* graphPtr, const char* name, Tcl_HashEntry* hPtr) : Marker(graphPtr, name, hPtr) { ops_ = (LineMarkerOptions*)calloc(1, sizeof(LineMarkerOptions)); optionTable_ = Tk_CreateOptionTable(graphPtr->interp_, optionSpecs); gc_ =NULL; segments_ =NULL; nSegments_ =0; } LineMarker::~LineMarker() { if (gc_) graphPtr_->freePrivateGC(gc_); if (segments_) delete [] segments_; } int LineMarker::configure() { LineMarkerOptions* ops = (LineMarkerOptions*)ops_; unsigned long gcMask = (GCLineWidth | GCLineStyle | GCCapStyle | GCJoinStyle); XGCValues gcValues; if (ops->outlineColor) { gcMask |= GCForeground; gcValues.foreground = ops->outlineColor->pixel; } if (ops->fillColor) { gcMask |= GCBackground; gcValues.background = ops->fillColor->pixel; } gcValues.cap_style = ops->capStyle; gcValues.join_style = ops->joinStyle; gcValues.line_width = ops->lineWidth; gcValues.line_style = LineSolid; if (LineIsDashed(ops->dashes)) { gcValues.line_style = (gcMask & GCBackground) ? LineDoubleDash : LineOnOffDash; } GC newGC = graphPtr_->getPrivateGC(gcMask, &gcValues); if (gc_) graphPtr_->freePrivateGC(gc_); if (LineIsDashed(ops->dashes)) graphPtr_->setDashes(newGC, &ops->dashes); gc_ = newGC; return TCL_OK; } void LineMarker::draw(Drawable drawable) { if (nSegments_ > 0) graphPtr_->drawSegments(drawable, gc_, segments_, nSegments_); } void LineMarker::map() { LineMarkerOptions* ops = (LineMarkerOptions*)ops_; nSegments_ = 0; if (segments_) delete [] segments_; if (!ops->worldPts || (ops->worldPts->num < 2)) return; Region2d extents; graphPtr_->extents(&extents); // Allow twice the number of world coordinates. The line will represented // as series of line segments, not one continous polyline. This is // because clipping against the plot area may chop the line into several // disconnected segments. Segment2d* segments = new Segment2d[ops->worldPts->num]; Point2d* srcPtr = ops->worldPts->points; Point2d p = mapPoint(srcPtr, ops->xAxis, ops->yAxis); p.x += ops->xOffset; p.y += ops->yOffset; Segment2d* segPtr = segments; Point2d* pend; for (srcPtr++, pend = ops->worldPts->points + ops->worldPts->num; srcPtr < pend; srcPtr++) { Point2d next = mapPoint(srcPtr, ops->xAxis, ops->yAxis); next.x += ops->xOffset; next.y += ops->yOffset; Point2d q = next; if (lineRectClip(&extents, &p, &q)) { segPtr->p = p; segPtr->q = q; segPtr++; } p = next; } nSegments_ = segPtr - segments; segments_ = segments; clipped_ = (nSegments_ == 0); } int LineMarker::pointIn(Point2d *samplePtr) { GraphOptions* gops = (GraphOptions*)graphPtr_->ops_; return pointInSegments(samplePtr, segments_, nSegments_, (double)gops->search.halo); } int LineMarker::pointInSegments(Point2d* samplePtr, Segment2d* segments, int nSegments, double halo) { double minDist = DBL_MAX; for (Segment2d *sp = segments, *send = sp + nSegments; sp < send; sp++) { Point2d t = getProjection((int)samplePtr->x, (int)samplePtr->y, &sp->p, &sp->q); double right; double left; if (sp->p.x > sp->q.x) { right = sp->p.x; left = sp->q.x; } else { right = sp->q.x; left = sp->p.x; } double top; double bottom; if (sp->p.y > sp->q.y) { bottom = sp->p.y; top = sp->q.y; } else { bottom = sp->q.y; top = sp->p.y; } Point2d p; p.x = BOUND(t.x, left, right); p.y = BOUND(t.y, top, bottom); double dist = hypot(p.x - samplePtr->x, p.y - samplePtr->y); if (dist < minDist) minDist = dist; } return (minDist < halo); } int LineMarker::regionIn(Region2d *extsPtr, int enclosed) { LineMarkerOptions* ops = (LineMarkerOptions*)ops_; if (!ops->worldPts || ops->worldPts->num < 2) return 0; if (enclosed) { for (Point2d *pp = ops->worldPts->points, *pend = pp + ops->worldPts->num; pp < pend; pp++) { Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); if ((p.x < extsPtr->left) && (p.x > extsPtr->right) && (p.y < extsPtr->top) && (p.y > extsPtr->bottom)) { return 0; } } return 1; } else { int count = 0; for (Point2d *pp=ops->worldPts->points, *pend=pp+(ops->worldPts->num - 1); pp < pend; pp++) { Point2d p = mapPoint(pp, ops->xAxis, ops->yAxis); Point2d q = mapPoint(pp + 1, ops->xAxis, ops->yAxis); if (lineRectClip(extsPtr, &p, &q)) count++; } return (count > 0); /* At least 1 segment passes through * region. */ } } void LineMarker::print(PSOutput* psPtr) { LineMarkerOptions* ops = (LineMarkerOptions*)ops_; if (nSegments_ > 0) { psPtr->setLineAttributes(ops->outlineColor, ops->lineWidth, &ops->dashes, ops->capStyle, ops->joinStyle); if ((LineIsDashed(ops->dashes)) && (ops->fillColor)) { psPtr->append("/DashesProc {\n gsave\n "); psPtr->setBackground(ops->fillColor); psPtr->append(" "); psPtr->setDashes(NULL); psPtr->append("stroke\n"); psPtr->append("grestore\n"); psPtr->append("} def\n"); } else psPtr->append("/DashesProc {} def\n"); psPtr->printSegments(segments_, nSegments_); } }