# Section 31 AutoCAD operator - orthogonal operator

Posted by corylulu on Fri, 24 Dec 2021 23:50:16 +0100

# function

This section simulates an AutoCAD operator. It is found that many netizens are using OSG to do CAD related operations, and there are many problems about using orthogonal operators. The functions of this paper are as follows:

• Draw a wireframe as the background
• Orthogonal projection
• With the mouse wheel up and down, you can zoom in and out of the wireframe
• Click a/d to move left / right to the viewpoint
• Click the q key to rotate the wireframe counterclockwise

The contents of this section are in the network disk:

Please use the browser to open it. If you encounter problems or add groups, you can also add me on wechat: 13324598743:
Link: https://pan.baidu.com/s/13gwJLwo_LbRnN3Bl2NXXXw
Extraction code: xrf5

# principle

## viewpoint

First of all, we should understand that the viewpoint position and projection are two different things. The position of the viewpoint is determined by three elements_ eye position, _center, _up

The projection is different. The projection is how you see the world. People's eyes are perspective projection from near to far. We won't talk about it. The projection to be used in this paper should be orthogonal 2D projection, that is, after the eye position is determined, only the data in such a large range as left right and bottom up of the eye are taken, and there is no near large and far small. All the data in this range are superimposed together.

Imagine an infinite square pipe from the viewpoint, and all the objects in the pipe are stacked on one surface, which is the result of orthogonal projection display. Of course, orthogonal projection can also set far near, that is, it is not an infinite square pipe. There is not much explanation here. No, the default is infinite.

In this paper, we still define the operator as follows:

```		_center = osg::Vec3(0.0, 0.0, 0.0);
_up = osg::Vec3(0.0, 0.0, 1.0);
_eye = osg::Vec3(0.0, -10.0, 0.0);
```

That is, by default, we stand at - 10 on the y axis and look at the origin. Here we want to have the concept of space, x-axis screen right, y-axis screen, z-axis screen.

It can be considered that at present, we are standing at a distance from the screen 10, looking towards the screen, and our head is facing above the screen. As shown in the figure below, x is on the right side of the screen, z is on the screen, and the position of the lion is in the direction of the negative y axis, that is, the y axis faces into the screen.

## Projection

For orthogonal projection, the parameters are particularly simple, that is, left, right, bottom and top. This is relative to the viewpoint. Imagine that the viewpoint is at the middle (0, 0) point of the pipeline, left is the left wall of the pipeline, right is the right wall, bottom is the lower wall, and top is the upper wall.

Because the grid we draw is drawn on the xz plane, one line per meter, a total of 20 lines, starting from (0,0) to (19,19), and the viewpoint is at (0,0), so we set left, right, top and bottom as follows:

```		_l = -2.0;
_r = 2.0;
_b = -2.0;
_t = 2.0;
```

You will only see two grids:

## Mouse wheel

The processing of wheel events is the simplest. We only need to redefine the range of left, right, top and bottom. Make it bigger or smaller.

## rotate

Rotation is actually changing the direction of up, which we define_ theta, every time q we click, we add 1. Then rotate the initial up = osg::Vec3(0.0, 0.0, 1.0) around the y axis_ theta.

## translation

This example only deals with a left shift and d right shift. When it does not rotate, the viewpoint moves left_ deltaEye=0.3 so far. When the mouse wheel, because the field of view becomes larger_ deltaEye will become larger and smaller along with left and right. This is not difficult to understand.

If we don't rotate, we just translate point a to the left. It's like this:

We just need to subtract deltaEye from the position of eye along the x direction. This is the code I Annotated:
//_eye += osg::Vec3(-_deltaEye, 0.0, 0.0);

So now the rotation is complicated. When we click q, the rotation_ theta degree, assuming that it is within 0 ~ 90 degrees, the situation changes as follows:

The thick line above is to walk_ deltaEye, rotation angle_ After theta, the red line shows its changes in the x direction and y direction. Within 0 ~ 90 degrees, sin and cos are positive, so the position of the new eye is:

```_eye += osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));
```

Once eye is changed, so will center

```_center = osg::Vec3(_eye.x(), 0.0, _eye.z());
```

That is, always stand outside the screen and look inside the screen. That's how you move. Because the y value does not change, y of eye is always - 10 and always looks at y=0

In other cases, the reader can push it by himself. Is the formula the same when rotating [90, 360]? And w up and s down, users can write by themselves. Or drag the mouse, users can write by themselves.

### All codes:

```#include <osgViewer/Viewer>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osgGA/CameraManipulator>

class MyCameraManipulator : public osgGA::CameraManipulator
{
public:
MyCameraManipulator()
{
_theta = 0.0;
_center = osg::Vec3(0.0, 0.0, 0.0);
_up = osg::Vec3(0.0, 0.0, 1.0);
_eye = osg::Vec3(0.0, -10.0, 0.0);

_deltaEye = 0.3;

_l = -2.0;
_r = 2.0;
_b = -2.0;
_t = 2.0;
}

//These three pure virtual functions will not be used in this example
virtual void setByMatrix(const osg::Matrixd& matrix) {};
virtual void setByInverseMatrix(const osg::Matrixd& matrix) {};
virtual osg::Matrixd getMatrix() const { return osg::Matrix::identity(); };

//The key is this, which returns the ViewMatrix
virtual osg::Matrixd getInverseMatrix() const
{
return osg::Matrix::lookAt(_eye, _center, _up);
};

//For event handling, we need to click A to rotate clockwise around the Z axis, and point D to rotate counterclockwise. When we rotate, we always look at point 0
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us)
{
if (ea.getEventType() == osgGA::GUIEventAdapter::FRAME)
{
us.asView()->getCamera()->setProjectionMatrixAsOrtho2D(_l, _r, _b, _t);
}

if (ea.getEventType() == osgGA::GUIEventAdapter::SCROLL)
{
//Determine the direction of scrolling
osgGA::GUIEventAdapter::ScrollingMotion sm = ea.getScrollingMotion();

if (sm == osgGA::GUIEventAdapter::SCROLL_DOWN)
{
_deltaEye *= 1.1;

_l *= 1.1;
_t *= 1.1;
_b *= 1.1;
_r *= 1.1;

}
else
{
//The range becomes smaller by 0.1, which is just the opposite of the larger range, and the left and right shrink inward at the same time
_deltaEye *= 0.9;

_l *= 0.9;
_t *= 0.9;
_b *= 0.9;
_r *= 0.9;
}
}

if (ea.getEventType() == osgGA::GUIEventAdapter::KEYDOWN)
{

//Rotation angle
if ((ea.getKey() == 'Q') || (ea.getKey() == 'q'))
{
//Point q changes by 1 degree
_theta += 1;

_up = osg::Vec3(0.0, 0.0, 1.0)*osg::Matrix::rotate(osg::inDegrees(_theta), osg::Y_AXIS);
}

//If it's A key
if ((ea.getKey() == 'A') || (ea.getKey() == 'a'))
{

//_eye += osg::Vec3(-_deltaEye, 0.0, 0.0);
_eye += osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));

_center = osg::Vec3(_eye.x(), 0.0, _eye.z());
}
if ((ea.getKey() == 'D') || (ea.getKey() == 'd'))
{
//_eye += osg::Vec3(_deltaEye, 0.0, 0.0);

_eye -= osg::Vec3(-_deltaEye*std::cos(osg::inDegrees(_theta)), 0.0, _deltaEye*std::sin(osg::inDegrees(_theta)));

_center = osg::Vec3(_eye.x(), 0.0, _eye.z());
}

}
return false;
}

//Viewpoint position
osg::Vec3d              _eye;

//Click the mouse a button to move the measurement to the left. As the mouse wheel zooms in and out, the measurement also changes
double _deltaEye;

//Where does the viewpoint look
osg::Vec3d              _center;
//Overhead orientation
osg::Vec3d              _up;

//The angle from the viewpoint to 0
float              _theta;

//2D projection parameters, left, right, bottom, top
double _l, _r, _b, _t;
};

osg::Geode* createNet()
{
osg::Geode* gnode = new osg::Geode;

osg::Geometry* geom = new osg::Geometry;
gnode->addDrawable(geom);

//Sets the color of the line to white
osg::Vec4Array* color = new osg::Vec4Array;
color->push_back(osg::Vec4(1.0, 1.0, 1.0, 1.0));
geom->setColorArray(color, osg::Array::BIND_OVERALL);

osg::Vec3Array* vertex = new osg::Vec3Array;
geom->setVertexArray(vertex);

//At an interval of 1m, there are 20 horizontal and 20 vertical
for (int i = 0; i < 20; i++)
{
//x direction
vertex->push_back(osg::Vec3(0, 0, i));
vertex->push_back(osg::Vec3(17, 0, i)); //Set to 17, not full

//z direction
vertex->push_back(osg::Vec3(i, 0, 0));
vertex->push_back(osg::Vec3(i, 0, 17));
}

geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, vertex->size()));
geom->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

return gnode;
}

int main()
{
osgViewer::Viewer viewer;
viewer.setSceneData(createNet());
viewer.setCameraManipulator(new MyCameraManipulator());
return viewer.run();
}
```

Topics: osg OpenSceneGraph