1 /* This file is part of the hkl library.
3 * The hkl library is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * The hkl library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with the hkl library. If not, see <http://www.gnu.org/licenses/>.
16 * Copyright (C) 2003-2010 Synchrotron SOLEIL
17 * L'Orme des Merisiers Saint-Aubin
18 * BP 48 91192 GIF-sur-YVETTE CEDEX
20 * Authors: Picca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>
29 #include <gsl/gsl_math.h>
30 #include <gsl/gsl_sf_trig.h>
32 #include <hkl/hkl-geometry.h>
35 * Try to add a axis to the axes list,
36 * if a identical axis is present in the list return it
37 * else create a new on and add it to the list.
38 * die if try to add an axis with the same name but a different axis_v
40 static size_t hkl_geometry_add_rotation(HklGeometry
*geometry
,
41 char const *name
, HklVector
const *axis_v
)
46 /* check if an axis with the same name is on the axis list */
47 for(i
=0; i
<HKL_LIST_LEN(geometry
->axes
); ++i
){
50 axis
= &geometry
->axes
[i
];
51 if(!strcmp(hkl_axis_get_name(axis
), name
)){
52 if (hkl_vector_cmp(&axis
->axis_v
, axis_v
)){
53 fprintf(stderr
, "can not add two axis with the same name \"%s\" but different axes <%f, %f, %f> != <%f, %f, %f> into an HklAxes.",
55 axis
->axis_v
.data
[0], axis
->axis_v
.data
[1], axis
->axis_v
.data
[2],
56 axis_v
->data
[0], axis_v
->data
[1], axis_v
->data
[2]);
64 /* no so create and add it to the list */
65 len
= HKL_LIST_LEN(geometry
->axes
);
66 hkl_axis_init(&axis
, name
, axis_v
);
67 HKL_LIST_ADD_VALUE(geometry
->axes
, axis
);
76 static void hkl_holder_init(HklHolder
*self
, HklGeometry
*geometry
)
78 static HklQuaternion q0
= {{0, 0, 0, 0}};
79 self
->geometry
= geometry
;
80 HKL_LIST_INIT(self
->idx
);
84 static int hkl_holder_init_copy(HklHolder
*self
, HklGeometry
*geometry
,
85 HklHolder
const *holder
)
87 /* check axes compatibility */
88 if (HKL_LIST_LEN(geometry
->axes
) != HKL_LIST_LEN(holder
->geometry
->axes
))
91 self
->geometry
= geometry
;
93 HKL_LIST_ALLOC(self
->idx
, HKL_LIST_LEN(holder
->idx
));
94 HKL_LIST_COPY(self
->idx
, holder
->idx
);
101 static void hkl_holder_release_memory(HklHolder
*self
)
103 HKL_LIST_FREE(self
->idx
);
106 static void hkl_holder_update(HklHolder
*self
)
108 static HklQuaternion q0
= {{1, 0, 0, 0}};
114 axes
= self
->geometry
->axes
;
116 for(i
=0; i
<HKL_LIST_LEN(self
->idx
); ++i
)
117 hkl_quaternion_times_quaternion(&self
->q
, &axes
[idx
[i
]].q
);
120 HklAxis
*hkl_holder_add_rotation_axis(HklHolder
* self
,
121 char const * name
, double x
, double y
, double z
)
123 HklAxis
*axis
= NULL
;
131 idx
= hkl_geometry_add_rotation(self
->geometry
, name
, &axis_v
);
133 /* check that the axis is not already in the holder */
134 for(i
=0; i
<HKL_LIST_LEN(self
->idx
); i
++)
135 if (idx
== self
->idx
[i
])
138 axis
= &self
->geometry
->axes
[idx
];
139 HKL_LIST_ADD_VALUE(self
->idx
, idx
);
149 HklGeometry
*hkl_geometry_new(void)
151 HklGeometry
*g
= NULL
;
153 g
= HKL_MALLOC(HklGeometry
);
156 hkl_source_init(&g
->source
, 1.54, 1, 0, 0);
157 HKL_LIST_INIT(g
->axes
);
158 HKL_LIST_INIT(g
->holders
);
163 HklGeometry
*hkl_geometry_new_copy(HklGeometry
const *src
)
165 HklGeometry
*self
= NULL
;
169 self
= HKL_MALLOC(HklGeometry
);
171 self
->config
= src
->config
;
172 self
->source
= src
->source
;
175 HKL_LIST_ALLOC(self
->axes
, HKL_LIST_LEN(src
->axes
));
176 HKL_LIST_COPY(self
->axes
, src
->axes
);
178 /* copy the holders */
179 len
= HKL_LIST_LEN(src
->holders
);
180 HKL_LIST_ALLOC(self
->holders
, len
);
182 hkl_holder_init_copy(&self
->holders
[i
], self
,
188 void hkl_geometry_free(HklGeometry
*self
)
193 HKL_LIST_FREE(self
->axes
);
195 len
= HKL_LIST_LEN(self
->holders
);
198 hkl_holder_release_memory(&self
->holders
[i
]);
199 HKL_LIST_FREE(self
->holders
);
205 void hkl_geometry_init_geometry(HklGeometry
*self
, HklGeometry
const *src
)
210 || self
->config
->type
!= src
->config
->type
)
213 self
->source
= src
->source
;
215 /* copy the axes configuration and mark it as dirty */
216 HKL_LIST_COPY(self
->axes
, src
->axes
);
217 for(i
=0; i
<HKL_LIST_LEN(src
->holders
); ++i
)
218 self
->holders
[i
].q
= src
->holders
[i
].q
;
221 HklHolder
*hkl_geometry_add_holder(HklGeometry
*self
)
226 len
= HKL_LIST_LEN(self
->holders
);
227 HKL_LIST_RESIZE(self
->holders
, len
+ 1);
228 holder
= &self
->holders
[len
];
229 hkl_holder_init(holder
, self
);
234 void hkl_geometry_update(HklGeometry
*self
)
239 len
= HKL_LIST_LEN(self
->axes
);
241 if (hkl_axis_get_changed(&self
->axes
[i
]) == HKL_TRUE
) {
247 for(i
=0; i
<HKL_LIST_LEN(self
->holders
); i
++)
248 hkl_holder_update(&self
->holders
[i
]);
251 hkl_axis_set_changed(&self
->axes
[i
], HKL_FALSE
);
256 * hkl_geometry_get_axis_idx_by_name:
260 * get the index of the axes named @name in the geometry
262 * Returns: -1 if the axis was not found
264 int hkl_geometry_get_axis_idx_by_name(HklGeometry
*self
, const char *name
)
272 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
){
273 axis
= &self
->axes
[i
];
274 if (!strcmp(hkl_axis_get_name(axis
), name
))
281 HklAxis
*hkl_geometry_get_axis_by_name(HklGeometry
*self
, char const *name
)
285 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
) {
286 axis
= &self
->axes
[i
];
287 if (!strcmp(hkl_axis_get_name(axis
), name
))
293 void hkl_geometry_randomize(HklGeometry
*self
)
297 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
)
298 hkl_axis_randomize(&self
->axes
[i
]);
299 hkl_geometry_update(self
);
302 int hkl_geometry_set_values_v(HklGeometry
*self
, size_t len
, ...)
307 if (!self
|| len
!= HKL_LIST_LEN(self
->axes
))
312 hkl_axis_set_value(&self
->axes
[i
], va_arg(ap
, double));
315 hkl_geometry_update(self
);
320 double hkl_geometry_distance(HklGeometry
*self
, HklGeometry
*geom
)
323 double value1
, value2
;
324 double distance
= 0.;
329 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
){
330 value1
= hkl_axis_get_value(&self
->axes
[i
]);
331 value2
= hkl_axis_get_value(&geom
->axes
[i
]);
332 distance
+= fabs(value2
- value1
);
338 double hkl_geometry_distance_orthodromic(HklGeometry
*self
, HklGeometry
*geom
)
341 double value1
, value2
;
342 double distance
= 0.;
347 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
){
350 value1
= hkl_axis_get_value(&self
->axes
[i
]);
351 value2
= hkl_axis_get_value(&geom
->axes
[i
]);
352 d
= fabs(gsl_sf_angle_restrict_symm(value2
) - gsl_sf_angle_restrict_symm(value1
));
353 /* as M_PI and -M_PI are included in the GSL restriction */
362 int hkl_geometry_is_valid(const HklGeometry
*self
)
367 len
= HKL_LIST_LEN(self
->axes
);
369 if(hkl_axis_is_valid(&self
->axes
[i
]) == HKL_FALSE
)
375 int hkl_geometry_closest_from_geometry_with_range(HklGeometry
*self
, HklGeometry
*ref
)
378 size_t len
= HKL_LIST_LEN(self
->axes
);
379 double *values
= alloca(len
* sizeof(*values
));
383 values
[i
] = hkl_axis_get_value_closest(&self
->axes
[i
], &ref
->axes
[i
]);
384 if(gsl_isnan(values
[i
])){
391 hkl_axis_set_value(&self
->axes
[i
], values
[i
]);
392 hkl_geometry_update(self
);
397 void hkl_geometry_fprintf(FILE *file
, HklGeometry
const *self
)
401 for(i
=0; i
<HKL_LIST_LEN(self
->axes
); ++i
){
404 hkl_parameter_fprintf(file
, (HklParameter
*)(&self
->axes
[i
]));
408 /*******************/
409 /* HklGeometryList */
410 /*******************/
412 HklGeometryList
*hkl_geometry_list_new(void)
414 HklGeometryList
*self
;
416 self
= HKL_MALLOC(HklGeometryList
);
418 HKL_LIST_INIT(self
->items
);
419 self
->multiply
= NULL
;
424 void hkl_geometry_list_free(HklGeometryList
*self
)
426 HKL_LIST_FREE_DESTRUCTOR(self
->items
, hkl_geometry_list_item_free
);
431 * @brief this method Add a geometry to the geometries
433 * @param self The current PseudoAxeEngine
434 * @param x A vector of double with the axes values to put in the geometry.
436 * This method try to be clever by allocating memory only if the current
437 * length of the geometries is not large enought. Then it just set the
438 * geometry axes and copy it to the right geometries. We do not gives the
439 * x len as it is equal to the self->axes_len.
441 void hkl_geometry_list_add(HklGeometryList
*self
, HklGeometry
*geometry
)
446 /* now check if the geometry is already in the geometry list */
448 for(i
=0; i
<HKL_LIST_LEN(self
->items
); ++i
)
449 if (hkl_geometry_distance_orthodromic(geometry
,
450 self
->items
[i
]->geometry
) < HKL_EPSILON
)
454 HKL_LIST_ADD_VALUE(self
->items
, hkl_geometry_list_item_new(geometry
));
457 void hkl_geometry_list_reset(HklGeometryList
*self
)
459 HKL_LIST_FREE_DESTRUCTOR(self
->items
, hkl_geometry_list_item_free
);
462 void hkl_geometry_list_sort(HklGeometryList
*self
, HklGeometry
*ref
)
464 size_t len
= HKL_LIST_LEN(self
->items
);
465 double *distances
= alloca(len
* sizeof(*distances
));
466 size_t *idx
= alloca(len
* sizeof(*idx
));
469 HklGeometryListItem
**items
;
471 /* compute the distances once for all */
472 for(i
=0; i
<len
; ++i
){
473 distances
[i
] = hkl_geometry_distance(ref
, self
->items
[i
]->geometry
);
477 /* insertion sorting */
478 for(i
=1; i
<len
; ++i
){
480 /* find the smallest idx p lower than i with distance[idx[p]] >= distance[x] */
481 for(p
= 0; distances
[idx
[p
]] < distances
[x
] && fabs(distances
[idx
[p
]] - distances
[x
]) > HKL_EPSILON
; p
++);
483 /* move evythings in between p and i */
484 for(j
=i
-1; j
>=p
; j
--)
487 idx
[p
] = x
; /* insert the saved idx */
490 /* reorder the geometries. */
491 items
= malloc(len
* sizeof(HklGeometryListItem
*));
493 items
[i
] = self
->items
[idx
[i
]];
498 void hkl_geometry_list_fprintf(FILE *f
, HklGeometryList
const *self
)
500 HklParameter
*parameter
;
503 size_t len
, axes_len
;
506 fprintf(f
, "multiply method: %p \n", self
->multiply
);
507 len
= HKL_LIST_LEN(self
->items
);
509 axes_len
= HKL_LIST_LEN(self
->items
[0]->geometry
->axes
);
510 g
= self
->items
[0]->geometry
;
512 for(i
=0; i
<axes_len
; ++i
)
513 fprintf(f
, "%19s", hkl_axis_get_name(&g
->axes
[i
]));
516 for(i
=0; i
<len
; ++i
) {
517 fprintf(f
, "\n%d :", i
);
518 for(j
=0; j
<axes_len
; ++j
) {
519 parameter
= (HklParameter
*)(&self
->items
[i
]->geometry
->axes
[j
]);
520 value
= hkl_parameter_get_value_unit(parameter
);
521 if (parameter
->punit
)
522 fprintf(f
, " % 18.15f %s", value
, parameter
->punit
->repr
);
524 fprintf(f
, " % 18.15f", value
);
528 for(j
=0; j
<axes_len
; ++j
) {
529 parameter
= (HklParameter
*)(&self
->items
[i
]->geometry
->axes
[j
]);
530 value
= gsl_sf_angle_restrict_symm(parameter
->value
) * hkl_unit_factor(parameter
->unit
, parameter
->punit
);
531 if (parameter
->punit
)
532 fprintf(f
, " % 18.15f %s", value
, parameter
->punit
->repr
);
534 fprintf(f
, " % 18.15f", value
);
541 void hkl_geometry_list_multiply(HklGeometryList
*self
)
546 if(!self
|| !self
->multiply
)
549 len
= HKL_LIST_LEN(self
->items
);
551 self
->multiply(self
, i
);
554 static void perm_r(HklGeometryList
*self
, HklGeometry
*ref
, HklGeometry
*geometry
,
555 int perm
[], size_t axis_idx
)
559 len
= HKL_LIST_LEN(geometry
->axes
);
561 if (axis_idx
== len
){
562 if(hkl_geometry_distance(ref
, geometry
) > HKL_EPSILON
)
563 HKL_LIST_ADD_VALUE(self
->items
, hkl_geometry_list_item_new(geometry
));
565 if(perm
[axis_idx
] == HKL_TRUE
){
571 axis
= &geometry
->axes
[axis_idx
];
572 max
= hkl_axis_get_max(axis
);
573 value
= hkl_axis_get_value(axis
);
576 /* fprintf(stdout, "\n%d %s, %f", axis_idx, hkl_axis_get_name(axis), value * HKL_RADTODEG); */
577 perm_r(self
, ref
, geometry
, perm
, axis_idx
+ 1);
579 if(value
<= (max
+ HKL_EPSILON
))
580 hkl_axis_set_value(axis
, value
);
581 }while(value
<= (max
+ HKL_EPSILON
));
582 hkl_axis_set_value(axis
, value0
);
584 perm_r(self
, ref
, geometry
, perm
, axis_idx
+ 1);
588 void hkl_geometry_list_multiply_from_range(HklGeometryList
*self
)
595 len
= HKL_LIST_LEN(self
->items
);
596 for(i
=0; i
<len
; ++i
){
597 HklGeometry
*geometry
;
601 ref
= self
->items
[i
]->geometry
;
602 geometry
= hkl_geometry_new_copy(ref
);
603 perm
= alloca(HKL_LIST_LEN(geometry
->axes
) * sizeof(*perm
));
605 /* find axes to permute and the first solution of thoses axes */
606 for(j
=0; j
<HKL_LIST_LEN(geometry
->axes
); ++j
){
607 HklAxis
*axis
= &geometry
->axes
[j
];
608 perm
[j
] = hkl_axis_is_value_compatible_with_range(axis
);
609 /* fprintf(stdout, "%d %d\n", j, perm[j]); */
610 if (perm
[j
] == HKL_TRUE
)
611 hkl_axis_set_value_smallest_in_range(axis
);
615 * fprintf(stdout, "FIRST SOLUTION\n");
616 * hkl_geometry_fprintf(stdout, geometry);
619 perm_r(self
, ref
, geometry
, perm
, 0);
620 hkl_geometry_free(geometry
);
624 void hkl_geometry_list_remove_invalid(HklGeometryList
*self
)
631 for(i
=0; i
<HKL_LIST_LEN(self
->items
); ++i
)
632 if(!hkl_geometry_is_valid(self
->items
[i
]->geometry
)){
633 HKL_LIST_DEL_DESTRUCTOR(self
->items
, i
, hkl_geometry_list_item_free
);
638 int hkl_geometry_list_len(HklGeometryList
*self
)
640 return HKL_LIST_LEN(self
->items
);
643 int hkl_geometry_list_is_empty(HklGeometryList
*self
)
645 return HKL_LIST_LEN(self
->items
) == 0;
648 /***********************/
649 /* HklGeometryListItem */
650 /***********************/
652 HklGeometryListItem
*hkl_geometry_list_item_new(HklGeometry
*geometry
)
654 HklGeometryListItem
*self
;
659 self
= HKL_MALLOC(HklGeometryListItem
);
661 self
->geometry
= hkl_geometry_new_copy(geometry
);
667 void hkl_geometry_list_item_free(HklGeometryListItem
*self
)
672 hkl_geometry_free(self
->geometry
);