I am looking for a deterministic threadsafe Rcpp algorithm for 2-D numerical integration. RcppNumerical provides a partial interface to Cuba for multidimensional integration, but from my trials that appears not to be threadsafe in RcppParallel, and it probably uses a Monte Carlo method. That throws me back on repeated 1-dimensional integration. I have used this successfully with the (not threadsafe) R function Rdqags, but my (possibly naive) coding for RcppNumerical fails to compile because the nested class is abstract. Perhaps due to the operator() virtual function.
Can anyone suggest a way around this in RcppNumerical, or some alternative?
My test code emulating the 2-D example from https://github.com/yixuan/RcppNumerical is below. It gives errors like
cannot declare variable 'f2' to be of abstract type 'Normal2'
cannot declare variable 'f1' to be of abstract type 'Normal1'
Murray
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::depends(RcppNumerical)]]
#include <RcppNumerical.h>
using namespace Numer;
// P(a1 < X1 < b1, a2 < X2 < b2), (X1, X2) ~ N([0], [1 rho])
// ([0], [rho 1])
class Normal2: public Func
{
private:
const double rho;
const double x;
double const1; // 2 * (1 - rho^2)
double const2; // 1 / (2 * PI) / sqrt(1 - rho^2)
public:
Normal2(const double& rho_, const double& x_) : rho(rho_), x(x_)
{
const1 = 2.0 * (1.0 - rho * rho);
const2 = 1.0 / (2 * M_PI) / std::sqrt(1.0 - rho * rho);
}
// PDF of bivariate normal
double operator()(const double& y)
{
double z = x * x - 2 * rho * x * y + y * y;
return const2 * std::exp(-z / const1);
}
};
class Normal1: public Func
{
private:
const double rho;
double a2, b2;
public:
Normal1(const double& rho_, const double& a2_, const double& b2_) : rho(rho_), a2(a2_), b2(b2_) {}
// integral in y dimension for given x
double operator()(const double& x)
{
Normal2 f2(rho, x);
double err_est;
int err_code;
const double res = integrate(f2, a2, b2, err_est, err_code);
return res;
}
};
// [[Rcpp::export]]
Rcpp::List integrate_test3()
{
double a1 = -1.0;
double b1 = 1.0;
double a2 = -1.0;
double b2 = 1.0;
Normal1 f1(0.5, a2, b2); // rho = 0.5
double err_est;
int err_code;
const double res = integrate(f1, a1, b1, err_est, err_code);
return Rcpp::List::create(
Rcpp::Named("approximate") = res,
Rcpp::Named("error_estimate") = err_est,
Rcpp::Named("error_code") = err_code
);
}
The Numer::Func class is an abstract class because of one undefined method:
virtual double operator()(const double& x) const = 0;
Now you are providing an implementation for
double operator()(const double& x)
which leaves the above method undefined and hence the class abstract. You should change this to
double operator()(const double& x) const
for both Normal1 and Normal2 to have your code compile.
BTW, my compiler (gcc 9.2) is even quite explicit about this problem:
59094915.cpp: In member function ‘double Normal1::operator()(const double&)’:
59094915.cpp:43:17: error: cannot declare variable ‘f2’ to be of abstract type ‘Normal2’
43 | Normal2 f2(rho, x);
| ^~
59094915.cpp:9:7: note: because the following virtual functions are pure within ‘Normal2’:
9 | class Normal2: public Func
| ^~~~~~~
In file included from /usr/local/lib/R/site-library/RcppNumerical/include/integration/wrapper.h:13,
from /usr/local/lib/R/site-library/RcppNumerical/include/RcppNumerical.h:16,
from 59094915.cpp:3:
/usr/local/lib/R/site-library/RcppNumerical/include/integration/../Func.h:26:20: note: ‘virtual double Numer::Func::operator()(const double&) const’
26 | virtual double operator()(const double& x) const = 0;
| ^~~~~~~~
59094915.cpp: In function ‘Rcpp::List integrate_test3()’:
59094915.cpp:58:13: error: cannot declare variable ‘f1’ to be of abstract type ‘Normal1’
58 | Normal1 f1(0.5, a2, b2); // rho = 0.5
| ^~
59094915.cpp:32:7: note: because the following virtual functions are pure within ‘Normal1’:
32 | class Normal1: public Func
| ^~~~~~~
In file included from /usr/local/lib/R/site-library/RcppNumerical/include/integration/wrapper.h:13,
from /usr/local/lib/R/site-library/RcppNumerical/include/RcppNumerical.h:16,
from 59094915.cpp:3:
/usr/local/lib/R/site-library/RcppNumerical/include/integration/../Func.h:26:20: note: ‘virtual double Numer::Func::operator()(const double&) const’
26 | virtual double operator()(const double& x) const = 0;
| ^~~~~~~~
Related
I have been experimenting with the RcppArrayFire Package, mostly rewriting some cost functions from RcppArmadillo and can't seem to get over "no viable conversion from 'af::array' to 'float'. I have also been getting some backend errors, the example below seems free of these.
This cov-var example is written poorly just to use all relevant coding pieces from my actual cost function. As of now it is the only addition in a package generated by, "RcppArrayFire.package.skeleton".
#include "RcppArrayFire.h"
#include <Rcpp.h>
// [[Rcpp::depends(RcppArrayFire)]]
// [[Rcpp::export]]
float example_ols(const RcppArrayFire::typed_array<f32>& X_vect, const RcppArrayFire::typed_array<f32>& Y_vect){
int Len = X_vect.dims()[0];
int Len_Y = Y_vect.dims()[0];
while( Len_Y < Len){
Len --;
}
float mean_X = af::sum(X_vect)/Len;
float mean_Y = af::sum(Y_vect)/Len;
RcppArrayFire::typed_array<f32> temp(Len);
RcppArrayFire::typed_array<f32> temp_x(Len);
for( int f = 0; f < Len; f++){
temp(f) = (X_vect(f) - mean_X)*(Y_vect(f) - mean_Y);
temp_x(f) = af::pow(X_vect(f) -mean_X, 2);
}
return af::sum(temp)/af::sum(temp_x);
}
/*** R
X <- 1:10
Y <- 2*X +rnorm(10, mean = 0, sd = 1)
example_ols(X, Y)
*/
The first thing to consider is the af::sum function, which comes in different forms: An sf::sum(af::array) that returns an af::array in device memory and a templated af::sum<T>(af::array) that returns a T in host memory. So the minimal change to your example would be using af::sum<float>:
#include "RcppArrayFire.h"
#include <Rcpp.h>
// [[Rcpp::depends(RcppArrayFire)]]
// [[Rcpp::export]]
float example_ols(const RcppArrayFire::typed_array<f32>& X_vect,
const RcppArrayFire::typed_array<f32>& Y_vect){
int Len = X_vect.dims()[0];
int Len_Y = Y_vect.dims()[0];
while( Len_Y < Len){
Len --;
}
float mean_X = af::sum<float>(X_vect)/Len;
float mean_Y = af::sum<float>(Y_vect)/Len;
RcppArrayFire::typed_array<f32> temp(Len);
RcppArrayFire::typed_array<f32> temp_x(Len);
for( int f = 0; f < Len; f++){
temp(f) = (X_vect(f) - mean_X)*(Y_vect(f) - mean_Y);
temp_x(f) = af::pow(X_vect(f) -mean_X, 2);
}
return af::sum<float>(temp)/af::sum<float>(temp_x);
}
/*** R
set.seed(1)
X <- 1:10
Y <- 2*X +rnorm(10, mean = 0, sd = 1)
example_ols(X, Y)
*/
However, there are more things one can improve. In no particular order:
You don't need to include Rcpp.h.
There is an af::mean function for computing the mean of an af::array.
In general RcppArrayFire::typed_array<T> is only needed for getting arrays from R into C++. Within C++ and for the way back you can use af::array.
Even when your device does not support double, you can still use double values on the host.
In order to get good performance, you should avoid for loops and use vectorized functions, just like in R. You have to impose equal dimensions for X and Y, though.
Interestingly I get a different result when I use vectorized functions. Right now I am not sure why this is the case, but the following form makes more sense to me. You should verify that the result is what you want to get:
#include <RcppArrayFire.h>
// [[Rcpp::depends(RcppArrayFire)]]
// [[Rcpp::export]]
double example_ols(const RcppArrayFire::typed_array<f32>& X_vect,
const RcppArrayFire::typed_array<f32>& Y_vect){
double mean_X = af::mean<double>(X_vect);
double mean_Y = af::mean<double>(Y_vect);
af::array temp = (X_vect - mean_X) * (Y_vect - mean_Y);
af::array temp_x = af::pow(X_vect - mean_X, 2.0);
return af::sum<double>(temp)/af::sum<double>(temp_x);
}
/*** R
set.seed(1)
X <- 1:10
Y <- 2*X +rnorm(10, mean = 0, sd = 1)
example_ols(X, Y)
*/
BTW, an even shorter version would be:
#include <RcppArrayFire.h>
// [[Rcpp::depends(RcppArrayFire)]]
// [[Rcpp::export]]
af::array example_ols(const RcppArrayFire::typed_array<f32>& X_vect,
const RcppArrayFire::typed_array<f32>& Y_vect){
return af::cov(X_vect, Y_vect) / af::var(X_vect);
}
Generally it is a good idea to use the in-build functions as much as possible.
I have a few questions about setting up NLopt with non-linear constraints:
If the number of constraints is bigger than the number of variables, how can we set grad[ ] in the constraint function? Is there any (automatic) method to solve the problem without introducing Lagrangian multiplier?
Using a Lagrangian multiplexer, I know we can solve the problem. However the use of Lagrangian multiplexer we have to obtain my_constraint_data manually, which make it difficult to solve large-scale problem.
For example, suppose I want to minimize the function
f(x1,x2) = -((x1)^3)-(2*(x2)^2)+(10*(x1))-6-(2*(x2)^3)
subject to the following constraints:
Constraint 1: c1 = 10-(x1)*(x2) >= 0
Constraint 2: c2 = ((x1)*(x2)^2)-5 >= 0
Constraint 3: c3 = (x2)-(x1)*(x2)^3 >= 0
In NLopt tutorial, we know that grad[0] = d(c1)/d(x1) and grad[1] = d(c2)/d(x2) as the gradient of constraints. Then, we set grad as follows:
double myconstraint(unsigned n, const double *x, double *grad, void *data) {
my_constraint_data *d = (my_constraint_data *)data;
if (grad) {
grad[0] = -x[1]; //grad[0] = d(c1)/dx[1]
grad[1] = 2*x[0]+x[1]; //grad[1] = d(c2)/dx[2]
grad[2] = ???; //grad[2] = d(c3)/dx[3] but we only have 2 variable (x1)&(x2)
}
return (10-x[0]*x[1], x[0]*x[1]*x[1]-5, x[1]-x[0]*x[1]*x[1]*x[1];
}
The problem is we do not know how to set grad[ ] (especially for c3) if the number of constraints are larger than the number of variables.
Of course we can solve the problem with non-automatic method like below by using Lagrangian multiplexer (l1, l2, l3) where
grad[0] = -l1*(d(c1)/d(x1))-l2*(d(c2)/d(x1))-l3*(d(c)/d(x1))
and
grad[1] = -l1*(d(c1)/d(x2))-l2*(d(c2)/d(x2))-l3*(d(c)/d(x3))
double myconstraint(unsigned n, const double *x, double *grad, void *data) {
my_constraint_data *d = (my_constraint_data *)data;
//set l1, l2, and l3 as parameter of lagrangian multiplier
double l1=d->l1,l2=d->l2,l3=d->l3;
++count;
if (grad) {
grad[0] = l1*x[1]-l2*x[1]*x[1]-l3*x[1]*x[1]*x[1];
grad[1] = l1*x[0]-2*l2*x[0]*x[1]-l3+3*l3*x[0]*x[1]*x[1];
}
return (10-x[0]*x[1], x[0]*x[1]*x[1]-5, x[1]-x[0]*x[1]*x[1]*x[1]);
}
Meanwhile, it is not easy to apply non-automatic method into large-scale problem because it will be inefficient and complicated in programming.
Is there any method to solve nonlinear simultaneous equations using NLopt? (When Lagrangian multiplexer is applied in case of the number of constraints are larger than the number of variables, nonlinear simultaneous equations should be solved.).
We appreciate for your answer. It will be really helpful to us. Thank you for all your kindness.
I think you've got the constraints and the variables you are minimizing mixed up. If I understand your question correctly, you need to create three separate constraint functions for your three constraints. For example:
double c1(unsigned n, const double *x, double *grad, void *data)
{
/* Enforces the constraint
*
* 10 - x1*x2 >= 0
*
* Note we compute x1*x2 - 10 instead of 10 - x1*x2 since nlopt expects
* inequality constraints to be of the form h(x) <= 0. */
if (grad) {
grad[0] = x[1]; // grad[0] = d(c1)/dx1
grad[1] = x[0]; // grad[1] = d(c1)/dx2
}
return x[0]*x[1] - 10;
}
double c2(unsigned n, const double *x, double *grad, void *data)
{
/* Enforces the constraint
*
* x1*x2^2 - 5 >= 0
*
* Note we compute -x1*x2^2 - 5 instead of x1*x2^2 - 5 since nlopt expects
* inequality constraints to be of the form h(x) <= 0. */
if (grad) {
grad[0] = -x[1]*x[1];
grad[1] = -2*x[0]*x[1];
}
return -x[0]*x[1]*x[1] + 5;
}
Then, in your main function you need to add each inequality constraint separately:
int main(int argc, char **argv)
{
// set up nlopt here
/* Add our constraints. */
nlopt_add_inequality_constraint(opt, c1, NULL, 1e-8);
nlopt_add_inequality_constraint(opt, c2, NULL, 1e-8);
// etc.
}
My aim is to solve a system of differential equations using Rcpp. Basically I want to set up a system as shown in the code below (modification of the code example found here: How to use C++ ODE solver with Rcpp in R?).
At the moment the code below integrates a set of odes in the time intervall 0 to 10. For the entire time params[0] is -100, and parms[1] = 10. However, my aim is to set up a system where parms[0] and parms[1] are only constant over a subset of the time intervall. E.g. for the time intervall 0-5 parms[0] should be set to 1 and for the remaining time parms[0]should be 10.
Actually, I have almost no experience in c++/rcpp. Thus, I have no idea how to set up such a system. Could you please give me a hint how I should construct the ode system. Thank you very much in advance for any advice how to solve this problem.
i save the code below in a cpp file and call it with sourceCpp in R:
#include <Rcpp.h>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace Rcpp;
using namespace std;
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
typedef boost::array< double ,2 > parms_type;
double time = 10;
parms_type parms = {-100, 10};
void rhs( const state_type &x , state_type &dxdt , const double t) {
dxdt[0] = parms[0]/(2.0*t*t) + x[0]/(2.0*t);
dxdt[1] = parms[1]/(2.0*t*t) + x[1]/(2.0*t);
dxdt[2] = parms[1]/(2.0*t*t) + x[1]/(2.0*t);
}
void write_cout( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool boostExample() {
state_type x = { 1.0 , 1.0, 1.0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
rhs , x , 1.0 , time, 0.1 , write_cout );
return true;
}
Your code does not compile for me:
boost-ode.cpp:11:8: error: ‘double time’ redeclared as different kind of symbol
double time = 10.0;
^~~~
In file included from /usr/include/pthread.h:24:0,
from /usr/include/x86_64-linux-gnu/c++/6/bits/gthr-default.h:35,
from /usr/include/x86_64-linux-gnu/c++/6/bits/gthr.h:148,
from /usr/include/c++/6/ext/atomicity.h:35,
from /usr/include/c++/6/bits/basic_string.h:39,
from /usr/include/c++/6/string:52,
from /usr/include/c++/6/stdexcept:39,
from /usr/include/c++/6/array:39,
from /usr/include/c++/6/tuple:39,
from /usr/include/c++/6/unordered_map:41,
from /usr/local/lib/R/site-library/Rcpp/include/Rcpp/platform/compiler.h:153,
from /usr/local/lib/R/site-library/Rcpp/include/Rcpp/r/headers.h:48,
from /usr/local/lib/R/site-library/Rcpp/include/RcppCommon.h:29,
from /usr/local/lib/R/site-library/Rcpp/include/Rcpp.h:27,
from boost-ode.cpp:1:
/usr/include/time.h:192:15: note: previous declaration ‘time_t time(time_t*)’
extern time_t time (time_t *__timer) __THROW;
^~~~
I simply removed the global variable time and used an explicit 10.0 in it's place. I also removed the namespace usage of Rcpp and std. The former was not used anyway, the latter only in one place. Generally I try to avoid importing such large namespaces, especially two at the same time.
Anyway, one simple solution would be to introduce two param vectors and select in rhs the appropriate one based on the time:
#include <Rcpp.h>
#include <boost/array.hpp>
#include <boost/numeric/odeint.hpp>
// [[Rcpp::depends(BH)]]
using namespace boost::numeric::odeint;
typedef boost::array< double ,3 > state_type;
typedef boost::array< double ,2 > parms_type;
parms_type parms_begin = {1, 10};
parms_type parms_end = {10, 10};
void rhs( const state_type &x , state_type &dxdt , const double t) {
if (t < 5.0) {
dxdt[0] = parms_begin[0]/(2.0*t*t) + x[0]/(2.0*t);
dxdt[1] = parms_begin[1]/(2.0*t*t) + x[1]/(2.0*t);
dxdt[2] = parms_begin[1]/(2.0*t*t) + x[1]/(2.0*t);
} else {
dxdt[0] = parms_end[0]/(2.0*t*t) + x[0]/(2.0*t);
dxdt[1] = parms_end[1]/(2.0*t*t) + x[1]/(2.0*t);
dxdt[2] = parms_end[1]/(2.0*t*t) + x[1]/(2.0*t);
}
}
void write_cout( const state_type &x , const double t ) {
// use Rcpp's stream
Rcpp::Rcout << t << '\t' << x[0] << '\t' << x[1] << '\t' << x[2] << std::endl;
}
typedef runge_kutta_dopri5< state_type > stepper_type;
// [[Rcpp::export]]
bool boostExample() {
state_type x = { 1.0 , 1.0, 1.0 }; // initial conditions
integrate_adaptive(make_controlled( 1E-12 , 1E-12 , stepper_type () ) ,
rhs , x , 1.0 , 10.0, 0.1 , write_cout );
return true;
}
/*** R
boostExample()
*/
I am trying to convert an SVG arc to a series of line segments. The background is, that I want to draw an arc using (reportlab)[http://www.reportlab.com/].
The svg gives me these parameters (accoring to here).
rx,ry,x-axis-rotation,large-arc-flag,sweep-flag,dx,dy
Now I need to determine lines following this arcs. But I do not understand how I can convert this to something geometrical more usable.
How would I determine the center of the ellipse arc and its rotation?
SVG elliptic arcs are really tricky and took me a while to implement it (even following the SVG specs). I ended up with something like this in C++:
//---------------------------------------------------------------------------
class svg_usek // virtual class for svg_line types
{
public:
int pat; // svg::pat[] index
virtual void reset(){};
virtual double getl (double mx,double my){ return 1.0; };
virtual double getdt(double dl,double mx,double my){ return 0.1; };
virtual void getpnt(double &x,double &y,double t){};
virtual void compute(){};
virtual void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val){};
virtual void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av){};
};
//---------------------------------------------------------------------------
class svg_ela:public svg_usek // sweep = 0 arc goes from line p0->p1 CW
{ // sweep = 1 arc goes from line p0->p1 CCW
public: // larc is unused if |da|=PI
double x0,y0,x1,y1,a,b,alfa; int sweep,larc;
double sx,sy,a0,a1,da,ang; // sx,sy rotated center by ang
double cx,cy; // real center
void reset() { x0=0; y0=0; x1=0; y1=0; a=0; b=0; alfa=0; sweep=false; larc=false; compute(); }
double getl (double mx,double my);
// double getdt(double dl,double mx,double my);
double getdt(double dl,double mx,double my) { int n; double dt; dt=divide(dl,getl(mx,my)); n=floor(divide(1.0,dt)); if (n<1) n=1; return divide(1.0,n); }
void getpnt(double &x,double &y,double t);
void compute();
void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val);
void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av);
svg_ela() {}
svg_ela(svg_ela& a) { *this=a; }
~svg_ela() {}
svg_ela* operator = (const svg_ela *a) { *this=*a; return this; }
//svg_ela* operator = (const svg_ela &a) { ...copy... return this; }
};
//---------------------------------------------------------------------------
void svg_ela::getpnt(double &x,double &y,double t)
{
double c,s,xx,yy;
t=a0+(da*t);
xx=sx+a*cos(t);
yy=sy+b*sin(t);
c=cos(-ang);
s=sin(-ang);
x=xx*c-yy*s;
y=xx*s+yy*c;
}
//---------------------------------------------------------------------------
void svg_ela::compute()
{
double ax,ay,bx,by; // body
double vx,vy,l,db;
int _sweep;
double c,s,e;
ang=pi-alfa;
_sweep=sweep;
if (larc) _sweep=!_sweep;
e=divide(a,b);
c=cos(ang);
s=sin(ang);
ax=x0*c-y0*s;
ay=x0*s+y0*c;
bx=x1*c-y1*s;
by=x1*s+y1*c;
ay*=e; // transform to circle
by*=e;
sx=0.5*(ax+bx); // mid point between A,B
sy=0.5*(ay+by);
vx=(ay-by);
vy=(bx-ax);
l=divide(a*a,(vx*vx)+(vy*vy))-0.25;
if (l<0) l=0;
l=sqrt(l);
vx*=l;
vy*=l;
if (_sweep)
{
sx+=vx;
sy+=vy;
}
else{
sx-=vx;
sy-=vy;
}
a0=atanxy(ax-sx,ay-sy);
a1=atanxy(bx-sx,by-sy);
// ay=divide(ay,e);
// by=divide(by,e);
sy=divide(sy,e);
da=a1-a0;
if (fabs(fabs(da)-pi)<=_acc_zero_ang) // half arc is without larc and sweep is not working instead change a0,a1
{
db=(0.5*(a0+a1))-atanxy(bx-ax,by-ay);
while (db<-pi) db+=pi2; // db<0 CCW ... sweep=1
while (db>+pi) db-=pi2; // db>0 CW ... sweep=0
_sweep=0;
if ((db<0.0)&&(!sweep)) _sweep=1;
if ((db>0.0)&&( sweep)) _sweep=1;
if (_sweep)
{
// a=0; b=0;
if (da>=0.0) a1-=pi2;
if (da< 0.0) a0-=pi2;
}
}
else if (larc) // big arc
{
if ((da< pi)&&(da>=0.0)) a1-=pi2;
if ((da>-pi)&&(da< 0.0)) a0-=pi2;
}
else{ // small arc
if (da>+pi) a1-=pi2;
if (da<-pi) a0-=pi2;
}
da=a1-a0;
// realny stred
c=cos(+ang);
s=sin(+ang);
cx=sx*c-sy*s;
cy=sx*s+sy*c;
}
//---------------------------------------------------------------------------
The atanxy(x,y) is the same as atan2(y,x). You can ignore class svg_usek. Usage of svg_ela is simple first feed the SVG parameters to it:
x0,y0 is start point (from previous <path> element)
x1,y1 is endpoint (x0+dx,y0+dy)
a,b are as yours rx,ry
alfa rotation angle [rad] so you need to convert from degrees...
sweep,larc are as yours.
And then call svg_ela::compute(); that will compute all variables needed for interpolation. When this initialization is done then to obtain any point from the arc just call svg_ela::getpnt(x,y,t); where x,y is the returned coordinate and t=<0,1> is input parameter. All the other methods are not important for you. To render your ARC just do this:
svg_ela arc; // your initialized arc here
int e; double x,y,t;
arc.getpnt(x,y,0.0);
Canvas->MoveTo(x,y);
for (e=1,t=0.0;e;t+=0.02)
{
if (t>=1.0) { t=1.0; e=0; }
arc.getpnt(x,y,t);
Canvas->LineTo(x,y);
}
Do not forget that SVG <g> and <path> can have transform matrices so you should apply them after each svg_ela::getpnt(x,y,t) call.
If you are interested how the stuff works compute() simply:
rotates the space so the ellipse semi-axises are axis aligned.
scale the space so ellipse becomes circle.
compute center point for circle
center lies on line that is perpendicular to line (x0,y0),(x1,y1) and also lies on its midpoint. The distance is computed by Pytagoras and direction from sweep and larc combination.
scale back to ellipse
rotate back
Now we have real center position so also compute the real endpoint angles relative to it. Now for each point on ellipse it is enough to compute it by standard parametric equation of ellipse and rotate to desired position which is what getpnt(x,y,t) does.
Hope it helps a bit.
Here related QA:
Express SVG arc as series of curves
with some images explaining the math behind SVG arcs (using the same variable names as here)
For my Java SVG application I needed a conversion of path arc to lines. I used the above code and converted it into a Java class and performed some cleanup.
package de.berndbock.tinysvg.helper;
/**
* Breaks down SVG arcs into line segments.
*
* #author Bernd Bock <chef#bernd-bock.de>
*/
public class ArcSegmenter {
private static final double PI2 = Math.PI * 2;
private static final double ACC_ZERO_ANG = 0.000001 * Math.PI / 180.0;
private final double x0;
private final double y0;
private final double x1;
private final double y1;
private final double a;
private final double b;
private final double alfa;
private final boolean sweep;
private final boolean larc;
private double sx, sy, a0, a1, da, ang; // sx, sy rotated center by ang
// private double cx, cy; // real center
public ArcSegmenter(double x0, double y0, double x1, double y1 , double a, double b, double alfa, int sweep, int larc) {
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
this.a = a;
this.b = b;
this.alfa = alfa;
this.sweep = sweep != 0;
this.larc = larc != 0;
compute();
}
private void compute() {
double ax, ay, bx, by; // body
double vx, vy, l, db;
boolean _sweep;
double c, s, e;
ang = Math.PI - alfa;
_sweep = sweep;
if (larc) {
_sweep = !_sweep;
}
e = a / b;
c = Math.cos(ang);
s = Math.sin(ang);
ax = x0 * c - y0 * s;
ay = x0 * s + y0 * c;
bx = x1 * c - y1 * s;
by = x1 * s + y1 * c;
ay *= e; // transform to circle
by *= e;
sx = 0.5 * (ax + bx); // mid point between A,B
sy = 0.5 * (ay + by);
vx = (ay - by);
vy = (bx - ax);
l = a * a / (vx * vx + vy * vy) - 0.25;
if (l < 0) {
l = 0;
}
l = Math.sqrt(l);
vx *= l;
vy *= l;
if (_sweep) {
sx += vx;
sy += vy;
}
else {
sx -= vx;
sy -= vy;
}
a0 = Math.atan2(ay - sy, ax - sx);
a1 = Math.atan2(by - sy, bx - sx);
sy = sy / e;
da = a1 - a0;
if (Math.abs(Math.abs(da) - Math.PI) <= ACC_ZERO_ANG) { // half arc is without larc and sweep is not working instead change a0,a1
db = (0.5 * (a0 + a1)) - Math.atan2(by - ay, bx - ax);
while (db < -Math.PI) {
db += PI2; // db<0 CCW ... sweep=1
}
while (db > Math.PI) {
db -= PI2; // db>0 CW ... sweep=0
}
_sweep = false;
if ((db < 0.0) && (!sweep)) {
_sweep = true;
}
if ((db > 0.0) && ( sweep)) {
_sweep = true;
}
if (_sweep) {
if (da >= 0.0) {
a1 -= PI2;
}
if (da < 0.0) {
a0 -= PI2;
}
}
}
else if (larc) { // big arc
if ((da < Math.PI) && (da >= 0.0)) {
a1 -= PI2;
}
if ((da > -Math.PI) && (da < 0.0)) {
a0 -= PI2;
}
}
else { // small arc
if (da > Math.PI) {
a1 -= PI2;
}
if (da < -Math.PI) {
a0 -= PI2;
}
}
da = a1 - a0;
// center point calculation:
// c = Math.cos(ang);
// s = Math.sin(ang);
// cx = sx * c - sy * s;
// cy = sx * s + sy * c;
}
public Point getpnt(double t) {
Point result = new Point();
double c, s, x, y;
t = a0 + da * t;
x = sx + a * Math.cos(t);
y = sy + b * Math.sin(t);
c = Math.cos(-ang);
s = Math.sin(-ang);
result.x = x * c - y * s;
result.y = x * s + y * c;
return result;
}
// public Point getCenterPoint() {
// return new Point(cx, cy);
// }
}
If you need the center point, then uncomment the respective lines.
Sample code to give you an idea of the usage:
ArcSegmenter segmenter = new ArcSegmenter(currentPoint.x, currentPoint.y, endPoint.x, endPoint.y, rx, ry, phi, sf, lf);
Point p1, p2;
p1 = segmenter.getpnt(0.0);
Line line;
for (double t = increment; t < 1.000001f; t += increment) {
p2 = segmenter.getpnt(t);
line = new Line(null, parent, p1.x, p1.y, p2.x, p2.y);
elements.add(line);
p1 = p2;
}
I have a problem with equality and adding objects to dictionary
class DoublePoint
{
public double X;
public double Y;
public double Z;
public DoublePoint(double x, double y, double z)
{
this.X = x; this.Y = y; this.Z = z;
}
public override bool Equals(object obj)
{
try
{
DoublePoint dPoint = obj as DoublePoint;
return this.X.IsEqualTo(dPoint.X) && this.Y.IsEqualTo(dPoint.Y) && this.Z.IsEqualTo(dPoint.Z);
}
catch
{
throw;
}
}
public override int GetHashCode()
{
return this.X.GetCode() ^ this.Y.GetCode() ^ this.Z.GetCode();
}
}
static class extensions
{
static double Tolerance = 0.001;
public static bool IsEqualTo(this double d1, double d2)
{
return (d1 - d2) <= Tolerance;
}
public static int GetCode(this double d1)
{
byte[] data = BitConverter.GetBytes(d1);
int x = BitConverter.ToInt32(data, 0);
int y = BitConverter.ToInt32(data, 4);
return x ^ y;
}
}
and here is my test:
DoublePoint d1 = new DoublePoint(1.200, 2.3, 3.4);
DoublePoint d2 = new DoublePoint(1.2001, 2.3, 3.4);
DoublePoint d3 = new DoublePoint(1.200, 2.3, 3.4);
bool isEqual = d1.Equals(d2); // true here
Dictionary<DoublePoint, int> dict = new Dictionary<DoublePoint, int>();
dict.Add(d1, 1);
dict.Add(d2, 2); // successful, d2 is also added but d2 is equal to d1
dict.Add(d3, 3); // Error! since we have d1 already in dictionary
With this,
when I add doublpoint objects which are same(with some tolerance), I am able to add them in dictionary. How to restrict such objects.
Is the right way to compare double data types with some tolerance.
Please advice.
Thank you
There is a problem with defining "equal" as "close enough". It is no doubt useful for computations, but such "equal" violates the transitivity rule: for Equals if a.Equals(b) && b.Equals(c), then a.Equals(c) must hold true (which is obviously not the property of your code).
So, IsEqualTo is unfortunately not suitable for redefining Equals.
What are the possible ways to solve the problem? Equals has to split into the disjoint groups of "equivalent" values. I usually do the following: define a rule to get the "canonical" value from a group, so two values are "equal" iff their canonical group representatives are equal.
Simple example: for just a double value d let's define the canonical value to be Math.Floor(d). So this way you have 1.0 equals 1.1, 0.9 equals to 0.0 but doesn't equal to 1.0. This way is not the ideal one (after all, 0.9 being not equal to 1.0 but equal to 0.0 seems to be wrong), but at least the transitivity rule is held.
Specifically for your case it could be this way:
class DoublePoint
{
public double X;
public double Y;
public double Z;
const double epsilon;
void GetCanonicalValues(out double x, out double y, out double z)
{
x = Math.Floor(X / epsilon) * epsilon;
y = Math.Floor(Y / epsilon) * epsilon;
z = Math.Floor(Z / epsilon) * epsilon;
}
public override bool Equals(object obj)
{
DoublePoint that = obj as DoublePoint;
if (that == null)
return false;
double x1, y1, z1, x2, y2, z2;
this.GetCanonicalValues(out x1, out x2, out z2);
that.GetCanonicalValues(out x1, out x2, out z2);
return (x1 == x2) && (y1 == y2) && (z1 == z2); // here we can compare
}
...
Another problem with your code is that your GetHashCode is not aligned with Equals: if a.Equals(b) then a.GetHashCode() must equal b.GetHashCode().
You can solve this as well by using the canonical values:
public override int GetHashCode()
{
double x, y, z;
GetCanonicalValues(out x, out y, out z);
return x.GetHashCode() ^ y.GetHashCode() ^ z.GetCode();
}
}
Beware that the behaviour of Equals may be unacceptable for your needs -- then you'll need to ensure the transitivity some other way.