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
*self
,
41 const char *name
, const HklVector
*axis_v
)
46 /* check if an axis with the same name is on the axis list */
47 for(i
=0; i
<self
->len
; ++i
){
50 axis
= &self
->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 self
->axes
= realloc(self
->axes
, sizeof(*self
->axes
) * (self
->len
+ 1));
66 hkl_axis_init(&self
->axes
[self
->len
], name
, axis_v
);
75 static struct HklHolderConfig
*hkl_holder_config_new(void)
77 struct HklHolderConfig
*self
;
79 self
= HKL_MALLOC(struct HklHolderConfig
);
88 static struct HklHolderConfig
*hkl_holder_config_ref(struct HklHolderConfig
*self
)
98 static void hkl_holder_config_unref(struct HklHolderConfig
*self
)
114 static void hkl_holder_init(HklHolder
*self
, HklGeometry
*geometry
)
116 static HklQuaternion q0
= {{0, 0, 0, 0}};
117 self
->config
= hkl_holder_config_new();
118 self
->geometry
= geometry
;
122 static int hkl_holder_init_copy(HklHolder
*self
, HklGeometry
*geometry
,
123 const HklHolder
*holder
)
125 /* check axes compatibility */
126 if (geometry
->len
!= holder
->geometry
->len
)
129 self
->config
= hkl_holder_config_ref(holder
->config
);
130 self
->geometry
= geometry
;
136 static void hkl_holder_release_memory(HklHolder
*self
)
138 hkl_holder_config_unref(self
->config
);
141 static void hkl_holder_update(HklHolder
*self
)
143 static HklQuaternion q0
= {{1, 0, 0, 0}};
149 axes
= self
->geometry
->axes
;
150 idx
= self
->config
->idx
;
151 for(i
=0; i
<self
->config
->len
; ++i
)
152 hkl_quaternion_times_quaternion(&self
->q
, &axes
[idx
[i
]].q
);
155 HklAxis
*hkl_holder_add_rotation_axis(HklHolder
*self
,
156 const char *name
, double x
, double y
, double z
)
158 HklAxis
*axis
= NULL
;
166 idx
= hkl_geometry_add_rotation(self
->geometry
, name
, &axis_v
);
168 /* check that the axis is not already in the holder */
169 for(i
=0; i
<self
->config
->len
; i
++)
170 if (idx
== self
->config
->idx
[i
])
173 axis
= &self
->geometry
->axes
[idx
];
174 self
->config
->idx
= realloc(self
->config
->idx
, sizeof(*self
->config
->idx
) * (self
->config
->len
+ 1));
175 self
->config
->idx
[self
->config
->len
++] = idx
;
185 * hkl_geometry_new: (skip)
191 HklGeometry
*hkl_geometry_new(void)
193 HklGeometry
*g
= NULL
;
195 g
= HKL_MALLOC(HklGeometry
);
198 hkl_source_init(&g
->source
, 1.54, 1, 0, 0);
208 * hkl_geometry_new_copy: (skip)
215 HklGeometry
*hkl_geometry_new_copy(const HklGeometry
*self
)
217 HklGeometry
*dup
= NULL
;
224 dup
= HKL_MALLOC(HklGeometry
);
226 dup
->config
= self
->config
;
227 dup
->source
= self
->source
;
228 dup
->len
= self
->len
;
229 dup
->holders_len
= self
->holders_len
;
232 dup
->axes
= malloc(sizeof(*dup
->axes
) * dup
->len
);
233 memcpy(dup
->axes
, self
->axes
, sizeof(*dup
->axes
) * dup
->len
);
235 /* copy the holders */
236 dup
->holders
= malloc(sizeof(*dup
->holders
) * dup
->holders_len
);
237 for(i
=0; i
<dup
->holders_len
; ++i
)
238 hkl_holder_init_copy(&dup
->holders
[i
], dup
,
245 * hkl_geometry_free: (skip)
250 void hkl_geometry_free(HklGeometry
*self
)
257 if(self
->holders_len
) {
258 for(i
=0; i
<self
->holders_len
; ++i
)
259 hkl_holder_release_memory(&self
->holders
[i
]);
267 * hkl_geometry_init_geometry: (skip)
271 * initilize an HklGeometry
273 void hkl_geometry_init_geometry(HklGeometry
*self
, const HklGeometry
*src
)
278 || self
->config
->type
!= src
->config
->type
)
281 self
->source
= src
->source
;
283 /* copy the axes configuration and mark it as dirty */
284 memcpy(self
->axes
, src
->axes
, sizeof(*self
->axes
) * self
->len
);
285 for(i
=0; i
<src
->holders_len
; ++i
)
286 self
->holders
[i
].q
= src
->holders
[i
].q
;
290 * hkl_geometry_add_holder: (skip)
293 * add an Holder to the #HklGeometry
297 HklHolder
*hkl_geometry_add_holder(HklGeometry
*self
)
299 self
->holders
= realloc(self
->holders
, sizeof(*self
->holders
) * (self
->holders_len
+ 1));
300 hkl_holder_init(&self
->holders
[self
->holders_len
], self
);
302 return &self
->holders
[self
->holders_len
++];
306 * hkl_geometry_update: (skip)
309 * update the geometry internal once an Axis values changed
311 void hkl_geometry_update(HklGeometry
*self
)
316 for(i
=0; i
<self
->len
; ++i
)
317 if (hkl_axis_get_changed(&self
->axes
[i
])) {
323 for(i
=0; i
<self
->holders_len
; i
++)
324 hkl_holder_update(&self
->holders
[i
]);
326 for(i
=0; i
<self
->len
; i
++)
327 hkl_axis_set_changed(&self
->axes
[i
], HKL_FALSE
);
332 * hkl_geometry_get_axis_idx_by_name: (skip)
336 * get the index of the axes named @name in the geometry
338 * Returns: -1 if the axis was not found
340 int hkl_geometry_get_axis_idx_by_name(HklGeometry
*self
, const char *name
)
348 for(i
=0; i
<self
->len
; ++i
){
349 axis
= &self
->axes
[i
];
350 if (!strcmp(hkl_axis_get_name(axis
), name
))
358 * hkl_geometry_get_axis_by_name:
362 * get an #HklAxis using its name
364 * Returns: (transfer none):
366 HklAxis
*hkl_geometry_get_axis_by_name(HklGeometry
*self
, const char *name
)
370 for(i
=0; i
<self
->len
; ++i
) {
371 axis
= &self
->axes
[i
];
372 if (!strcmp(hkl_axis_get_name(axis
), name
))
379 * hkl_geometry_randomize: (skip)
382 * randomize the #HklGeometry
384 void hkl_geometry_randomize(HklGeometry
*self
)
388 for(i
=0; i
<self
->len
; ++i
)
389 hkl_axis_randomize(&self
->axes
[i
]);
390 hkl_geometry_update(self
);
394 * hkl_geometry_set_values_v: (skip)
399 * set the axes values
403 int hkl_geometry_set_values_v(HklGeometry
*self
, size_t len
, ...)
408 if (!self
|| len
!= self
->len
)
413 hkl_axis_set_value(&self
->axes
[i
], va_arg(ap
, double));
416 hkl_geometry_update(self
);
421 int hkl_geometry_set_values_unit_v(HklGeometry
*self
, ...)
430 for(i
=0; i
<self
->len
; ++i
)
431 hkl_axis_set_value_unit(&self
->axes
[i
], va_arg(ap
, double));
434 hkl_geometry_update(self
);
440 * hkl_geometry_distance: (skip)
444 * compute the distance between two #HklGeometries
448 double hkl_geometry_distance(HklGeometry
*self
, HklGeometry
*geom
)
451 double value1
, value2
;
452 double distance
= 0.;
457 for(i
=0; i
<self
->len
; ++i
){
458 value1
= hkl_axis_get_value(&self
->axes
[i
]);
459 value2
= hkl_axis_get_value(&geom
->axes
[i
]);
460 distance
+= fabs(value2
- value1
);
467 * hkl_geometry_distance_orthodromic: (skip)
471 * compute the orthodromique distance between two #HklGeometry
475 double hkl_geometry_distance_orthodromic(HklGeometry
*self
, HklGeometry
*geom
)
478 double value1
, value2
;
479 double distance
= 0.;
484 for(i
=0; i
<self
->len
; ++i
){
487 value1
= hkl_axis_get_value(&self
->axes
[i
]);
488 value2
= hkl_axis_get_value(&geom
->axes
[i
]);
489 d
= fabs(gsl_sf_angle_restrict_symm(value2
) - gsl_sf_angle_restrict_symm(value1
));
490 /* as M_PI and -M_PI are included in the GSL restriction */
500 * hkl_geometry_is_valid: (skip)
503 * check if all axes of the #HklGeometry are valid.
507 int hkl_geometry_is_valid(const HklGeometry
*self
)
511 for(i
=0; i
<self
->len
; ++i
)
512 if(hkl_axis_is_valid(&self
->axes
[i
]) == HKL_FALSE
)
519 * hkl_geometry_closest_from_geometry_with_range: (skip)
523 * get the closest axes values in the HklInterval compatible with the
524 * current axes values
528 int hkl_geometry_closest_from_geometry_with_range(HklGeometry
*self
, HklGeometry
*ref
)
531 double *values
= alloca(self
->len
* sizeof(*values
));
534 for(i
=0;i
<self
->len
;++i
){
535 values
[i
] = hkl_axis_get_value_closest(&self
->axes
[i
], &ref
->axes
[i
]);
536 if(gsl_isnan(values
[i
])){
542 for(i
=0;i
<self
->len
;++i
)
543 hkl_axis_set_value(&self
->axes
[i
], values
[i
]);
544 hkl_geometry_update(self
);
550 * hkl_geometry_get_axes_values_unit:
552 * @len: (out caller-allocates)
554 * return all the axes values (must be free by the user)
556 * Returns: (array length=len) (transfer full):
558 double *hkl_geometry_get_axes_values_unit(const HklGeometry
*self
, unsigned int *len
)
563 if(!self
|| !len
|| self
->len
== 0)
567 values
= malloc(self
->len
* sizeof(*values
));
570 for(i
=0; i
<self
->len
; ++i
)
571 values
[i
] = hkl_axis_get_value_unit(&self
->axes
[i
]);
577 * hkl_geometry_set_axes_values_unit:
579 * @values: (array length=len):
582 * set the axes values
584 void hkl_geometry_set_axes_values_unit(HklGeometry
*self
, double *values
, unsigned int len
)
588 if (!self
|| !values
|| len
!= self
->len
)
591 for(i
=0; i
<self
->len
; ++i
)
592 hkl_axis_set_value_unit(&self
->axes
[i
], values
[i
]);
593 hkl_geometry_update(self
);
597 * hkl_geometry_fprintf: (skip)
601 * print into a file the #HklGeometry
603 void hkl_geometry_fprintf(FILE *file
, const HklGeometry
*self
)
610 for(i
=0; i
<self
->len
; ++i
){
613 hkl_parameter_fprintf(file
, (HklParameter
*)(&self
->axes
[i
]));
617 /*******************/
618 /* HklGeometryList */
619 /*******************/
622 * hkl_geometry_list_new: (skip)
628 HklGeometryList
*hkl_geometry_list_new(void)
630 HklGeometryList
*self
;
632 self
= HKL_MALLOC(HklGeometryList
);
634 list_head_init(&self
->items
);
636 self
->multiply
= NULL
;
642 * hkl_geometry_list_new_copy: (skip)
649 HklGeometryList
*hkl_geometry_list_new_copy(const HklGeometryList
*self
)
651 HklGeometryList
*dup
;
652 HklGeometryListItem
*item
;
658 dup
= HKL_MALLOC(HklGeometryList
);
661 list_head_init(&dup
->items
);
663 /* now copy the item arrays */
664 list_for_each(&self
->items
, item
, node
){
665 HklGeometryListItem
*dup_item
= hkl_geometry_list_item_new_copy(item
);
666 list_add_tail(&dup
->items
, &dup_item
->node
);
673 * hkl_geometry_list_free: (skip)
678 void hkl_geometry_list_free(HklGeometryList
*self
)
680 hkl_geometry_list_reset(self
);
686 * @self: The current #HklGeometryList
687 * @geometry: the #HklGeometry to add
689 * this method Add a geometry to the geometries
691 * This method try to be clever by allocating memory only if the
692 * current length of the geometries is not large enought. Then it just
693 * set the geometry axes and copy it to the right geometries. We do
694 * not gives the x len as it is equal to the self->axes_len.
696 void hkl_geometry_list_add(HklGeometryList
*self
, HklGeometry
*geometry
)
699 HklGeometryListItem
*item
;
701 /* now check if the geometry is already in the geometry list */
703 list_for_each(&self
->items
, item
, node
)
704 if (hkl_geometry_distance_orthodromic(geometry
,
705 item
->geometry
) < HKL_EPSILON
)
709 item
= hkl_geometry_list_item_new(geometry
);
710 list_add_tail(&self
->items
, &item
->node
);
716 * hkl_geometry_list_reset: (skip)
719 * reset the HklGeometry, in fact it is a sort of clean method remove
720 * all the items of the list.
722 void hkl_geometry_list_reset(HklGeometryList
*self
)
724 HklGeometryListItem
*item
;
725 HklGeometryListItem
*next
;
727 list_for_each_safe(&self
->items
, item
, next
, node
)
728 hkl_geometry_list_item_free(item
);
730 list_head_init(&self
->items
);
735 * hkl_geometry_list_sort: (skip)
739 * sort the #HklGeometryList compare to the distance of the given
742 void hkl_geometry_list_sort(HklGeometryList
*self
, HklGeometry
*ref
)
744 double *distances
= alloca(self
->len
* sizeof(*distances
));
745 size_t *idx
= alloca(self
->len
* sizeof(*idx
));
749 HklGeometryListItem
**items
;
750 HklGeometryListItem
*item
;
751 HklGeometryListItem
*next
;
753 /* prepare a vector to sort the items */
754 /* it should be better to sort directly the list instead of
756 items
= malloc(self
->len
* sizeof(*items
));
758 /* compute the distances once for all */
759 list_for_each(&self
->items
, item
, node
){
760 distances
[i
] = hkl_geometry_distance(ref
, item
->geometry
);
766 /* insertion sorting */
767 for(i
=1; i
<self
->len
; ++i
){
769 /* find the smallest idx p lower than i with distance[idx[p]] >= distance[x] */
770 for(p
= 0; distances
[idx
[p
]] < distances
[x
] && fabs(distances
[idx
[p
]] - distances
[x
]) > HKL_EPSILON
; p
++);
772 /* move everythings in between p and i */
773 for(j
=i
-1; j
>=p
; j
--)
776 idx
[p
] = x
; /* insert the saved idx */
779 list_head_init(&self
->items
);
780 for(i
=0; i
<self
->len
; ++i
)
781 list_add_tail(&self
->items
, &items
[idx
[i
]]->node
);
786 * hkl_geometry_list_fprintf: (skip)
790 * print to a file the #HklGeometryList
792 void hkl_geometry_list_fprintf(FILE *f
, const HklGeometryList
*self
)
794 HklParameter
*parameter
;
803 fprintf(f
, "multiply method: %p \n", self
->multiply
);
805 HklGeometryListItem
*item
;
807 item
= list_top(&self
->items
, HklGeometryListItem
, node
);
808 axes_len
= item
->geometry
->len
;
810 for(i
=0; i
<axes_len
; ++i
)
811 fprintf(f
, "%19s", hkl_axis_get_name(&item
->geometry
->axes
[i
]));
814 list_for_each(&self
->items
, item
, node
){
815 fprintf(f
, "\n%d :", i
);
816 for(j
=0; j
<axes_len
; ++j
) {
817 parameter
= (HklParameter
*)(&item
->geometry
->axes
[j
]);
818 value
= hkl_parameter_get_value_unit(parameter
);
819 if (parameter
->punit
)
820 fprintf(f
, " % 18.15f %s", value
, parameter
->punit
->repr
);
822 fprintf(f
, " % 18.15f", value
);
826 for(j
=0; j
<axes_len
; ++j
) {
827 parameter
= (HklParameter
*)(&item
->geometry
->axes
[j
]);
828 value
= gsl_sf_angle_restrict_symm(parameter
->value
) * hkl_unit_factor(parameter
->unit
, parameter
->punit
);
829 if (parameter
->punit
)
830 fprintf(f
, " % 18.15f %s", value
, parameter
->punit
->repr
);
832 fprintf(f
, " % 18.15f", value
);
840 * hkl_geometry_list_multiply: (skip)
843 * apply the multiply lenthod to the #HklGeometry
845 void hkl_geometry_list_multiply(HklGeometryList
*self
)
847 HklGeometryListItem
*item
;
848 HklGeometryListItem
*last
;
850 if(!self
|| !self
->multiply
)
854 * warning this method change the self->len so we need to save it
855 * before using the recursive perm_r calls
857 last
= list_tail(&self
->items
, HklGeometryListItem
, node
);
858 list_for_each(&self
->items
, item
, node
){
859 self
->multiply(self
, item
);
865 static void perm_r(HklGeometryList
*self
, HklGeometry
*ref
, HklGeometry
*geometry
,
866 int perm
[], size_t axis_idx
)
868 if (axis_idx
== geometry
->len
){
869 if(hkl_geometry_distance(ref
, geometry
) > HKL_EPSILON
){
870 HklGeometryListItem
*item
;
872 item
= hkl_geometry_list_item_new(geometry
);
873 list_add_tail(&self
->items
, &item
->node
);
883 axis
= &geometry
->axes
[axis_idx
];
884 max
= hkl_axis_get_max(axis
);
885 value
= hkl_axis_get_value(axis
);
888 /* fprintf(stdout, "\n%d %s, %f", axis_idx, hkl_axis_get_name(axis), value * HKL_RADTODEG); */
889 perm_r(self
, ref
, geometry
, perm
, axis_idx
+ 1);
891 if(value
<= (max
+ HKL_EPSILON
))
892 hkl_axis_set_value(axis
, value
);
893 }while(value
<= (max
+ HKL_EPSILON
));
894 hkl_axis_set_value(axis
, value0
);
896 perm_r(self
, ref
, geometry
, perm
, axis_idx
+ 1);
900 void hkl_geometry_list_multiply_from_range(HklGeometryList
*self
)
903 HklGeometryListItem
*item
;
904 HklGeometryListItem
*last
;
910 * warning this method change the self->len so we need to save it
911 * before using the recursive perm_r calls
913 last
= list_tail(&self
->items
, HklGeometryListItem
, node
);
914 list_for_each(&self
->items
, item
, node
){
915 HklGeometry
*geometry
;
919 ref
= item
->geometry
;
920 geometry
= hkl_geometry_new_copy(ref
);
921 perm
= alloca(geometry
->len
* sizeof(*perm
));
923 /* find axes to permute and the first solution of thoses axes */
924 for(j
=0; j
<geometry
->len
; ++j
){
925 HklAxis
*axis
= &geometry
->axes
[j
];
926 perm
[j
] = hkl_axis_is_value_compatible_with_range(axis
);
927 /* fprintf(stdout, "%d %d\n", j, perm[j]); */
929 hkl_axis_set_value_smallest_in_range(axis
);
932 * fprintf(stdout, "FIRST SOLUTION\n");
933 * hkl_geometry_fprintf(stdout, geometry);
936 perm_r(self
, ref
, geometry
, perm
, 0);
937 hkl_geometry_free(geometry
);
944 * hkl_geometry_list_remove_invalid: (skip)
947 * remove all invalid #HklGeometry from the #HklGeometryList
949 void hkl_geometry_list_remove_invalid(HklGeometryList
*self
)
951 HklGeometryListItem
*item
;
952 HklGeometryListItem
*next
;
957 list_for_each_safe(&self
->items
, item
, next
, node
)
958 if(!hkl_geometry_is_valid(item
->geometry
)){
959 list_del(&item
->node
);
960 hkl_geometry_list_item_free(item
);
965 /***********************/
966 /* HklGeometryListItem */
967 /***********************/
970 * hkl_geometry_list_item_new: (skip)
977 HklGeometryListItem
*hkl_geometry_list_item_new(HklGeometry
*geometry
)
979 HklGeometryListItem
*self
;
984 self
= HKL_MALLOC(HklGeometryListItem
);
986 self
->geometry
= hkl_geometry_new_copy(geometry
);
992 * hkl_geometry_list_item_new_copy: (skip)
999 HklGeometryListItem
*hkl_geometry_list_item_new_copy(const HklGeometryListItem
*self
)
1001 HklGeometryListItem
*dup
;
1006 dup
= HKL_MALLOC(HklGeometryListItem
);
1008 dup
->geometry
= hkl_geometry_new_copy(self
->geometry
);
1014 * hkl_geometry_list_item_free: (skip)
1019 void hkl_geometry_list_item_free(HklGeometryListItem
*self
)
1024 hkl_geometry_free(self
->geometry
);