From Mike Connell, "Here are some small fixes that allow you to specify the allowable deviation when creating polylines from arcs and circles in DXF files. Changes are against 2.8.0

It adds two options:

Accuracy(x) - ensures the polyline will be within x units from the ideal arc/curve
ImproveAccuracyOnly - do not use the given accuracy 'x', if it would result in a worse curve than with the previous (2.8.0) implementation for a particular arc/curve.

As an added bonus there was a small bug in the existing implementation whereby the primitives were line strips but the vertices generated were actually suitable for GL_LINES, so the improved accuracy doesn't even have to come at a performance cost :-)"
This commit is contained in:
Robert Osfield
2009-04-08 13:21:59 +00:00
parent 69181e1697
commit fa27223fcd
3 changed files with 129 additions and 32 deletions

View File

@@ -16,6 +16,8 @@
#include "dxfBlock.h"
#include "codeValue.h"
#include <osg/io_utils> // just for debugging
using namespace std;
using namespace osg;
@@ -173,27 +175,41 @@ dxfCircle::drawScene(scene* sc)
getOCSMatrix(_ocs, m);
sc->ocs(m);
std::vector<Vec3d> vlist;
int numsteps = 360/5; // baaarghf.
double angle_step = osg::DegreesToRadians((double)360.0 / (double) numsteps);
double angle1 = 0.0f;
double angle2 = 0.0f;
double theta=5.0; // we generate polyline from "spokes" at theta degrees at arc's center
if (_useAccuracy) {
// we generate points on a polyline where each point lies on the arc, thus the maximum error occurs at the midpoint of each line segment where it lies furthest inside the arc
// If we divide the segment in half and connect the bisection point to the arc's center, we have two rightangled triangles with
// one side=r-maxError, hypotenuse=r, and internal angle at center is half the angle we will step with:
double maxError=min(_maxError,_radius); // Avoid offending acos() in the edge case where allowable deviation is greater than radius.
double newtheta=acos( (_radius-maxError) / _radius);
newtheta=osg::RadiansToDegrees(newtheta)*2.0;
// Option to only use the new accuracy code when it would improve on the accuracy of the old method
if (_improveAccuracyOnly) {
theta=min(newtheta,theta);
} else {
theta=newtheta;
}
}
theta=osg::DegreesToRadians(theta);
// We create an anglestep<=theta so that the line's points are evenly distributed around the circle
unsigned int numsteps=floor(osg::PI*2/theta);
if (numsteps<3) numsteps=3; // Sanity check: minimal representation of a circle is a tri
double anglestep=osg::PI*2/numsteps;
double angle1 = 0.0;
Vec3d a = _center;
Vec3d b,c;
for (int r = 0; r < numsteps; r++)
{
angle1 = angle2;
if (r == numsteps - 1)
angle2 = 0.0f;
else
angle2 += angle_step;
Vec3d b;
for(unsigned int r=0;r<=numsteps;r++) {
b = a + Vec3d(_radius * (double) sin(angle1), _radius * (double) cos(angle1), 0);
c = a + Vec3d(_radius * (double) sin(angle2), _radius * (double) cos(angle2), 0);
// vlist.push_back(a);
angle1 += anglestep;
vlist.push_back(b);
vlist.push_back(c);
}
sc->addLineStrip(getLayer(), _color, vlist);
// sc->addTriangles(getLayer(), _color, vlist);
sc->addLineStrip(getLayer(), _color, vlist); // Should really add LineLoop implementation and save a vertex
sc->ocs_clear();
}
@@ -253,25 +269,46 @@ dxfArc::drawScene(scene* sc)
start = _startAngle;
end = _endAngle;
}
double theta=5.0; // we generate polyline from "spokes" at theta degrees at arc's center
if (_useAccuracy) {
// we generate points on a polyline where each point lies on the arc, thus the maximum error occurs at the midpoint of each line segment where it lies furthest inside the arc
// If we divide the segment in half and connect the bisection point to the arc's center, we have two rightangled triangles with
// one side=r-maxError, hypotenuse=r, and internal angle at center is half the angle we will step with:
double maxError=min(_maxError,_radius); // Avoid offending acos() in the edge case where allowable deviation is greater than radius.
double newtheta=acos( (_radius-maxError) / _radius);
newtheta=osg::RadiansToDegrees(newtheta)*2.0;
//cout<<"r="<<_radius<<" _me="<<_maxError<<" (_radius-_maxError)="<<(_radius-_maxError)<<" newtheta="<<newtheta<<endl;
// Option to only use the new accuracy code when it would improve on the accuracy of the old method
if (_improveAccuracyOnly) {
theta=min(newtheta,theta);
} else {
theta=newtheta;
}
}
double angle_step = DegreesToRadians(end - start);
int numsteps = (int)((end - start)/5.0); // hurmghf. say 5 degrees?
if (numsteps * 5 < (end - start)) numsteps++;
int numsteps = (int)((end - start)/theta);
//cout<<"arc theta="<<osg::RadiansToDegrees(theta)<<" end="<<end<<" start="<<start<<" numsteps="<<numsteps<<" e-s/theta="<<((end-start)/theta)<<" end-start="<<(end-start)<<endl;
if (numsteps * theta < (end - start)) numsteps++;
numsteps=max(numsteps,2); // Whatever else, minimum representation of an arc is a straightline
angle_step /= (double) numsteps;
end = DegreesToRadians((-_startAngle)+90.0);
start = DegreesToRadians((-_endAngle)+90.0);
double angle1 = 0.0f;
double angle2 = (start);
double angle1 = start;
Vec3d a = _center;
Vec3d b,c;
for (int r = 0; r < numsteps; r++)
Vec3d b;
for (int r = 0; r <= numsteps; r++)
{
angle1 = angle2;
angle2 = angle1 + angle_step;
b = a + Vec3d(_radius * (double) sin(angle1), _radius * (double) cos(angle1), 0);
c = a + Vec3d(_radius * (double) sin(angle2), _radius * (double) cos(angle2), 0);
angle1 += angle_step;
vlist.push_back(b);
vlist.push_back(c);
}
sc->addLineStrip(getLayer(), _color, vlist);
sc->ocs_clear();
}