home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- //
- // A very specialized node; this manipulator completely takes care of
- // editing a set of line segments. This is somewhat different from
- // other manipulators, because you insert it in the scene graph and it
- // edits itself (instead of pointing it at the thing you want it to
- // edit).
- //
-
- #include <stdio.h>
- #include <assert.h>
-
- #include <Inventor/SbColor.h>
- #include <Inventor/SoPickedPoint.h>
- #include <Inventor/actions/SoHandleEventAction.h>
- #include <Inventor/details/SoDetail.h>
- #include <Inventor/details/SoLineDetail.h>
- #include <Inventor/details/SoPointDetail.h>
- #include <Inventor/elements/SoViewVolumeElement.h>
- #include <Inventor/elements/SoViewportRegionElement.h>
- #include <Inventor/events/SoEvent.h>
- #include <Inventor/events/SoKeyboardEvent.h>
- #include <Inventor/events/SoLocation2Event.h>
- #include <Inventor/events/SoMouseButtonEvent.h>
- #include <Inventor/misc/SoState.h>
- #include <Inventor/nodes/SoCoordinate3.h>
- #include <Inventor/nodes/SoDrawStyle.h>
- #include <Inventor/nodes/SoLightModel.h>
- #include <Inventor/nodes/SoLineSet.h>
- #include <Inventor/nodes/SoPointSet.h>
- #include <Inventor/nodes/SoTransform.h>
- #include <Inventor/projectors/SbLineProjector.h>
- #include <Inventor/projectors/SbPlaneProjector.h>
-
- #include "LineManip.h"
-
- //
- // Constructor; sets up scene graph
- //
- LineManip2::LineManip2()
- {
- coord = new SoCoordinate3();
- addChild(coord);
- coord->point.deleteValues(0);
-
- pset = new SoPointSet;
- pset->numPoints.setValue(SO_POINT_SET_USE_REST_OF_POINTS);
- addChild(pset);
-
- drawStyle = new SoDrawStyle;
- drawStyle->style = SoDrawStyle::LINES;
- addChild(drawStyle);
-
- lset = new SoLineSet;
- lset->numVertices.setValue(SO_LINE_SET_USE_REST_OF_VERTICES);
- addChild(lset);
-
- current_coord = -1;
- planeProj = new SbPlaneProjector(FALSE);
- planeProj->setPlane( SbPlane( SbVec3f(0,0,1), 0 ) );
- lineProj = new SbLineProjector();
- lineProj->setLine( SbLine( SbVec3f(0,0,0),SbVec3f(0,0,1) ) );
-
- hilightSize = 0.05;
-
- initHilightStuff();
-
- myHandleEventAction = NULL;
- }
-
- //
- // Destructor. This will be called when this is unref'ed.
- LineManip2::~LineManip2()
- {
- //
- // Don't need to delete children; they will have already been
- // unref'ed and deleted (since this is derived from SoGroup).
- //
-
- delete planeProj;
- delete lineProj;
- }
-
- //
- // Magic stuff to register this node with the database
- //
- SO_NODE_SOURCE(LineManip2);
- void
- LineManip2::initClass()
- {
- SO_NODE_INIT_CLASS(LineManip2, SoSeparator, "Separator");
- }
-
- //
- // This tiny routine is how this Node gets used. After creating a
- // LineManip2, call this routine and put a data sensor on the Coordinate3
- // returned.
- //
- SoCoordinate3 *
- LineManip2::getCoordinate3()
- {
- return coord;
- }
-
- //
- // This tiny routine is how this Node gets used. After creating a
- // LineManip2, call this routine and put a data sensor on the Coordinate3
- // returned.
- //
- void
- LineManip2::setCoordinate3( SoCoordinate3 *newNode )
- {
- if ( newNode == coord)
- return;
-
- if ( coord != NULL )
- removeChild( coord );
- if ( newNode != NULL )
- insertChild(newNode,0);
- coord = newNode;
- }
-
- //
- // For setting the plane that the manip works in. You pass it a normal.
- //
- void
- LineManip2::setPlaneNormal( const SbVec3f &newNormal )
- {
- SbVec3f zeroVec(0,0,0);
- planeNormal = newNormal;
- planeProj->setPlane( SbPlane( planeNormal, zeroVec ) );
- lineProj->setLine( SbLine( zeroVec, zeroVec + planeNormal ) );
- hilightTransform->rotation.setValue( SbRotation(SbVec3f(0,0,1), SbVec3f(planeNormal)));
- }
-
- //
- // Note:
- // This node eats events. It uses the entire surface of the render
- // area as its playground, sucking up mouse motion, left mouse and
- // backspace/delete events, even if the mouse isn't on top of it.
- //
- void
- LineManip2::handleEvent(SoHandleEventAction *ha)
- {
- myHandleEventAction = ha;
-
- const SoEvent *e = ha->getEvent();
-
- if (SO_MOUSE_PRESS_EVENT(e, BUTTON1)) {
- if (dragStart())
- ha->setHandled();
- }
- else if (e->isOfType(SoLocation2Event::getClassTypeId())) {
- // If not dragging, locate hilight
- if (ha->getGrabber() != this) {
- if (locateHilight())
- ha->setHandled();
- }
- else { // Are dragging, move coordinate:
- moveCoord();
- ha->setHandled();
- }
- }
- else if (SO_MOUSE_RELEASE_EVENT(e, BUTTON1) &&
- ha->getGrabber() == this) {
- ha->releaseGrabber();
- ha->setHandled();
- }
- else if (SO_KEY_PRESS_EVENT(e, BACKSPACE) ||
- SO_KEY_PRESS_EVENT(e, DELETE)) {
- if (remove())
- ha->setHandled();
- // If we are in mid-motion and we remove the point,
- // we better stop grabber, cause we got nothin to grab anymore.
- if (ha->getGrabber() == this)
- ha->releaseGrabber();
- current_coord = -1;
- }
- }
-
- static SbColor locateColor(0.5, 0.5, 0.5);
- static SbColor pickColor(0.9, 0.9, 0.9);
-
- //
- // Figures out which part of the line manipulator is picked by the
- // given detail. This will also set current_position and
- // current_coord.
- //
- #ifdef __C_PLUS_PLUS_2
- LineManipPart
- #else
- LineManip2::Part
- #endif
- LineManip2::whichPart()
- {
- // Can't pick if we don't have an action, now can we?
- if (myHandleEventAction == NULL)
- return NOTHING;
-
- // We're also out of luck if there's no coord node.
- if (coord == NULL)
- return NOTHING;
-
- const SoPickedPoint *picked_point = myHandleEventAction->getPickedPoint();
-
- projectMouse(current_position);
-
- if (picked_point == NULL) {
- //
- // Endpoints
- // Pick the endPoint that's closer to where the mouse is now.
- int n = coord->point.getNum();
- if (n == 0) {
- current_coord = -1;
- }
- else {
- SbVec3f v1, v2;
- v1 = current_position - coord->point[0];
- v2 = current_position - coord->point[n-1];
- //
- // Choose the endPoint closer to the current position.
- //
- if (v1.dot(v1) < v2.dot(v2)) {
- current_coord = 0;
- }
- else {
- current_coord = n;
- }
- }
- return ENDPOINTS;
- }
-
- const SoPath *dpath = picked_point->getPath();
- const SoDetail *detail = picked_point->getDetail();
-
- if (dpath->containsNode(pset)) {
- current_coord =
- ((SoPointDetail *)detail)->getCoordinateIndex();
- return POINTS;
- }
- if (dpath->containsNode(lset)) {
- current_coord =
- ((SoLineDetail *)detail)->getPartIndex();
- return LINES;
- }
- }
-
- //
- // Called when the mouse goes down to figure out what is being picked.
- //
- SbBool
- LineManip2::dragStart()
- {
- if ( coord == NULL)
- return FALSE;
-
- SbBool gotSomething = TRUE;
-
- switch(whichPart()) {
- case POINTS:
- myHandleEventAction->setGrabber(this);
- hilightVertex(coord->point[current_coord], pickColor);
- break;
-
- case LINES:
- // Create a new vertex
- coord->point.insertSpace(current_coord+1, 1);
- coord->point.set1Value(current_coord+1, current_position);
- myHandleEventAction->setGrabber(this);
- ++current_coord;
- hilightVertex(coord->point[current_coord], pickColor);
- break;
-
- case ENDPOINTS:
- // Create a new vertex
- if (current_coord != -1) {
- coord->point.insertSpace(current_coord, 1);
- }
- else current_coord = 0;
- coord->point.set1Value(current_coord, current_position);
- myHandleEventAction->setGrabber(this);
- hilightVertex(coord->point[current_coord], pickColor);
- break;
-
- case NOTHING:
- current_coord = -1;
- removeHilights();
- gotSomething = FALSE;
- break;
- }
-
- return gotSomething;
- }
-
- //
- // Called by SoHandleEventAction when we grab events
- // using myHandleEventAction->setGrabber( this );
- //
- // We will record the viewport and viewvolume.
- // This is because once we've grabbed, we can't rely on the new handleEvent
- // action to have correct view information.
- // Why? Because grabbing bypasses traversal and sends the event directly
- // to this node. The camera node has no chance to modify the state.
- //
-
- void
- LineManip2::grabEventsSetup()
- {
- extractViewingParams(myHandleEventAction);
- savedRenderCachingVal
- = (SoSeparator::CacheEnabled) renderCaching.getValue();
- renderCaching = OFF;
- }
-
-
- void
- LineManip2::grabEventsCleanup()
- {
- removeHilights();
- current_coord = -1;
-
- renderCaching = savedRenderCachingVal;
- }
-
- void
- LineManip2::extractViewingParams( SoHandleEventAction *ha )
- {
- if (ha == NULL) {
- // If the action is NULL, use default values for view info.
- myViewVolume.ortho(-1,1,-1,1,1,10);
- myVpRegion = SbViewportRegion(1,1);
- }
- else {
- // Get the view info from the action.
- SoState *state = ha->getState();
- myViewVolume = SoViewVolumeElement::get( state );
- myVpRegion = SoViewportRegionElement::get( state );
- }
- }
-
- //
- // Called when the backspace or delete keys is hit. Note that if the
- // mouse is close to the middle of a line segment (so a pick would
- // insert a point) nothing is done. This used to delete the entire
- // line segment, but that was not intuitive. That might be a good
- // interface for creating disconnected line segments, though...
- //
- SbBool
- LineManip2::remove()
- {
- if (coord == NULL)
- return FALSE;
-
- SbBool removedSomething = TRUE;
-
- if ( myHandleEventAction->getGrabber() == this ) {
- // Remove whatever we're working on.
- if (current_coord != -1)
- coord->point.deleteValues(current_coord, 1);
- }
- else {
- // Remove whatever the cursor indicates should be gone.
- switch (whichPart()) {
- case POINTS:
- coord->point.deleteValues(current_coord, 1);
- break;
-
- case LINES:
- // Do nothing.
- break;
-
- case ENDPOINTS:
- if (current_coord != -1) {
- if (current_coord != coord->point.getNum())
- coord->point.deleteValues(current_coord, 1);
- else
- coord->point.deleteValues(current_coord-1, 1);
- }
- break;
-
- case NOTHING:
- removedSomething = FALSE;
- break;
- }
- }
-
- removeHilights();
-
- return removedSomething;
- }
-
- //
- // Called when the mouse is moving around, but the left mouse button
- // is up.
- //
- SbBool
- LineManip2::locateHilight()
- {
- if (coord == NULL )
- return FALSE;
-
- SbBool hilitSomething = TRUE;
-
- switch (whichPart()) {
- case POINTS:
- hilightVertex(coord->point[current_coord], locateColor);
- break;
-
- case LINES:
- hilightLine(current_coord, current_position, locateColor);
- break;
-
- case ENDPOINTS:
- if (current_coord == -1) {
- // There are no coordinates at all. We can't perform a
- // hilight.
- hilitSomething = FALSE;
- }
- else if (current_coord == 0)
- hilightLine(-1, current_position, locateColor);
- else
- hilightLine(current_coord-1, current_position, locateColor);
- break;
-
- case NOTHING:
- hilitSomething = FALSE;
- break;
- }
-
- return hilitSomething;
- }
-
- //
- // Called to move the selected point when the mouse is moved and the
- // left mouse button is pressed.
- //
- void
- LineManip2::moveCoord()
- {
- if (coord == NULL)
- return;
-
- assert(current_coord > -1 && current_coord < coord->point.getNum());
- updateProjectors( coord->point[current_coord] );
- SbVec3f t;
- projectMouse(t);
- coord->point.set1Value(current_coord, t);
-
- hilightVertex(coord->point[current_coord], pickColor);
- }
-
- void
- LineManip2::updateProjectors( const SbVec3f &curPt )
- {
- SbVec3f dir = lineProj->getLine().getDirection();
- lineProj->setLine( SbLine( curPt, curPt + dir ) );
-
- dir = planeProj->getPlane().getNormal();
- planeProj->setPlane( SbPlane( dir, curPt ) );
- }
-
- //
- // Does the grunt work of projecting the mouse onto the plane
- // containing the line manipulator.
- //
- void
- LineManip2::projectMouse(SbVec3f &result)
- {
- const SoEvent *e = myHandleEventAction->getEvent();
-
- // If we're grabbing, we've already got our view parameters.
- // If not, then we'd better get them now.
- // NOTE: If you're grabbing and you try to get view parameters, you'll
- // get a bad answer. Grabbing causes you to skip over camera
- // nodes directly to this one. So the state has no valid view info.
- if ( myHandleEventAction->getGrabber() != this )
- extractViewingParams(myHandleEventAction);
-
- // If alt key is down, then we move in line perpendicular to plane
- if ( e->wasAltDown() == FALSE ) {
- planeProj->setViewVolume(myViewVolume);
- result = planeProj->project(e->getNormalizedPosition(myVpRegion));
- }
- else {
- lineProj->setViewVolume(myViewVolume);
- result = lineProj->project(e->getNormalizedPosition(myVpRegion));
- }
- }
-