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

@ -17,8 +17,10 @@
#include <map>
#include <iostream>
#include <utility>
#include <string>
#include <sstream>
#include <string.h>
#include "dxfFile.h"
@ -46,10 +48,38 @@ REGISTER_OSGPLUGIN(dxf, ReaderWriterdxf)
// read file and convert to OSG.
osgDB::ReaderWriter::ReadResult
ReaderWriterdxf::readNode(const std::string& filename, const osgDB::ReaderWriter::Options*) const
ReaderWriterdxf::readNode(const std::string& filename, const osgDB::ReaderWriter::Options* options) const
{
std::string ext = osgDB::getFileExtension(filename);
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
// extract accuracy options if available
if (options) {
bool useAccuracy=false; // if we specify accuracy of curve rendering or not
double maxError=0.0; // if useAccuracy - the accuracy (max deviation) from the arc
bool improveAccuracyOnly=false; // if true only use the given accuracy if it would improve the curve compared to the previous implementation
// Thus you can ensure that large curves get rendered better but small ones don't get worse
std::string optionsstring=options->getOptionString();
size_t accstart=optionsstring.find("Accuracy(");
if (accstart>=0) {
const char* start=optionsstring.c_str() + accstart + strlen("Accuracy(");
if (sscanf(start,"%lf",&maxError)==1) useAccuracy=true;
}
if (useAccuracy) {
// Option to only use the new accuracy code when it would improve on the accuracy of the old method
if (optionsstring.find("ImproveAccuracyOnly") != std::string::npos) {
improveAccuracyOnly=true;
}
// Pull out the initial dxfArc copy from the registry and set accuracy there.
// When actual dxfArcs/Circles are created they will inherit these parameters from the exemplar
dxfEntity::getRegistryEntity("ARC")->setAccuracy(true,maxError,improveAccuracyOnly);
dxfEntity::getRegistryEntity("CIRCLE")->setAccuracy(true,maxError,improveAccuracyOnly);
} // accuracy options exists
} // options exist
// Open
dxfFile df(filename);
if (df.parseFile()) {

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();
}

View File

@ -60,16 +60,29 @@ getOCSMatrix(const osg::Vec3d& ocs, osg::Matrixd& m)
class dxfBasicEntity : public osg::Referenced
{
public:
dxfBasicEntity() : _color(0) {}
dxfBasicEntity() : _color(0), _useAccuracy(false), _maxError(0.01), _improveAccuracyOnly(false) {}
virtual ~dxfBasicEntity() {}
virtual dxfBasicEntity* create() = 0;
virtual const char* name() = 0;
virtual void assign(dxfFile* dxf, codeValue& cv);
virtual void drawScene(scene*) {}
const std::string getLayer() const { return _layer; }
void setAccuracy(bool useAccuracy,double maxError,bool improveAccuracyOnly) {
_useAccuracy=useAccuracy;
_maxError=maxError;
_improveAccuracyOnly=improveAccuracyOnly;
}
protected:
std::string _layer;
unsigned short _color;
bool _useAccuracy; // true to specify a maximum deviation for curve rendering
double _maxError; // the error in model units, if _useAccuracy==true
bool _improveAccuracyOnly;// if true only use _maxError where it would increase the quality of curves compared to the previous algorithm
};
@ -78,7 +91,11 @@ class dxfCircle : public dxfBasicEntity
public:
dxfCircle() : _radius(0), _ocs(0,0,1) {}
virtual ~dxfCircle() {}
virtual dxfBasicEntity* create() { return new dxfCircle; }
virtual dxfBasicEntity* create() { // we create a copy which uses our accuracy settings
dxfBasicEntity* circle=new dxfCircle;
circle->setAccuracy(_useAccuracy,_maxError,_improveAccuracyOnly);
return circle;
}
virtual const char* name() { return "CIRCLE"; }
virtual void assign(dxfFile* dxf, codeValue& cv);
virtual void drawScene(scene* sc);
@ -93,7 +110,12 @@ class dxfArc : public dxfBasicEntity
public:
dxfArc() : _radius(0), _startAngle(0), _endAngle(360), _ocs(0,0,1) {}
virtual ~dxfArc() {}
virtual dxfBasicEntity* create() { return new dxfArc; }
virtual dxfBasicEntity* create() { // we create a copy which uses our accuracy settings
dxfBasicEntity* arc=new dxfArc;
arc->setAccuracy(_useAccuracy,_maxError,_improveAccuracyOnly);
//std::cout<<"dxfArc::create with _useAccuracy="<<_useAccuracy<<" maxError="<<_maxError<<" improveAccuracyOnly="<<_improveAccuracyOnly<<std::endl;
return arc;
}
virtual const char* name() { return "ARC"; }
virtual void assign(dxfFile* dxf, codeValue& cv);
virtual void drawScene(scene* sc);
@ -304,7 +326,7 @@ protected:
class dxfEntity : public osg::Referenced
{
public:
dxfEntity(std::string s) : _entity(NULL), _seqend(false)
dxfEntity(std::string s) : _entity(NULL), _seqend(false)
{
_entity = findByName(s);
if (_entity) {
@ -329,11 +351,19 @@ public:
virtual void drawScene(scene* sc);
dxfBasicEntity* getEntity() { return _entity; }
// Returns the exemplar from the registry - all other entities of this type are created by this one via entity->create
static dxfBasicEntity* getRegistryEntity(std::string s) {
return _registry[s].get();
}
protected:
std::vector<osg::ref_ptr<dxfBasicEntity> > _entityList;
static std::map<std::string, osg::ref_ptr<dxfBasicEntity> > _registry;
dxfBasicEntity* _entity;
bool _seqend; // bypass 0 codes. needs a 0 seqend to close.
};
/** Proxy class for automatic registration of dxf entities reader/writers.*/