1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
//
// GENERATED FILE
//
use super::*;
use f2rust_std::*;
pub const UBEL: i32 = 9;
pub const UBPL: i32 = 4;
const TOL: f64 = 0.000000001;
const SMLNPT: i32 = 320;
const BIGNPT: i32 = 400;
const MAXITR: i32 = 100;
const INF: i32 = -1;
const EXTLEN: i32 = 3;
//$Procedure ZZASRYEL ( Angular separation of ray and ellipse )
pub fn ZZASRYEL(
EXTREM: &[u8],
ELLIPS: &[f64],
VERTEX: &[f64],
DIR: &[f64],
ANGLE: &mut f64,
EXTPT: &mut [f64],
ctx: &mut Context,
) -> f2rust_std::Result<()> {
let ELLIPS = DummyArray::new(ELLIPS, 1..=UBEL);
let VERTEX = DummyArray::new(VERTEX, 1..=3);
let DIR = DummyArray::new(DIR, 1..=3);
let mut EXTPT = DummyArrayMut::new(EXTPT, 1..=3);
let mut EXTTYP = [b' '; EXTLEN as usize];
let mut A: f64 = 0.0;
let mut ACOMP: f64 = 0.0;
let mut ASIGN: f64 = 0.0;
let mut B: f64 = 0.0;
let mut BCOMP: f64 = 0.0;
let mut BTWEEN: f64 = 0.0;
let mut BTWPRX: f64 = 0.0;
let mut CENTER = StackArray::<f64, 3>::new(1..=3);
let mut DELTA: f64 = 0.0;
let mut DIFF = StackArray::<f64, 3>::new(1..=3);
let mut EPLANE = StackArray::<f64, 4>::new(1..=UBPL);
let mut EXTPRX: f64 = 0.0;
let mut GR: f64 = 0.0;
let mut LEVEL: f64 = 0.0;
let mut LOWER: f64 = 0.0;
let mut LPT = StackArray::<f64, 3>::new(1..=3);
let mut NEWPT: f64 = 0.0;
let mut P2: f64 = 0.0;
let mut PROXY: f64 = 0.0;
let mut SMAJOR = StackArray::<f64, 3>::new(1..=3);
let mut SMINOR = StackArray::<f64, 3>::new(1..=3);
let mut THETA: f64 = 0.0;
let mut UDIFF = StackArray::<f64, 3>::new(1..=3);
let mut UDIR = StackArray::<f64, 3>::new(1..=3);
let mut UPPER: f64 = 0.0;
let mut V2 = StackArray::<f64, 3>::new(1..=3);
let mut VPRJ = StackArray::<f64, 3>::new(1..=3);
let mut XOFF = StackArray::<f64, 3>::new(1..=3);
let mut XPT = StackArray::<f64, 3>::new(1..=3);
let mut EXTIDX: i32 = 0;
let mut NITR: i32 = 0;
let mut NPT: i32 = 0;
let mut NXPTS: i32 = 0;
let mut DOMIN: bool = false;
//
// SPICELIB functions
//
//
// Local parameters
//
//
// Tolerance used for loop convergence. This tolerance applies
// to the angular parameter used to specify points on the ellipse.
//
//
// Number of steps used to search the ellipse for region containing
// the point of extreme angular separation. We use two different
// values: one for the outer minimum case, which is mathematically
// well behaved, and one for the other cases.
//
//
// Maximum number of loop iterations allowed for extremum search.
//
//
// Code returned in INRYPL indicating ray lies in plane.
//
//
// String length for extremum specifier.
//
//
// Local variables
//
//
// Standard SPICE error handling.
//
if RETURN(ctx) {
return Ok(());
} else {
CHKIN(b"ZZASRYEL", ctx)?;
}
//
// Decide whether we're looking for a minimum or maximum.
//
CMPRSS(b" ", 0, EXTREM, &mut EXTTYP);
LJUST(&EXTTYP.clone(), &mut EXTTYP);
if fstr::eq(&EXTTYP, b"MIN") {
DOMIN = true;
} else if fstr::eq(&EXTTYP, b"MAX") {
DOMIN = false;
} else {
SETMSG(b"Extremum specifier # was not recognized.", ctx);
ERRCH(b"#", EXTREM, ctx);
SIGERR(b"SPICE(NOTSUPPORTED)", ctx)?;
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// Get the center and semi-axes of the ellipse.
//
EL2CGV(
ELLIPS.as_slice(),
CENTER.as_slice_mut(),
SMAJOR.as_slice_mut(),
SMINOR.as_slice_mut(),
);
//
// The ellipse semi-axes must have positive length.
//
A = VNORM(SMAJOR.as_slice());
B = VNORM(SMINOR.as_slice());
if (VZERO(SMAJOR.as_slice()) || VZERO(SMINOR.as_slice())) {
SETMSG(b"Semi-axis lengths: A = #, B = #.", ctx);
ERRDP(b"#", A, ctx);
ERRDP(b"#", B, ctx);
SIGERR(b"SPICE(INVALIDAXISLENGTH)", ctx)?;
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// Find the plane of the ellipse.
//
PSV2PL(
CENTER.as_slice(),
SMAJOR.as_slice(),
SMINOR.as_slice(),
EPLANE.as_slice_mut(),
ctx,
)?;
if FAILED(ctx) {
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// The ray's direction vector must be non-zero.
//
if VZERO(DIR.as_slice()) {
SETMSG(b"Ray\'s direction vector must be non-zero.", ctx);
SIGERR(b"SPICE(ZEROVECTOR)", ctx)?;
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// The ray's vertex must not lie in the plane of the ellipse.
// The orthogonal projection of the point onto the plane should
// yield a distinct vector.
//
VPRJP(
VERTEX.as_slice(),
EPLANE.as_slice(),
VPRJ.as_slice_mut(),
ctx,
)?;
if (VDIST(VERTEX.as_slice(), VPRJ.as_slice()) == 0.0) {
SETMSG(b"Viewing point is in the plane of the ellipse.", ctx);
SIGERR(b"SPICE(DEGENERATECASE)", ctx)?;
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// See whether the ray intersects the plane region bounded by the
// ellipse. If it does, set the limb angle sign to -1. Otherwise
// the sign is +1.
//
// First, find the intersection of the ray and plane.
//
INRYPL(
VERTEX.as_slice(),
DIR.as_slice(),
EPLANE.as_slice(),
&mut NXPTS,
XPT.as_slice_mut(),
ctx,
)?;
if (NXPTS == INF) {
//
// We don't expect to hit this case since we've already tested
// for the vertex lying in the ellipse plane. However,
// variations in round-off error make this case possible though
// unlikely.
//
SETMSG(b"Ray lies in the plane of the ellipse.", ctx);
SIGERR(b"SPICE(DEGENERATECASE)", ctx)?;
CHKOUT(b"ZZASRYEL", ctx)?;
return Ok(());
}
//
// Give NPT an initial value.
//
NPT = BIGNPT;
if (NXPTS == 0) {
//
// The ray does not intersect the plane.
//
ASIGN = 1.0;
} else {
//
// The ray intersects the plane. We must determine if the
// ray intersects the region bounded by the ellipse.
//
// Find the coordinates of the intersection point in a frame
// aligned with the axes of the ellipse and centered at
// the ellipse's center.
//
VSUB(XPT.as_slice(), CENTER.as_slice(), XOFF.as_slice_mut());
ACOMP = (VDOT(XOFF.as_slice(), SMAJOR.as_slice()) / A);
BCOMP = (VDOT(XOFF.as_slice(), SMINOR.as_slice()) / B);
//
// Now find the "level curve parameter" LEVEL for the offset of
// the intersection point from the ellipse's center.
//
LEVEL = ((f64::powi(ACOMP, 2) / f64::powi(A, 2)) + (f64::powi(BCOMP, 2) / f64::powi(B, 2)));
if (LEVEL <= 1.0) {
//
// The ray-plane intersection is on the ellipse or inside the
// plane region bounded by the ellipse.
//
ASIGN = -1.0;
} else {
ASIGN = 1.0;
if DOMIN {
//
// We have the exterior minimum case: the ray doesn't
// penetrate the plane region bounded by the ellipse,
// and we're looking for an absolute minimum of angular
// separation. We can use a fairly small number of test
// points on the limb and still find the location of
// minimum angular separation.
//
NPT = SMLNPT;
}
}
}
//
// ASIGN has been set.
//
//
// The limb is the set of points
//
// CENTER + cos(theta) SMAJOR + sin(theta) SMINOR
//
// where theta is in the interval (-pi, pi].
//
// We want to find the value of `theta' for which the angular
// separation of ray and ellipse is minimized (or maximized). To
// improve efficiency, instead of working with angular separation,
// we'll find the extremum of a proxy function: the distance
// between the unit ray direction vector and the unit vector in the
// direction from the ray's vertex to a selected point on the
// ellipse. This function doesn't require an arcsine evaluation,
// and its extrema occur at the same locations as the extrema of the
// angular separation.
//
// We'll compute the proxy value for the angular separation of the
// ray and limb at NPT different points on the limb, where the
// points are generated by taking equally spaced values of theta.
// We'll find the extremum of the proxy function on this set of
// points, and then search for the absolute extremum.
//
// To make our computations more efficient, we'll subtract off
// the ellipse's center from the vertex position to obtain a
// translated ellipse centered at the origin.
//
VSUB(VERTEX.as_slice(), CENTER.as_slice(), V2.as_slice_mut());
if DOMIN {
EXTPRX = 2.0;
} else {
EXTPRX = 0.0;
}
EXTIDX = 0;
P2 = TWOPI(ctx);
DELTA = (P2 / NPT as f64);
VHAT(DIR.as_slice(), UDIR.as_slice_mut());
for I in 0..=(NPT - 1) {
THETA = ((I as f64) * DELTA);
VLCOM3(
-1.0,
V2.as_slice(),
f64::cos(THETA),
SMAJOR.as_slice(),
f64::sin(THETA),
SMINOR.as_slice(),
DIFF.as_slice_mut(),
);
VHAT(DIFF.as_slice(), UDIFF.as_slice_mut());
PROXY = VDIST(UDIFF.as_slice(), UDIR.as_slice());
if DOMIN {
if (PROXY < EXTPRX) {
EXTIDX = I;
EXTPRX = PROXY;
}
} else {
if (PROXY > EXTPRX) {
EXTIDX = I;
EXTPRX = PROXY;
}
}
}
//
// The extreme value of the proxy function is EXTPRX, and was
// obtained at the test point indexed by EXTIDX. We find the values
// of the proxy function at the neighboring points and perform a
// `golden section' search.
//
// In the following section of code,
//
// LOWER is the lower bound of the interval in which
// the extremum is bracketed.
//
// UPPER is the upper bound of the interval in which
// the extremum is bracketed.
//
// BTWEEN is a point between LOWER and UPPER. The proxy
// function value corresponding to the angle
// BTWEEN is less than the proxy function value
// corresponding to LOWER and UPPER.
//
// NEWPT is a point between LOWER and UPPER such that
// ___
// BTWEEN - LOWER 3 - \/ 5
// -------------- = GR = ------------
// UPPER - LOWER 2
//
//
GR = ((3.0 - f64::sqrt(5.0)) / 2.0);
LOWER = ((P2 / NPT as f64) * (EXTIDX - 1) as f64);
UPPER = ((P2 / NPT as f64) * (EXTIDX + 1) as f64);
//
// We're going to move LOWER and UPPER closer together at each
// iteration of the following loop, thus trapping the extremum. The
// invariant condition that we will maintain is that the proxy value
// corresponding to the angle BTWEEN is less (or more) than the proxy
// value for the limb points corresponding to LOWER and UPPER.
//
// The loop terminates when the offset by which we adjust LOWER or
// UPPER is smaller than our tolerance value. This offset is no
// larger than the difference between LOWER and BTWEEN.
//
BTWEEN = ((P2 / NPT as f64) * EXTIDX as f64);
//
// We'll give the names LOWPRX and UPRPRX to the proxy function
// values at the limb points corresponding to LOWER and UPPER,
// respectively. We don't actually have to evaluate these values,
// however. They are useful for understanding the minimization
// algorithm we'll use, but are not actually used in the code.
//
// We already know that the proxy function value corresponding to
// BTWEEN is EXTPRX; this was computed above.
//
BTWPRX = EXTPRX;
//
// Before starting our loop, we're going to shift all of our angles
// by 2*pi, so that they're bounded away from zero.
//
LOWER = (LOWER + P2);
UPPER = (UPPER + P2);
BTWEEN = (BTWEEN + P2);
NITR = 0;
PROXY = 3.0;
while ((NITR <= MAXITR) && (TOUCHD((UPPER - LOWER)) > TOL)) {
//
// At this point, the following order relations hold:
//
// LOWER < BTWEEN < UPPER
// - -
//
// BTWPRX < MIN ( LOWPRX, UPRPRX )
// -
//
// Compute NEWPT. This point is always located at the fraction
// GR of the way into the larger of the intervals
// [ LOWER, BTWEEN ] and [ BTWEEN, UPPER ].
//
//
if ((BTWEEN - LOWER) > (UPPER - BTWEEN)) {
NEWPT = (LOWER + (GR * (BTWEEN - LOWER)));
} else {
NEWPT = (BTWEEN + (GR * (UPPER - BTWEEN)));
}
//
// We are going to shorten our interval by changing LOWER to
// NEWPT or UPPER to BTWEEN, and if necessary, BTWEEN to NEWPT,
// while maintaining the order relations of UPPER, LOWER, and
// BTWEEN, and also the order relations of UPRPRX, LOWPRX, and
// BTWPRX. To do this, we need the proxy function value at
// NEWPT.
//
VLCOM3(
-1.0,
V2.as_slice(),
f64::cos(NEWPT),
SMAJOR.as_slice(),
f64::sin(NEWPT),
SMINOR.as_slice(),
DIFF.as_slice_mut(),
);
VHAT(DIFF.as_slice(), UDIFF.as_slice_mut());
PROXY = VDIST(UDIFF.as_slice(), UDIR.as_slice());
//
// Swap NEWPT and BTWEEN if necessary, to ensure that
//
// NEWPT < BTWEEN.
// _
//
if (NEWPT > BTWEEN) {
SWAPD(&mut BTWEEN, &mut NEWPT);
SWAPD(&mut BTWPRX, &mut PROXY);
}
if DOMIN {
if (PROXY > BTWPRX) {
LOWER = NEWPT;
} else {
UPPER = BTWEEN;
BTWEEN = NEWPT;
BTWPRX = PROXY;
}
} else {
if (PROXY < BTWPRX) {
LOWER = NEWPT;
} else {
UPPER = BTWEEN;
BTWEEN = NEWPT;
BTWPRX = PROXY;
}
}
NITR = (NITR + 1);
}
//
// At this point, LPT is a good estimate of the limb point at which
// the extremum of the angular separation from the ray occurs.
//
VADD(DIFF.as_slice(), V2.as_slice(), LPT.as_slice_mut());
//
// Add the center back to LPT to find EXTPT on the original ellipse.
//
VADD(CENTER.as_slice(), LPT.as_slice(), EXTPT.as_slice_mut());
//
// Set the angular separation at EXTPT.
//
*ANGLE = (VSEP(DIFF.as_slice(), UDIR.as_slice(), ctx) * ASIGN);
CHKOUT(b"ZZASRYEL", ctx)?;
Ok(())
}