This is HitXDPS.c in view mode; [Download] [Up]
/*
* $RCSfile: HitXDPS.c,v $
*
* Copyright (C) 1992 by Adobe Systems Incorporated.
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notices appear in all copies and that
* both those copyright notices and this permission notice appear in
* supporting documentation and that the name of Adobe Systems
* Incorporated not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. If any portion of this software is changed, it cannot be
* marketed under Adobe's trademarks and/or copyrights unless Adobe, in
* its sole discretion, approves by a prior writing the quality of the
* resulting implementation.
*
* ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
* ANY PURPOSE. IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
* ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL ADOBE BE LIABLE
* TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ADOBE WILL NOT
* PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
*
* PostScript, Display PostScript, and Adobe are trademarks of Adobe Systems
* Incorporated registered in the U.S.A. and other countries.
*
* Author: Adobe Systems Incorporated
*/
/***************************************************************
**
** INCLUDES
**
***************************************************************/
#include "Hit.h"
/***************************************************************
**
** DATA DECLARATIONS
**
***************************************************************/
static void mergeBoxes(), bboxOfObject(), insetBox();
static int Grid; /* User path index for grid */
static UserPath Curve; /* User path for the bezier curve */
char FontName[] = "ControlPointsFont";
static char ControlString[] = "abba";
float CtlPtSize = 4.0; /* size of the control points */
/***************************************************************
**
** FUNCTION: drawControlPoints
**
** DESCRIPTION: Displays the control points for the curve passed in
**
** PARAMETERS: object curve being drawn
**
** RETURN: None
**
***************************************************************/
static void drawControlPoints(object)
UserPath *object;
{
int i, j;
float pts[10];
/*
** Set the position of the first control point
*/
i = 0;
pts[i++] = object->pts[0];
pts[i++] = object->pts[1];
/*
** Compute the positions of other points relative to the previous point
*/
for (j = 2; i < PTS_BEZIER * 2; j++, i++)
pts[i] = object->pts[j] - object->pts[j - 2];
pts[i++] = 0;
pts[i++] = 0;
PSWDrawControlPoints(pts[0], pts[1], &pts[2], 8, ControlString);
} /* end drawControlPoints() */
/***************************************************************
**
** FUNCTION: drawControlLines
**
** DESCRIPTION: Draws the control lines for the upath
**
** PARAMETERS: object upath of curve to draw
**
** RETURN: None
**
***************************************************************/
static void drawControlLines(object)
UserPath *object;
{
DPSUserPathOp ops[4];
/*
** Set the operations to draw the two lines. We can use
** the same coordinates and bounding box
*/
ops[0] = ops[2] = dps_moveto;
ops[1] = ops[3] = dps_lineto;
PSDoUserPath((DPSPointer) object->pts, 8, dps_float,
ops, 4, (DPSPointer) object->bbox, dps_ustroke);
} /* end drawControlLines() */
/***************************************************************
**
** FUNCTION: drawObject
**
** DESCRIPTION: Draws the object and, if selected, the controls
**
** PARAMETERS: object upath of curve to draw
**
** RETURN: None
**
***************************************************************/
static void drawObject(object)
UserPath *object;
{
PSDoUserPath((DPSPointer) object->pts, object->numPts, dps_float,
object->ops, object->numOps, (DPSPointer) object->bbox,
dps_ustroke);
if (AppData.selected) {
drawControlPoints(object);
drawControlLines(object);
}
} /* end drawObject() */
/***************************************************************
**
** FUNCTION: drawSelf
**
** DESCRIPTION: Draws the existing curve as defined in the upath
** Curve. Also draws the grid if it is turned on.
** Does not return until drawing is complete.
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
void drawSelf()
{
XDPSChainTextContext(AppData.dpsCtxt, AppData.drawTrace);
PSWClearWindow(PAGE_WIDTH, PAGE_HEIGHT);
/*
** Draw the background desktop, if needed
*/
if (AppData.desktop) PSWDesktop(0.0, 0.0, PAGE_WIDTH,
PAGE_HEIGHT);
/*
** Draw the grid if necessary
*/
if (AppData.gridOn) PSWDrawGrid(COLORGRID, WIDTHGRID, Grid);
/*
** Draw the curve and control points if selected
*/
drawObject(&Curve);
DPSWaitContext(AppData.dpsCtxt);
if (AppData.drawTrace) XDPSChainTextContext(AppData.dpsCtxt, False);
/*
** If showing buffers, refresh original buffer window
*/
if (AppData.showBuffer) {
XCopyArea(XtDisplay(AppData.drawingArea), AppData.original,
XtWindow(AppData.bufOrig), AppData.gc,
0, 0, AppData.drawingWidth,
AppData.drawingHeight, 0, 0);
}
} /* end drawSelf() */
/***************************************************************
**
** FUNCTION: drawSelfAndUpdate
**
** DESCRIPTION: Draw the curve, then copy the original pixmap into
** the drawing area window
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
void drawSelfAndUpdate()
{
drawSelf();
XCopyArea(XtDisplay(AppData.drawingArea), AppData.original,
XtWindow(AppData.drawingArea), AppData.gc,
0, 0, AppData.drawingWidth, AppData.drawingHeight, 0, 0);
}
/***************************************************************
**
** FUNCTION: copyCurve
**
** DESCRIPTION: This function creates a copy of the existing
** curve. Since the operands are the same for
** drawing the curve the original operand array is
** used.
**
** PARAMETERS: object upath in which to copy curve
**
** RETURN: None
**
***************************************************************/
static void copyCurve(object)
UserPath *object;
{
register int i;
/*
** Use the original operand array
*/
object->ops = Curve.ops;
object->numOps = Curve.numOps;
/*
** Allocate space for the points on the object
*/
object->pts = (float *) XtCalloc(PTS_CURVE_BUFFER, sizeof(float));
/*
** Copy the points and bbox from the existing object
*/
for (i = 0; i < Curve.numPts; i++) object->pts[i] = Curve.pts[i];
object->numPts = Curve.numPts;
for (i = 0; i < 4; i++) object->bbox[i] = Curve.bbox[i];
} /* end copyCurve() */
/***************************************************************
**
** FUNCTION: changePoint
**
** DESCRIPTION: Change the point number passed in by the amount
** passed in in pt. Recalculate the bounds because
** one of the bounding points could have been the
** changed point.
**
** PARAMETERS: ptNum index of control point
** object upath to change
** pt point indicating change in x & y
**
** RETURN: None
**
***************************************************************/
static void changePoint(ptNum, object, pt)
int ptNum;
UserPath *object;
Point *pt;
{
int i;
/*
** Relocate the point
*/
object->pts[ptNum*2] += pt->x;
object->pts[ptNum*2 + 1] += pt->y;
/*
** Recalculate the bounds
*/
LLX(object) = LLY(object) = 9999;
URX(object) = URY(object) = -9999;
for (i = 0; i < PTS_BEZIER * 2; i += 2) {
LLX(object) = MIN(LLX(object), object->pts[i]);
LLY(object) = MIN(LLY(object), object->pts[i+1]);
URX(object) = MAX(URX(object), object->pts[i]);
URY(object) = MAX(URY(object), object->pts[i+1]);
}
} /* end changePoint() */
/***************************************************************
**
** FUNCTION: setPoint
**
** DESCRIPTION: This function determines which points need to be
** changed when one of the control points is changed.
** ptNum is the changing control point. pt holds the
** relative change in each coordinate.
**
** PARAMETERS: ptNum index of changing control point
** object upath to change
** pt point indicating change in x & y
**
** RETURN: None
**
***************************************************************/
static void setPoint(ptNum, object, pt)
int ptNum;
UserPath *object;
Point *pt;
{
changePoint(ptNum, object, pt);
/*
** If this is the first or last point, the adjacent control
** point must change by the same amount
*/
if (ptNum == 0) changePoint(1, object, pt);
else if (ptNum == 3) changePoint(2, object, pt);
} /* end setPoint() */
/***************************************************************
**
** FUNCTION: copyOrigToComposite
**
** DESCRIPTION: Copies the original buffer to the composite one
** and updates the buffer window
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
static void copyOrigToComposite()
{
Display *dpy = XtDisplay(AppData.drawingArea);
XCopyArea(dpy, AppData.original, AppData.composite, AppData.gc,
0, 0, AppData.drawingWidth, AppData.drawingHeight,
0, 0);
if (AppData.showBuffer) {
XCopyArea(dpy, AppData.composite, XtWindow(AppData.bufComp),
AppData.gc, 0, 0, AppData.drawingWidth,
AppData.drawingHeight, 0, 0);
}
} /* end copyOrigToComposite() */
/***************************************************************
**
** FUNCTION: checkScrolling
**
** DESCRIPTION: Checks if the point is outside the window, and scrolls
** if so.
**
** PARAMETERS: x Mouse x coordinate
** y Mouse y coordinate
**
** RETURN: whether scrolling occured
**
***************************************************************/
static Boolean checkScrolling(x, y)
int x, y;
{
int deltaX = 0, deltaY = 0;
/*
** If inside the window, no scrolling needed
*/
if (x >= 0 && y >= 0 &&
x <= AppData.drawingWidth && y <= AppData.drawingHeight) return False;
/*
** If x is outside to left, scroll to left...but not beyond left edge
*/
if (x < 0) {
deltaX = x;
if (deltaX < -AppData.scrollX) deltaX = -AppData.scrollX;
} else if (x > AppData.drawingWidth) {
/*
** Ditto for outside to right
*/
if (AppData.scaledWidth > AppData.drawingWidth) {
if (AppData.scrollX + x > AppData.scaledWidth) {
deltaX = AppData.scaledWidth -
(AppData.scrollX + AppData.drawingWidth);
} else deltaX = x - AppData.drawingWidth;
}
}
/*
** Now do the same thing for y
*/
if (y < 0) {
deltaY = y;
if (deltaY < -AppData.scrollY) deltaY = -AppData.scrollY;
} else if (y > AppData.drawingHeight) {
if (AppData.scaledHeight > AppData.drawingHeight) {
if (AppData.scrollY + y > AppData.scaledHeight) {
deltaY = AppData.scaledHeight -
(AppData.scrollY + AppData.drawingHeight);
} else deltaY = y - AppData.drawingHeight;
}
}
/*
** We may have hit an edge and not need to scroll after all
*/
if (deltaX == 0 && deltaY == 0) return False;
/*
** Restore original buffer gstate and do the scroll
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.origGState);
doScroll(deltaX, deltaY);
/*
** Reflect new scrolled values in scrollbars
*/
XtVaSetValues(AppData.hScroll, XmNvalue, AppData.scrollX, NULL);
XtVaSetValues(AppData.vScroll, XmNvalue, AppData.scrollY, NULL);
/*
** Restore composite gstate for drawing
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.compGState);
return True;
} /* end checkScrolling() */
/***************************************************************
**
** FUNCTION: getNextMouseEvent
**
** DESCRIPTION: Returns the next button release or motion event
**
** PARAMETERS: dpy Display
** win Window
**
** RETURN: event Event
**
***************************************************************/
static void getNextMouseEvent(dpy, win, event)
Display *dpy;
Window win;
XEvent *event;
{
XWindowEvent(dpy, win, ButtonMotionMask | ButtonReleaseMask, event);
/*
** Do motion compression by skipping over more motion events
*/
while (event->type != ButtonRelease) {
if (!XCheckWindowEvent(dpy, win,
ButtonMotionMask | ButtonReleaseMask,
event)) break;
}
} /* end getNextMouseEvent() */
/***************************************************************
**
** FUNCTION: computeReshapeBBox
**
** DESCRIPTION: Compute bounding box that must be updated
**
** PARAMETERS: object Pointer to changing object
** first Whether this is the first call (updated)
** oldBBox Previous curve bounding box (updated)
**
** RETURN: xll, xur X bounds of area to update
**
***************************************************************/
static void computeReshapeBBox(object, first, oldBBox, xll, xur)
UserPath *object;
Boolean *first;
BBox *oldBBox;
XPoint *xll, *xur;
{
BBox currentBBox;
if (AppData.copyAll) {
xll->x = xur->y = 0;
xll->y = AppData.drawingHeight;
xur->x = AppData.drawingWidth;
return;
}
/*
** The changed area is the union of the bounding box of the
** new curve and the previous curve
*/
bboxOfObject(object, ¤tBBox);
if (!*first) {
mergeBoxes(oldBBox, ¤tBBox);
} else {
*oldBBox = currentBBox;
*first = False;
}
/*
** Compute the changed area.
*/
convertToX(xll, &(oldBBox->ll));
convertToX(xur, &(oldBBox->ur));
*oldBBox = currentBBox;
} /* end computeReshapeBBox() */
/***************************************************************
**
** FUNCTION: doReshape
**
** DESCRIPTION: Handle a mouse movement during reshape
**
** PARAMETERS: object Changing object
** delta Change in mouse location original
** first Whether this is the first time
** scrolled Whether we scrolled this time through
** ptNum Which point is changing
**
** RETURN: None
**
***************************************************************/
static void doReshape(object, delta, first, scrolled, ptNum)
UserPath *object;
Point *delta;
Boolean *first, scrolled;
int ptNum;
{
static BBox oldBBox;
XPoint xll, xur;
Display *dpy = XtDisplay(AppData.drawingArea);
Window win = XtWindow(AppData.drawingArea);
/*
** Change the point locations of the curve
*/
setPoint(ptNum, object, delta);
/*
** Compute area that must be updated. If we scrolled, make it
** everything, but we still have to compute the bbox so we have
** an oldBBox for next time around
*/
computeReshapeBBox(object, first, &oldBBox, &xll, &xur);
if (scrolled) {
xll.x = xur.y = 0;
xll.y = AppData.drawingHeight;
xur.x = AppData.drawingWidth;
}
/*
** Copy original into composite
*/
XCopyArea(dpy, AppData.original, AppData.composite, AppData.gc,
xll.x, xur.y, xur.x - xll.x, xll.y - xur.y, xll.x, xur.y);
/*
** Draw the new curve to the composite pixmap
*/
drawObject(object);
/*
** Synchronize so the drawing is complete and copy into the window
*/
DPSWaitContext(AppData.dpsCtxt);
XCopyArea(dpy, AppData.composite, win, AppData.gc, xll.x, xur.y,
xur.x - xll.x, xll.y - xur.y, xll.x, xur.y);
if (AppData.showBuffer) {
XCopyArea(dpy, AppData.composite, XtWindow(AppData.bufComp),
AppData.gc, xll.x, xur.y, xur.x - xll.x, xll.y - xur.y,
xll.x, xur.y);
}
} /* end doReshape() */
/***************************************************************
**
** FUNCTION: doReshapeLoop
**
** DESCRIPTION: Event handling loop for Bezier reshaping
**
** PARAMETERS: object Pointer to changing object
** initPt Initial mouse down point
** ptNum Which point is changing
**
** RETURN: None
**
***************************************************************/
static void doReshapeLoop(object, initPt, ptNum)
UserPath *object;
Point *initPt;
int ptNum;
{
Point point, delta, lastPt;
XEvent event;
XPoint xpoint;
Boolean first = True;
Display *dpy = XtDisplay(AppData.drawingArea);
Window win = XtWindow(AppData.drawingArea);
Boolean scrolled;
lastPt = *initPt;
do {
/*
** Wait for a motion or button release event
*/
getNextMouseEvent(dpy, win, &event);
/*
** See if user moved outside of window, and scroll drawing if so
*/
scrolled = checkScrolling(event.xbutton.x, event.xbutton.y);
/*
** Store new coordinates in xpoint and point
*/
xpoint.x = event.xbutton.x;
xpoint.y = event.xbutton.y;
convertToDPS(&xpoint, &point);
/*
** Compute the movement of the mouse
*/
delta.x = point.x - lastPt.x;
delta.y = point.y - lastPt.y;
/*
** If the mouse moved, update
*/
if (delta.x || delta.y) {
doReshape(object, &delta, &first, scrolled, ptNum);
}
lastPt = point;
} while (event.type != ButtonRelease);
} /* end doReshapeLoop() */
/***************************************************************
**
** FUNCTION: reshapeObject
**
** DESCRIPTION: Redraws the graphic when one of the control
** points changes
**
** PARAMETERS: pt X coordinates of button press
** ptNum index of control point used
**
** RETURN: None
**
***************************************************************/
void reshapeObject(pt, ptNum)
XPoint *pt;
int ptNum;
{
Point initPt;
UserPath object;
register int i;
XDPSChainTextContext(AppData.dpsCtxt, AppData.drawTrace);
/*
** Since all our drawing is to the composite pixmap, switch to that
** pixmap's gstate
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.compGState);
/*
** Convert the mouse point to PS coordinates
*/
convertToDPS(pt, &initPt);
/*
** Initialize the object to the existing object
*/
copyCurve(&object);
/*
** Copy the original to the composite pixmap
*/
copyOrigToComposite();
/*
** Call reshape event dispatching loop
*/
doReshapeLoop(&object, &initPt, ptNum);
/*
** Done, so update stored curve from new one
*/
for (i = 0; i < Curve.numPts; i++) Curve.pts[i] = object.pts[i];
for (i = 0; i < 4; i++) Curve.bbox[i] = object.bbox[i];
XtFree(object.pts);
/*
** Restore drawing to original pixmap
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.origGState);
drawSelfAndUpdate();
if (AppData.drawTrace) XDPSChainTextContext(AppData.dpsCtxt, False);
} /* end reshapeObject() */
/***************************************************************
**
** FUNCTION: moveAll
**
** DESCRIPTION: Moves the graphic object using the relative point
** change.
**
** PARAMETERS: pt relative point change
**
** RETURN: None
**
***************************************************************/
static void moveAll(pt)
Point *pt;
{
int i;
register UserPath *c = &Curve;
for (i = 0; i < PTS_BEZIER * 2; i += 2) {
Curve.pts[i] += pt->x;
Curve.pts[i + 1] += pt->y;
}
LLX(c) += pt->x;
LLY(c) += pt->y;
URX(c) += pt->x;
URY(c) += pt->y;
} /* end moveAll() */
/***************************************************************
**
** FUNCTION: computeMoveBBox
**
** DESCRIPTION: Compute bounding box that must be updated
**
** PARAMETERS: trans Current translation
** first Whether this is the first call (updated)
** oldBBox Previous curve bounding box (updated)
**
** RETURN: xll, xur X bounds of area to update
**
***************************************************************/
static void computeMoveBBox(trans, first, oldBBox, xll, xur)
Point *trans;
Boolean *first;
BBox *oldBBox;
XPoint *xll, *xur;
{
BBox currentBBox;
if (AppData.copyAll) {
xll->x = xur->y = 0;
xll->y = AppData.drawingHeight;
xur->x = AppData.drawingWidth;
return;
}
/*
** The changed area is the union of the bounding box of the
** translated curve and the previous curve
*/
bboxOfObject(&Curve, ¤tBBox);
currentBBox.ll.x += trans->x;
currentBBox.ur.x += trans->x;
currentBBox.ll.y += trans->y;
currentBBox.ur.y += trans->y;
if (!*first) {
mergeBoxes(oldBBox, ¤tBBox);
} else {
*oldBBox = currentBBox;
*first = False;
}
/*
** Compute the changed area.
*/
convertToX(xll, &(oldBBox->ll));
convertToX(xur, &(oldBBox->ur));
*oldBBox = currentBBox;
} /* end computeMoveBBox() */
/***************************************************************
**
** FUNCTION: doMove
**
** DESCRIPTION: Handle a mouse movement during move
**
** PARAMETERS: trans Current translation
** first Whether this is the first call
** scrolled Whether we scrolled this time through
**
** RETURN: None
**
***************************************************************/
static void doMove(trans, first, scrolled)
Point *trans;
Boolean *first, scrolled;
{
static BBox oldBBox;
XPoint xll, xur;
Display *dpy = XtDisplay(AppData.drawingArea);
Window win = XtWindow(AppData.drawingArea);
/*
** Compute area that must be updated. If we scrolled, make it
** everything, but we still have to compute the bbox so we have
** an oldBBox for next time around
*/
computeMoveBBox(trans, first, &oldBBox, &xll, &xur);
if (scrolled) {
xll.x = xur.y = 0;
xll.y = AppData.drawingHeight;
xur.x = AppData.drawingWidth;
}
/*
** Copy original into composite
*/
XCopyArea(dpy, AppData.original, AppData.composite, AppData.gc,
xll.x, xur.y, xur.x - xll.x, xll.y - xur.y, xll.x, xur.y);
/*
** Draw the translated curve to the composite pixmap
*/
PSgsave();
PStranslate(trans->x, trans->y);
drawObject(&Curve);
PSgrestore();
/*
** Synchronize so the drawing is complete before copying to the window
*/
DPSWaitContext(AppData.dpsCtxt);
XCopyArea(dpy, AppData.composite, win, AppData.gc, xll.x, xur.y,
xur.x - xll.x, xll.y - xur.y, xll.x, xur.y);
if (AppData.showBuffer) {
XCopyArea(dpy, AppData.composite, XtWindow(AppData.bufComp),
AppData.gc, xll.x, xur.y, xur.x - xll.x, xll.y - xur.y,
xll.x, xur.y);
}
} /* end doMove() */
/***************************************************************
**
** FUNCTION: doMoveLoop
**
** DESCRIPTION: Event handling loop for Bezier moving
**
** PARAMETERS: initPt Original mouse point
**
** RETURN: trans Returns final translation
**
***************************************************************/
static void doMoveLoop(initPt, trans)
Point *initPt, *trans;
{
XPoint xpoint;
Point point, delta, lastPt;
XEvent event;
Display *dpy = XtDisplay(AppData.drawingArea);
Window win = XtWindow(AppData.drawingArea);
Boolean scrolled, first = True;
lastPt = *initPt;
do {
/*
** Wait for a motion or button release event
*/
getNextMouseEvent(dpy, win, &event);
/*
** See if user moved outside of window, and scroll drawing if so
*/
scrolled = checkScrolling(event.xbutton.x, event.xbutton.y);
/*
** Store new coordinates in xpoint and point
*/
xpoint.x = event.xbutton.x;
xpoint.y = event.xbutton.y;
convertToDPS(&xpoint, &point);
/*
** Compute the movement of the mouse and total change
*/
delta.x = point.x - lastPt.x;
delta.y = point.y - lastPt.y;
trans->x = point.x - initPt->x;
trans->y = point.y - initPt->y;
/*
** If the mouse moved, update
*/
if (delta.x || delta.y) doMove(trans, &first, scrolled);
lastPt = point;
} while (event.type != ButtonRelease);
} /* end doMoveLoop() */
/***************************************************************
**
** FUNCTION: moveObject
**
** DESCRIPTION: Moves the graphic object. Essentially the same
** algorithm is used for moving the object as in
** reshapeObject() except translation is used to
** move the position of the object when it is redrawn.
**
** PARAMETERS: xpoint position of cursor at start of move
**
** RETURN: None
**
***************************************************************/
void moveObject(xpt)
XPoint *xpt;
{
Point initPt, trans;
XDPSChainTextContext(AppData.dpsCtxt, AppData.drawTrace);
/*
** Since all our drawing is to the composite pixmap, switch to that
** pixmap's gstate
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.compGState);
/*
** Convert the mouse point to PS coordinates
*/
convertToDPS(xpt, &initPt);
/*
** Copy the original to the composite pixmap
*/
copyOrigToComposite();
doMoveLoop(&initPt, &trans);
/*
** Done, so move the curve to the new location
*/
moveAll(&trans);
/*
** Restore drawing to original pixmap
*/
(void) XDPSSetContextGState(AppData.dpsCtxt, AppData.origGState);
drawSelfAndUpdate();
if (AppData.drawTrace) XDPSChainTextContext(AppData.dpsCtxt, False);
} /* end moveObject() */
/***************************************************************
**
** FUNCTION: hitControl
**
** DESCRIPTION: Check for a hit on the control points.
**
** PARAMETERS: xpoint X coordinates of hit point
** ptNum returned index of control point hit
**
** RETURN: True if control point hit
** False if no hit
**
***************************************************************/
Boolean hitControl(xpoint, ptNum)
XPoint *xpoint;
int *ptNum;
{
int i;
Point center;
float hitSize;
float dx, dy;
/*
** Convert mouse point into PS coordinates
*/
convertToDPS(xpoint, ¢er);
/*
** Check for a control point hit. Just see if the distance
** between the mouse point and the control point is less than
** the size of the control point or the mouse sensitivity
*/
hitSize = MAX(CtlPtSize / AppData.scale,
AppData.hitSize / 2.0 / AppData.scale);
for (i = 0; i < PTS_BEZIER * 2; i += 2) {
dx = center.x - Curve.pts[i];
dy = center.y - Curve.pts[i+1];
if (sqrt(dx*dx + dy*dy) < hitSize) {
*ptNum = i/2;
return True;
}
}
return False;
} /* end hitControl() */
/***************************************************************
**
** FUNCTION: hitObject
**
** DESCRIPTION: Check for a hit on the curve. This uses
** the inustroke operator to check for an
** intersection with the path of the Bezier.
**
** PARAMETERS: xpoint X coordinates of hit point
**
** RETURN: True if curve hit
** False if no hit
**
***************************************************************/
Boolean hitObject(xpoint)
XPoint *xpoint;
{
Point center;
BBox bounds;
Boolean hit = True;
float hitSize;
XDPSChainTextContext(AppData.dpsCtxt, AppData.hitTrace);
/*
** Convert mouse point into PS coordinates
*/
convertToDPS(xpoint, ¢er);
/*
** Compute the hit size in scaled user space
*/
hitSize = AppData.hitSize / 2.0 / AppData.scale;
/*
** Get the bounding box of the curve and check if the point is
** inside the bounding box. The bounding box is expanded by the
** hit size
*/
bboxOfObject(&Curve, &bounds);
if (center.x < bounds.ll.x - hitSize || center.x > bounds.ur.x + hitSize ||
center.y < bounds.ll.y - hitSize || center.y > bounds.ur.y + hitSize)
hit = False;
if (hit) {
/*
** We're in the bounding box, so check against the curve
*/
hit = PSHitUserPath(center.x, center.y, hitSize,
(DPSPointer) Curve.pts, Curve.numPts, dps_float,
Curve.ops, Curve.numOps, (DPSPointer) Curve.bbox,
dps_inustroke);
}
if (AppData.hitTrace) XDPSChainTextContext(AppData.dpsCtxt, False);
return hit;
} /* end hitObject() */
/***************************************************************
**
** FUNCTION: createCurve
**
** DESCRIPTION: Generates the initial curve.
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
static void createCurve()
{
int i;
XPoint xPt;
Point pt;
register UserPath *c = &Curve;
PSerasepage();
/*
** Allocate the curve point and operator arrays
*/
Curve.pts = (float *) XtCalloc(PTS_CURVE_BUFFER, sizeof(float));
Curve.ops = (DPSUserPathOp *) XtCalloc(OPS_CURVE_BUFFER,
sizeof(DPSUserPathOp));
/*
** Initialize the bounding box
*/
LLX(c) = LLY(c) = 9999;
URX(c) = URY(c) = -9999;
for (i = 0; i < PTS_BEZIER * 2; i += 2) {
/*
** Randomly select the points for the curve
*/
xPt.x = random() % (AppData.drawingWidth - 8) + 4;
xPt.y = random() % (AppData.drawingHeight - 8) + 4;
convertToDPS(&xPt, &pt);
c->pts[i] = pt.x;
c->pts[i+1] = pt.y;
/*
** Update the bounding box
*/
LLX(c) = MIN(LLX(c), c->pts[i]);
LLY(c) = MIN(LLY(c), c->pts[i+1]);
URX(c) = MAX(URX(c), c->pts[i]);
URY(c) = MAX(URY(c), c->pts[i+1]);
}
c->numPts = PTS_BEZIER * 2;
/*
** Add the path construction operators
*/
c->ops[0] = (DPSUserPathOp) dps_moveto;
c->ops[1] = (DPSUserPathOp) dps_curveto;
c->numOps = 2;
/*
** Draw the curve in the buffer
*/
drawSelf();
} /* end createCurve() */
/***************************************************************
**
** FUNCTION: drawSensitivityCircle
**
** DESCRIPTION: Draw the hit detection point.
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
void drawSensitivityCircle()
{
DPSPointer pushCookie;
/*
** Temporarily set the context drawable to the mouse window
*/
(void) XDPSPushContextGState(AppData.dpsCtxt, AppData.mouseGState,
&pushCookie);
/*
** Clear the window and draw the new size
*/
XClearWindow(XtDisplay(AppData.mouseArea), XtWindow(AppData.mouseArea));
PSarc(0.0, 0.0, AppData.hitSize / 2.0, 0.0, 360.0);
PSfill();
/*
** Restore the previous graphics state
*/
(void) XDPSPopContextGState(pushCookie);
} /* end drawSensitivityCircle() */
/***************************************************************
**
** FUNCTION: createGrid
**
** DESCRIPTION: Creates the grid. A cached user path is created
** and stored as a user object in the server. The
** grid does not change so it can be stored in the
** server instead of being resent each time.
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
static void createGrid()
{
int i, j, numPts, numOps;
float pt;
DPSUserPathOp *ops;
float *pts;
float bbox[4];
/*
** Compute the size of the point and operator arrays and allocate them
*/
numOps = 2 * ceil(PAGE_WIDTH / SIZEGRID +
PAGE_HEIGHT / SIZEGRID) + 2;
numPts = numOps * 2;
pts = (float *) XtCalloc(numPts, sizeof(float));
ops = (DPSUserPathOp *) XtCalloc(numOps, sizeof(DPSUserPathOp));
/*
** Make the path be cached
*/
i = j = 0;
ops[j++] = dps_ucache;
/*
** The bounding box is the whole window
*/
bbox[0] = 0;
bbox[1] = 0;
bbox[2] = PAGE_WIDTH;
bbox[3] = PAGE_HEIGHT;
/*
** Add vertical lines to path
*/
for (pt = 0; pt < PAGE_WIDTH; pt += SIZEGRID) {
pts[i++] = pt;
pts[i++] = 0;
ops[j++] = dps_moveto;
pts[i++] = pt;
pts[i++] = PAGE_HEIGHT;
ops[j++] = dps_lineto;
}
/*
** Add horizontal lines to path
*/
for (pt = 0; pt < PAGE_HEIGHT; pt += SIZEGRID) {
pts[i++] = 0;
pts[i++] = pt;
ops[j++] = dps_moveto;
pts[i++] = PAGE_WIDTH;
pts[i++] = pt;
ops[j++] = dps_lineto;
}
/*
** Store the grid as a user object first by placing it
** on the stack and then define it
*/
PSDoUserPath((DPSPointer) pts, i, dps_float, ops, j, bbox, dps_send);
Grid = PSDefineAsUserObj();
XtFree(pts);
XtFree(ops);
} /* end createGrid() */
/***************************************************************
**
** FUNCTION: insetBox
**
** DESCRIPTION: Decreases a rectangle by the deltas
**
** PARAMETERS: pBox pointer to bbox
** deltaX change in X
** deltaY change in Y
**
** RETURN: None (the bbox dimensions are updated).
**
***************************************************************/
static void insetBox (pBox, deltaX, deltaY)
BBox *pBox;
float deltaX;
float deltaY;
{
pBox->ll.x += deltaX;
pBox->ll.y += deltaY;
pBox->ur.x -= deltaX;
pBox->ur.y -= deltaY;
}
/***************************************************************
**
** FUNCTION: mergeBoxes
**
** DESCRIPTION: Adds the second bbox to the first
**
** PARAMETERS: orig Original bbox
** add Box being added
**
** RETURN: None
**
***************************************************************/
static void mergeBoxes (orig, add)
BBox *orig, *add;
{
if (add->ll.x < orig->ll.x) orig->ll.x = add->ll.x;
if (add->ll.y < orig->ll.y) orig->ll.y = add->ll.y;
if (add->ur.x > orig->ur.x) orig->ur.x = add->ur.x;
if (add->ur.y > orig->ur.y) orig->ur.y = add->ur.y;
} /* end mergeBoxes() */
/***************************************************************
**
** FUNCTION: bboxOfObject
**
** DESCRIPTION: Return the bbox of a user path, expanded by the
** control point (knob) size
**
** PARAMETERS: object User path
** bbox New bbox
**
** RETURN: None
**
***************************************************************/
static void bboxOfObject(object, bbox)
UserPath *object;
BBox *bbox;
{
float knobsize = (CtlPtSize+2) / AppData.scale;
bbox->ll.x = LLX(object);
bbox->ll.y = LLY(object);
bbox->ur.x = URX(object);
bbox->ur.y = URY(object);
insetBox(bbox, -knobsize, -knobsize);
} /* end bboxOfObject() */
/***************************************************************
**
** FUNCTION: initBuffers
**
** DESCRIPTION: Creates the buffers and the gstates that refer to them
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
static void initBuffers()
{
Display *dpy = XtDisplay(AppData.drawingArea);
Window win = XtWindow(AppData.drawingArea);
int depth;
XtVaGetValues(AppData.drawingArea, XtNdepth, &depth, NULL);
/*
** Create pixmap buffers
*/
AppData.original = XCreatePixmap(dpy, win,
AppData.drawingWidth, AppData.drawingHeight, depth);
AppData.composite = XCreatePixmap(dpy, win,
AppData.drawingWidth, AppData.drawingHeight, depth);
/*
** Clear pixmaps
*/
XFillRectangle(dpy, AppData.original, AppData.gc, 0, 0,
AppData.drawingWidth, AppData.drawingHeight);
XFillRectangle(dpy, AppData.composite, AppData.gc, 0, 0,
AppData.drawingWidth, AppData.drawingHeight);
/*
** Define pixmap gstates
*/
(void) XDPSSetContextDrawable(AppData.dpsCtxt,
AppData.composite, AppData.drawingHeight);
(void) XDPSCaptureContextGState(AppData.dpsCtxt, &AppData.compGState);
(void) XDPSSetContextDrawable(AppData.dpsCtxt,
AppData.original, AppData.drawingHeight);
(void) XDPSCaptureContextGState(AppData.dpsCtxt, &AppData.origGState);
} /* end initBuffers() */
/***************************************************************
**
** FUNCTION: initMouseWindow
**
** DESCRIPTION: Create the gstate to use to draw into the
** mouse sensitivity window
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
static void initMouseWindow()
{
Dimension width, height;
XtVaGetValues(AppData.mouseArea, XtNwidth, &width,
XtNheight, &height, NULL);
(void) XDPSSetContextDrawable(AppData.dpsCtxt, XtWindow(AppData.mouseArea),
height);
/*
** Make the origin be in the center
*/
PSsetXoffset(width/2, height/2);
PSsetgray(0.0);
PSinitclip();
PSinitviewclip();
(void) XDPSCaptureContextGState(AppData.dpsCtxt, &AppData.mouseGState);
} /* end initMouseWindow() */
/***************************************************************
**
** FUNCTION: initDPSContext
**
** DESCRIPTION: Handle post-Realize initialization:
**
** PARAMETERS: None
**
** RETURN: None
**
***************************************************************/
void initDPSContext()
{
Display *dpy = XtDisplay(AppData.drawingArea);
XPoint xpt;
Point pt1, pt2;
int i;
/*
** Get height and width of drawing window
*/
XtVaGetValues(AppData.drawingArea, XtNheight, &AppData.drawingHeight,
XtNwidth, &AppData.drawingWidth, NULL);
/*
** Create the DPSContext in which rendering will occur
*/
AppData.dpsCtxt = XDPSGetSharedContext(dpy);
(void) XDPSSetEventDelivery(dpy, dps_event_pass_through);
if (AppData.dpsCtxt == NULL) {
printf("Couldn't create a Display PostScript context.\n");
exit(1);
}
if (XDPSSetContextDrawable(AppData.dpsCtxt, XtWindow(AppData.drawingArea),
AppData.drawingHeight) != dps_status_success) {
printf ("Couldn't set Display PostScript context drawable.\n");
exit (1);
}
/*
** Set the default DPSContext
*/
DPSSetContext(AppData.dpsCtxt);
/*
** Create the control point font and make it the current font
*/
PSWDefineFont(FontName);
PSselectfont(FontName, CtlPtSize / AppData.scale);
PSsetlinewidth(LINEWIDTH);
/*
** Initialize the window used to show the hit point size
*/
initMouseWindow();
/*
** Initialize the buffers -- must be last initialization;
** leaves the right gstate
*/
initBuffers();
/*
** Get the transformation matrices
*/
PSWGetTransform(AppData.ctm, AppData.invctm,
&AppData.xOffset, &AppData.yOffset);
for (i = 0; i < 6; i++) AppData.origInvctm[i] = AppData.invctm[i];
/*
** Compute how large a page would be needed to draw the whole thing
*/
xpt.x = 0;
xpt.y = 100;
convertToDPS(&xpt, &pt1);
xpt.x = 100;
xpt.y = 0;
convertToDPS(&xpt, &pt2);
AppData.origXScale = ABS(100.0 / (pt2.x - pt1.x));
AppData.origYScale = ABS(100.0 / (pt2.y - pt1.y));
AppData.scaledWidth = PAGE_WIDTH * AppData.origXScale * AppData.scale;
AppData.scaledHeight = PAGE_HEIGHT * AppData.origYScale * AppData.scale;
/*
** Position the drawing area so the center is in the center of the window
*/
positionDrawingArea(PAGE_WIDTH / 2, PAGE_HEIGHT / 2,
AppData.drawingWidth / 2, AppData.drawingHeight / 2);
/*
** Create the grid and the curve
*/
createGrid();
createCurve();
} /* end initDPSContext() */
/***************************************************************
**
** FUNCTION: convertToX
**
** DESCRIPTION: Convert user space to X coordinates.
**
** PARAMETERS: pXPt points to the target XPoint struct;
** pUPt points to the target Point struct;
**
** RETURN: None
**
***************************************************************/
void convertToX (pXPt, pUPt)
XPoint *pXPt;
Point *pUPt;
{
pXPt->x = AppData.ctm[A_COEFF] * pUPt->x + AppData.ctm[C_COEFF] * pUPt->y +
AppData.ctm[TX_CONS] + AppData.xOffset;
pXPt->y = AppData.ctm[B_COEFF] * pUPt->x + AppData.ctm[D_COEFF] * pUPt->y +
AppData.ctm[TY_CONS] + AppData.yOffset;
} /* end convertToX() */
/***************************************************************
**
** FUNCTION: convertToDPS
**
** DESCRIPTION: Convert X coordinates to user space
**
** PARAMETERS: pXPt points to the target XPoint struct;
** pUPt points to the target Point struct;
**
** RETURN: None
**
***************************************************************/
void convertToDPS(pXPt, pUPt)
XPoint *pXPt;
Point *pUPt;
{
int ix, iy;
ix = pXPt->x - AppData.xOffset;
iy = pXPt->y - AppData.yOffset;
pUPt->x = AppData.invctm[A_COEFF] * ix + AppData.invctm[C_COEFF] * iy +
AppData.invctm[TX_CONS];
pUPt->y = AppData.invctm[B_COEFF] * ix + AppData.invctm[D_COEFF] * iy +
AppData.invctm[TY_CONS];
} /* end convertToDPS() */
/***************************************************************
**
** FUNCTION: convertToOrigDPS
**
** DESCRIPTION: Convert X coordinates to user space using the original
** transformation matrix
**
** PARAMETERS: pXPt points to the target XPoint struct;
** pUPt points to the target Point struct;
**
** RETURN: None
**
***************************************************************/
void convertToOrigDPS(pXPt, pUPt)
XPoint *pXPt;
Point *pUPt;
{
int ix, iy;
ix = pXPt->x - AppData.xOffset;
iy = pXPt->y - AppData.yOffset;
pUPt->x = AppData.origInvctm[A_COEFF] * ix +
AppData.origInvctm[C_COEFF] * iy + AppData.origInvctm[TX_CONS];
pUPt->y = AppData.origInvctm[B_COEFF] * ix +
AppData.origInvctm[D_COEFF] * iy + AppData.origInvctm[TY_CONS];
} /* end convertToOrigDPS() */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.