@@ -134,6 +134,13 @@ namespace GEO {
134134 linesearch_init_iter_ = 0 ;
135135
136136 use_direct_solver_ = false ;
137+
138+ nb_air_particles_ = 0 ;
139+ air_particles_ = nil;
140+ air_particles_stride_ = 0 ;
141+ air_fraction_ = 0.0 ;
142+
143+ clip_by_balls_ = false ;
137144 }
138145
139146 OptimalTransportMap::~OptimalTransportMap () {
@@ -144,27 +151,45 @@ namespace GEO {
144151 void OptimalTransportMap::set_points (
145152 index_t nb_points, const double * points, index_t stride
146153 ) {
154+
147155 if (stride == 0 ) {
148156 stride = dimension_;
149157 }
158+
159+ index_t nb_total = nb_points + nb_air_particles_;
150160
151161 // Note: we represent power diagrams as (d+1)-dim Voronoi diagrams.
152162 // The target points are lifted to (d+1)-dim.
153- points_dimp1_.resize (nb_points * dimp1_);
163+ points_dimp1_.resize (nb_total * dimp1_);
154164 for (index_t i = 0 ; i < nb_points; ++i) {
165+ double * p = &points_dimp1_[i*dimp1_];
155166 for (index_t c=0 ; c<dimension_; ++c) {
156- points_dimp1_[i*dimp1_+ c] = points[i*stride+c];
167+ p[ c] = points[i*stride+c];
157168 }
158- points_dimp1_[i*dimp1_ + dimension ()] = 0.0 ;
169+ p[dimension ()] = 0.0 ; // Yes, dimension() and not dimension()-1
170+ // (for instance, in 2d, x->0, y->1, W->2)
159171 }
172+ for (index_t i=0 ; i<nb_air_particles_; ++i) {
173+ double * p = &points_dimp1_[(i + nb_points)*dimp1_];
174+ for (index_t c=0 ; c<dimension_; ++c) {
175+ p[c] = air_particles_[i*air_particles_stride_+c];
176+ }
177+ p[dimension ()] = 0.0 ; // Yes, dimension() and not dimension()-1
178+ // (for instance, in 2d, x->0, y->1, W->2)
179+ }
160180 weights_.assign (nb_points, 0 );
161- constant_nu_ = total_mass_ / double (nb_points);
181+ constant_nu_ = (1.0 - air_fraction_) * total_mass_ / double (nb_points);
182+ if (air_fraction_ != 0.0 && nb_air_particles_ == 0 ) {
183+ // constant_nu_ = pi*R^2 -> R^2 = constant_nu_ / pi
184+ // TODO: volumetric case.
185+ weights_.assign (nb_points, constant_nu_ / M_PI);
186+ }
162187 }
163188
164189 void OptimalTransportMap::set_nu (index_t i, double nu) {
165190 geo_debug_assert (i < weights_.size ());
166191 if (nu_.size () != weights_.size ()) {
167- nu_.assign (weights_.size (), 0.0 );
192+ nu_.assign (weights_.size (), 0.0 );
168193 }
169194 nu_[i] = nu;
170195 }
@@ -173,7 +198,7 @@ namespace GEO {
173198 index_t max_iterations, index_t n
174199 ) {
175200 if (n == 0 ) {
176- n = index_t (points_dimp1_.size () / dimp1_);
201+ n = index_t (points_dimp1_.size () / dimp1_) - nb_air_particles_ ;
177202 }
178203
179204 vector<double > pk (n);
@@ -212,13 +237,21 @@ namespace GEO {
212237 FOR (i,n) {
213238 epsilon0 = geo_min (epsilon0, nu (i));
214239 }
240+ if (nb_air_particles_ != 0.0 ) {
241+ epsilon0 = geo_min (epsilon0, air_fraction_ * total_mass_);
242+ }
215243 epsilon0 = 0.5 * epsilon0;
216244 }
217245
218246 if (verbose_) {
219247 Logger::out (" OTM" ) << " Solving linear system" << std::endl;
220248 }
221249
250+ if (nbZ_ != 0 ) {
251+ std::cerr << " There were empty cells !!!!!!" << std::endl;
252+ std::cerr << " FATAL error, exiting Newton" << std::endl;
253+ return ;
254+ }
222255 solve_linear_system ();
223256
224257 if (verbose_) {
@@ -269,8 +302,10 @@ namespace GEO {
269302
270303 // Condition to exit linesearch loop
271304 if (
272- (measure_of_smallest_cell_ >= epsilon0) &&
273- (g_norm_ <= (1.0 - 0.5 *alphak) * gknorm)
305+ (measure_of_smallest_cell_ >= epsilon0) && (
306+ (air_fraction_ != 0.0 ) ||
307+ (g_norm_ <= (1.0 - 0.5 *alphak) * gknorm)
308+ )
274309 ) {
275310 // Condition for global convergence
276311 if (g_norm_ < gradient_threshold (n)) {
@@ -307,36 +342,36 @@ namespace GEO {
307342 }
308343
309344 void OptimalTransportMap::optimize (index_t max_iterations) {
310-
311- index_t n = index_t (points_dimp1_.size () / dimp1_);
312-
345+
346+ index_t n = index_t (points_dimp1_.size () / dimp1_) - nb_air_particles_ ;
347+
313348 // Sanity check
314349 if (nu_.size () != 0 ) {
315- double total_nu = 0.0 ;
316- FOR (i,n) {
317- total_nu += nu (i);
318- }
319- std::cerr << " total nu=" << total_nu << std::endl;
320- std::cerr << " total mass=" << total_mass_ << std::endl;
321- if (::fabs (total_nu - total_mass_)/total_mass_ > 0.01 ) {
322- Logger::warn (" OTM" ) << " Specified nu do not sum to domain measure"
323- << std::endl;
324- Logger::warn (" OTM" ) << " rescaling..."
325- << std::endl;
326- }
327- FOR (i,n) {
328- set_nu (i, nu (i) * total_mass_ / total_nu);
329- }
330-
331- total_nu = 0.0 ;
332- FOR (i,n) {
333- total_nu += nu (i);
334- }
335- if (::fabs (total_nu - total_mass_)/total_mass_ > 0.01 ) {
336- Logger::warn (" OTM" ) << " Specified nu do not sum to domain measure"
337- << std::endl;
338- return ;
339- }
350+ double total_nu = 0.0 ;
351+ FOR (i,n) {
352+ total_nu += nu (i);
353+ }
354+ std::cerr << " total nu=" << total_nu << std::endl;
355+ std::cerr << " total mass=" << total_mass_ << std::endl;
356+ if (::fabs (total_nu - total_mass_)/total_mass_ > 0.01 ) {
357+ Logger::warn (" OTM" ) << " Specified nu do not sum to domain measure"
358+ << std::endl;
359+ Logger::warn (" OTM" ) << " rescaling..."
360+ << std::endl;
361+ }
362+ FOR (i,n) {
363+ set_nu (i, nu (i) * total_mass_ / total_nu);
364+ }
365+
366+ total_nu = 0.0 ;
367+ FOR (i,n) {
368+ total_nu += nu (i);
369+ }
370+ if (::fabs (total_nu - total_mass_)/total_mass_ > 0.01 ) {
371+ Logger::warn (" OTM" ) << " Specified nu do not sum to domain measure"
372+ << std::endl;
373+ return ;
374+ }
340375 }
341376
342377 if (newton_) {
@@ -556,10 +591,17 @@ namespace GEO {
556591 for (index_t p = 0 ; p < n; ++p) {
557592 W = geo_max (W, w[p]);
558593 }
559- for (index_t p = 0 ; p < n; ++p) {
560- points_dimp1_[dimp1_ * p + dimension_] = ::sqrt (W - w[p]);
561- }
562-
594+ for (index_t p = 0 ; p < n; ++p) {
595+ // Yes, dimension_ and not dimension_ -1,
596+ // for instance in 2d, x->0, y->1, W->2
597+ points_dimp1_[dimp1_ * p + dimension_] = ::sqrt (W - w[p]);
598+ }
599+ if (nb_air_particles_ != 0 ) {
600+ for (index_t p = 0 ; p < nb_air_particles_; ++p) {
601+ points_dimp1_[dimp1_ * (n + p) + dimension_] = ::sqrt (W - 0.0 );
602+ }
603+ }
604+
563605 // Step 2: compute function and gradient
564606 {
565607 Stopwatch* SW = nil;
@@ -569,7 +611,7 @@ namespace GEO {
569611 Logger::out (" OTM" ) << " In power diagram..." << std::endl;
570612 }
571613 }
572- delaunay_->set_vertices (n , points_dimp1_.data ());
614+ delaunay_->set_vertices ((n + nb_air_particles_) , points_dimp1_.data ());
573615 if (verbose_ && newton_) {
574616 delete SW;
575617 }
@@ -634,6 +676,17 @@ namespace GEO {
634676 ++nb_empty_cells;
635677 }
636678 }
679+ if (air_fraction_ != 0.0 ) {
680+ double air_mass = total_mass_;
681+ for (index_t i=0 ; i<n; ++i) {
682+ air_mass -= g[i];
683+ }
684+ if (fabs (air_mass) < 1e-30 ) {
685+ ++nb_empty_cells;
686+ }
687+ measure_of_smallest_cell_ =
688+ GEO::geo_min (measure_of_smallest_cell_, air_mass);
689+ }
637690 }
638691
639692 if (update_fg) {
0 commit comments