forked from alexpevzner/sane-airscan
-
Notifications
You must be signed in to change notification settings - Fork 0
/
airscan-math.c
166 lines (137 loc) · 3.55 KB
/
airscan-math.c
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
/* AirScan (a.k.a. eSCL) backend for SANE
*
* Copyright (C) 2019 and up by Alexander Pevzner ([email protected])
* See LICENSE for license terms and conditions
*
* Miscellaneous mathematical functions
*/
#include "airscan.h"
/* Find greatest common divisor of two positive integers
*/
SANE_Word
math_gcd (SANE_Word x, SANE_Word y)
{
log_assert(NULL, x > 0 && y > 0);
while (x != y) {
if (x > y) {
x -= y;
} else {
y -= x;
}
}
return x;
}
/* Find least common multiple of two positive integers
*/
SANE_Word
math_lcm (SANE_Word x, SANE_Word y)
{
return (x * y) / math_gcd(x, y);
}
/* Check two ranges for equivalency
*/
static inline bool
math_range_eq (const SANE_Range *r1, const SANE_Range *r2)
{
return r1->min == r2->min && r1->max == r2->max && r1->quant == r2->quant;
}
/* Check two ranges for overlapping
*/
static inline bool
math_range_ovp (const SANE_Range *r1, const SANE_Range *r2)
{
return r1->max >= r2->min && r2->max >= r1->min;
}
/* Merge two ranges, if possible
*/
bool
math_range_merge (SANE_Range *out, const SANE_Range *r1, const SANE_Range *r2)
{
/* Check for trivial cases */
if (math_range_eq(r1, r2)) {
*out = *r1;
return true;
}
if (!math_range_ovp(r1, r2)) {
return false;
}
/* Ranges have equal quantization? If yes, just adjust min and max */
if (r1->quant == r2->quant) {
out->min = math_max(r1->min, r2->min);
out->max = math_min(r1->max, r2->max);
out->quant = r1->quant;
return true;
}
/* At least one of ranges don't have quantization? */
if (!r1->quant || !r2->quant) {
/* To avoid code duplication, normalize things, so
* r1 does have quantization and r2 doesn't. Note,
* situation when both ranges don't have quantization
* was covered before, when we checked for equal quantization
*/
if (r1->quant == 0) {
const SANE_Range *tmp = r1;
r1 = r2;
r2 = tmp;
}
/* And fit r2 within r1 */
out->min = math_range_fit(r1, r2->min);
out->max = math_range_fit(r1, r2->max);
out->quant = r1->quant;
return true;
}
/* Now the most difficult case */
SANE_Word quant = math_lcm(r1->quant, r2->quant);
SANE_Word min, max, bounds_min, bounds_max;
min = math_min(r1->min, r2->min);
bounds_min = math_max(r1->min, r2->min);
bounds_max = math_min(r1->max, r2->max);
for (min = math_min(r1->min, r2->min); min < bounds_min; min += quant)
;
if (min > bounds_max) {
return false;
}
for (max = min; max + quant <= bounds_max; max += quant)
;
out->min = min;
out->max = max;
out->quant = quant;
return true;
}
/* Choose nearest integer in range
*/
SANE_Word
math_range_fit(const SANE_Range *r, SANE_Word i)
{
if (i < r->min) {
return r->min;
}
if (i > r->max) {
return r->max;
}
if (r->quant == 0) {
return i;
}
i -= r->min;
i = ((i + r->quant / 2) / r->quant) * r->quant;
i += r->min;
return math_min(i, r->max);
}
/* Format millimeters, for printing
*/
char*
math_fmt_mm (SANE_Word mm, char buf[])
{
double mmd = SANE_UNFIX(mm);
double integer, fraction;
integer = floor(mmd);
fraction = mmd - integer;
if (fraction != 0) {
sprintf(buf, "%d.%2.2d", (int) integer, (int) round(fraction * 100));
} else {
sprintf(buf, "%d", (int) integer);
}
return buf;
}
/* vim:ts=8:sw=4:et
*/