Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/fontTools/misc/arrayTools.py: 28%

92 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:33 +0000

1"""Routines for calculating bounding boxes, point in rectangle calculations and 

2so on. 

3""" 

4 

5from fontTools.misc.roundTools import otRound 

6from fontTools.misc.vector import Vector as _Vector 

7import math 

8import warnings 

9 

10 

11def calcBounds(array): 

12 """Calculate the bounding rectangle of a 2D points array. 

13 

14 Args: 

15 array: A sequence of 2D tuples. 

16 

17 Returns: 

18 A four-item tuple representing the bounding rectangle ``(xMin, yMin, xMax, yMax)``. 

19 """ 

20 if not array: 

21 return 0, 0, 0, 0 

22 xs = [x for x, y in array] 

23 ys = [y for x, y in array] 

24 return min(xs), min(ys), max(xs), max(ys) 

25 

26 

27def calcIntBounds(array, round=otRound): 

28 """Calculate the integer bounding rectangle of a 2D points array. 

29 

30 Values are rounded to closest integer towards ``+Infinity`` using the 

31 :func:`fontTools.misc.fixedTools.otRound` function by default, unless 

32 an optional ``round`` function is passed. 

33 

34 Args: 

35 array: A sequence of 2D tuples. 

36 round: A rounding function of type ``f(x: float) -> int``. 

37 

38 Returns: 

39 A four-item tuple of integers representing the bounding rectangle: 

40 ``(xMin, yMin, xMax, yMax)``. 

41 """ 

42 return tuple(round(v) for v in calcBounds(array)) 

43 

44 

45def updateBounds(bounds, p, min=min, max=max): 

46 """Add a point to a bounding rectangle. 

47 

48 Args: 

49 bounds: A bounding rectangle expressed as a tuple 

50 ``(xMin, yMin, xMax, yMax)``. 

51 p: A 2D tuple representing a point. 

52 min,max: functions to compute the minimum and maximum. 

53 

54 Returns: 

55 The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``. 

56 """ 

57 (x, y) = p 

58 xMin, yMin, xMax, yMax = bounds 

59 return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y) 

60 

61 

62def pointInRect(p, rect): 

63 """Test if a point is inside a bounding rectangle. 

64 

65 Args: 

66 p: A 2D tuple representing a point. 

67 rect: A bounding rectangle expressed as a tuple 

68 ``(xMin, yMin, xMax, yMax)``. 

69 

70 Returns: 

71 ``True`` if the point is inside the rectangle, ``False`` otherwise. 

72 """ 

73 (x, y) = p 

74 xMin, yMin, xMax, yMax = rect 

75 return (xMin <= x <= xMax) and (yMin <= y <= yMax) 

76 

77 

78def pointsInRect(array, rect): 

79 """Determine which points are inside a bounding rectangle. 

80 

81 Args: 

82 array: A sequence of 2D tuples. 

83 rect: A bounding rectangle expressed as a tuple 

84 ``(xMin, yMin, xMax, yMax)``. 

85 

86 Returns: 

87 A list containing the points inside the rectangle. 

88 """ 

89 if len(array) < 1: 

90 return [] 

91 xMin, yMin, xMax, yMax = rect 

92 return [(xMin <= x <= xMax) and (yMin <= y <= yMax) for x, y in array] 

93 

94 

95def vectorLength(vector): 

96 """Calculate the length of the given vector. 

97 

98 Args: 

99 vector: A 2D tuple. 

100 

101 Returns: 

102 The Euclidean length of the vector. 

103 """ 

104 x, y = vector 

105 return math.sqrt(x**2 + y**2) 

106 

107 

108def asInt16(array): 

109 """Round a list of floats to 16-bit signed integers. 

110 

111 Args: 

112 array: List of float values. 

113 

114 Returns: 

115 A list of rounded integers. 

116 """ 

117 return [int(math.floor(i + 0.5)) for i in array] 

118 

119 

120def normRect(rect): 

121 """Normalize a bounding box rectangle. 

122 

123 This function "turns the rectangle the right way up", so that the following 

124 holds:: 

125 

126 xMin <= xMax and yMin <= yMax 

127 

128 Args: 

129 rect: A bounding rectangle expressed as a tuple 

130 ``(xMin, yMin, xMax, yMax)``. 

131 

132 Returns: 

133 A normalized bounding rectangle. 

134 """ 

135 (xMin, yMin, xMax, yMax) = rect 

136 return min(xMin, xMax), min(yMin, yMax), max(xMin, xMax), max(yMin, yMax) 

137 

138 

139def scaleRect(rect, x, y): 

140 """Scale a bounding box rectangle. 

141 

142 Args: 

143 rect: A bounding rectangle expressed as a tuple 

144 ``(xMin, yMin, xMax, yMax)``. 

145 x: Factor to scale the rectangle along the X axis. 

146 Y: Factor to scale the rectangle along the Y axis. 

147 

148 Returns: 

149 A scaled bounding rectangle. 

150 """ 

151 (xMin, yMin, xMax, yMax) = rect 

152 return xMin * x, yMin * y, xMax * x, yMax * y 

153 

154 

155def offsetRect(rect, dx, dy): 

156 """Offset a bounding box rectangle. 

157 

158 Args: 

159 rect: A bounding rectangle expressed as a tuple 

160 ``(xMin, yMin, xMax, yMax)``. 

161 dx: Amount to offset the rectangle along the X axis. 

162 dY: Amount to offset the rectangle along the Y axis. 

163 

164 Returns: 

165 An offset bounding rectangle. 

166 """ 

167 (xMin, yMin, xMax, yMax) = rect 

168 return xMin + dx, yMin + dy, xMax + dx, yMax + dy 

169 

170 

171def insetRect(rect, dx, dy): 

172 """Inset a bounding box rectangle on all sides. 

173 

174 Args: 

175 rect: A bounding rectangle expressed as a tuple 

176 ``(xMin, yMin, xMax, yMax)``. 

177 dx: Amount to inset the rectangle along the X axis. 

178 dY: Amount to inset the rectangle along the Y axis. 

179 

180 Returns: 

181 An inset bounding rectangle. 

182 """ 

183 (xMin, yMin, xMax, yMax) = rect 

184 return xMin + dx, yMin + dy, xMax - dx, yMax - dy 

185 

186 

187def sectRect(rect1, rect2): 

188 """Test for rectangle-rectangle intersection. 

189 

190 Args: 

191 rect1: First bounding rectangle, expressed as tuples 

192 ``(xMin, yMin, xMax, yMax)``. 

193 rect2: Second bounding rectangle. 

194 

195 Returns: 

196 A boolean and a rectangle. 

197 If the input rectangles intersect, returns ``True`` and the intersecting 

198 rectangle. Returns ``False`` and ``(0, 0, 0, 0)`` if the input 

199 rectangles don't intersect. 

200 """ 

201 (xMin1, yMin1, xMax1, yMax1) = rect1 

202 (xMin2, yMin2, xMax2, yMax2) = rect2 

203 xMin, yMin, xMax, yMax = ( 

204 max(xMin1, xMin2), 

205 max(yMin1, yMin2), 

206 min(xMax1, xMax2), 

207 min(yMax1, yMax2), 

208 ) 

209 if xMin >= xMax or yMin >= yMax: 

210 return False, (0, 0, 0, 0) 

211 return True, (xMin, yMin, xMax, yMax) 

212 

213 

214def unionRect(rect1, rect2): 

215 """Determine union of bounding rectangles. 

216 

217 Args: 

218 rect1: First bounding rectangle, expressed as tuples 

219 ``(xMin, yMin, xMax, yMax)``. 

220 rect2: Second bounding rectangle. 

221 

222 Returns: 

223 The smallest rectangle in which both input rectangles are fully 

224 enclosed. 

225 """ 

226 (xMin1, yMin1, xMax1, yMax1) = rect1 

227 (xMin2, yMin2, xMax2, yMax2) = rect2 

228 xMin, yMin, xMax, yMax = ( 

229 min(xMin1, xMin2), 

230 min(yMin1, yMin2), 

231 max(xMax1, xMax2), 

232 max(yMax1, yMax2), 

233 ) 

234 return (xMin, yMin, xMax, yMax) 

235 

236 

237def rectCenter(rect): 

238 """Determine rectangle center. 

239 

240 Args: 

241 rect: Bounding rectangle, expressed as tuples 

242 ``(xMin, yMin, xMax, yMax)``. 

243 

244 Returns: 

245 A 2D tuple representing the point at the center of the rectangle. 

246 """ 

247 (xMin, yMin, xMax, yMax) = rect 

248 return (xMin + xMax) / 2, (yMin + yMax) / 2 

249 

250 

251def rectArea(rect): 

252 """Determine rectangle area. 

253 

254 Args: 

255 rect: Bounding rectangle, expressed as tuples 

256 ``(xMin, yMin, xMax, yMax)``. 

257 

258 Returns: 

259 The area of the rectangle. 

260 """ 

261 (xMin, yMin, xMax, yMax) = rect 

262 return (yMax - yMin) * (xMax - xMin) 

263 

264 

265def intRect(rect): 

266 """Round a rectangle to integer values. 

267 

268 Guarantees that the resulting rectangle is NOT smaller than the original. 

269 

270 Args: 

271 rect: Bounding rectangle, expressed as tuples 

272 ``(xMin, yMin, xMax, yMax)``. 

273 

274 Returns: 

275 A rounded bounding rectangle. 

276 """ 

277 (xMin, yMin, xMax, yMax) = rect 

278 xMin = int(math.floor(xMin)) 

279 yMin = int(math.floor(yMin)) 

280 xMax = int(math.ceil(xMax)) 

281 yMax = int(math.ceil(yMax)) 

282 return (xMin, yMin, xMax, yMax) 

283 

284 

285def quantizeRect(rect, factor=1): 

286 """ 

287 >>> bounds = (72.3, -218.4, 1201.3, 919.1) 

288 >>> quantizeRect(bounds) 

289 (72, -219, 1202, 920) 

290 >>> quantizeRect(bounds, factor=10) 

291 (70, -220, 1210, 920) 

292 >>> quantizeRect(bounds, factor=100) 

293 (0, -300, 1300, 1000) 

294 """ 

295 if factor < 1: 

296 raise ValueError(f"Expected quantization factor >= 1, found: {factor!r}") 

297 xMin, yMin, xMax, yMax = normRect(rect) 

298 return ( 

299 int(math.floor(xMin / factor) * factor), 

300 int(math.floor(yMin / factor) * factor), 

301 int(math.ceil(xMax / factor) * factor), 

302 int(math.ceil(yMax / factor) * factor), 

303 ) 

304 

305 

306class Vector(_Vector): 

307 def __init__(self, *args, **kwargs): 

308 warnings.warn( 

309 "fontTools.misc.arrayTools.Vector has been deprecated, please use " 

310 "fontTools.misc.vector.Vector instead.", 

311 DeprecationWarning, 

312 ) 

313 

314 

315def pairwise(iterable, reverse=False): 

316 """Iterate over current and next items in iterable. 

317 

318 Args: 

319 iterable: An iterable 

320 reverse: If true, iterate in reverse order. 

321 

322 Returns: 

323 A iterable yielding two elements per iteration. 

324 

325 Example: 

326 

327 >>> tuple(pairwise([])) 

328 () 

329 >>> tuple(pairwise([], reverse=True)) 

330 () 

331 >>> tuple(pairwise([0])) 

332 ((0, 0),) 

333 >>> tuple(pairwise([0], reverse=True)) 

334 ((0, 0),) 

335 >>> tuple(pairwise([0, 1])) 

336 ((0, 1), (1, 0)) 

337 >>> tuple(pairwise([0, 1], reverse=True)) 

338 ((1, 0), (0, 1)) 

339 >>> tuple(pairwise([0, 1, 2])) 

340 ((0, 1), (1, 2), (2, 0)) 

341 >>> tuple(pairwise([0, 1, 2], reverse=True)) 

342 ((2, 1), (1, 0), (0, 2)) 

343 >>> tuple(pairwise(['a', 'b', 'c', 'd'])) 

344 (('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'a')) 

345 >>> tuple(pairwise(['a', 'b', 'c', 'd'], reverse=True)) 

346 (('d', 'c'), ('c', 'b'), ('b', 'a'), ('a', 'd')) 

347 """ 

348 if not iterable: 

349 return 

350 if reverse: 

351 it = reversed(iterable) 

352 else: 

353 it = iter(iterable) 

354 first = next(it, None) 

355 a = first 

356 for b in it: 

357 yield (a, b) 

358 a = b 

359 yield (a, first) 

360 

361 

362def _test(): 

363 """ 

364 >>> import math 

365 >>> calcBounds([]) 

366 (0, 0, 0, 0) 

367 >>> calcBounds([(0, 40), (0, 100), (50, 50), (80, 10)]) 

368 (0, 10, 80, 100) 

369 >>> updateBounds((0, 0, 0, 0), (100, 100)) 

370 (0, 0, 100, 100) 

371 >>> pointInRect((50, 50), (0, 0, 100, 100)) 

372 True 

373 >>> pointInRect((0, 0), (0, 0, 100, 100)) 

374 True 

375 >>> pointInRect((100, 100), (0, 0, 100, 100)) 

376 True 

377 >>> not pointInRect((101, 100), (0, 0, 100, 100)) 

378 True 

379 >>> list(pointsInRect([(50, 50), (0, 0), (100, 100), (101, 100)], (0, 0, 100, 100))) 

380 [True, True, True, False] 

381 >>> vectorLength((3, 4)) 

382 5.0 

383 >>> vectorLength((1, 1)) == math.sqrt(2) 

384 True 

385 >>> list(asInt16([0, 0.1, 0.5, 0.9])) 

386 [0, 0, 1, 1] 

387 >>> normRect((0, 10, 100, 200)) 

388 (0, 10, 100, 200) 

389 >>> normRect((100, 200, 0, 10)) 

390 (0, 10, 100, 200) 

391 >>> scaleRect((10, 20, 50, 150), 1.5, 2) 

392 (15.0, 40, 75.0, 300) 

393 >>> offsetRect((10, 20, 30, 40), 5, 6) 

394 (15, 26, 35, 46) 

395 >>> insetRect((10, 20, 50, 60), 5, 10) 

396 (15, 30, 45, 50) 

397 >>> insetRect((10, 20, 50, 60), -5, -10) 

398 (5, 10, 55, 70) 

399 >>> intersects, rect = sectRect((0, 10, 20, 30), (0, 40, 20, 50)) 

400 >>> not intersects 

401 True 

402 >>> intersects, rect = sectRect((0, 10, 20, 30), (5, 20, 35, 50)) 

403 >>> intersects 

404 1 

405 >>> rect 

406 (5, 20, 20, 30) 

407 >>> unionRect((0, 10, 20, 30), (0, 40, 20, 50)) 

408 (0, 10, 20, 50) 

409 >>> rectCenter((0, 0, 100, 200)) 

410 (50.0, 100.0) 

411 >>> rectCenter((0, 0, 100, 199.0)) 

412 (50.0, 99.5) 

413 >>> intRect((0.9, 2.9, 3.1, 4.1)) 

414 (0, 2, 4, 5) 

415 """ 

416 

417 

418if __name__ == "__main__": 

419 import sys 

420 import doctest 

421 

422 sys.exit(doctest.testmod().failed)