Coverage for continuous/abstract.py: 18%

123 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-11-16 22:49 +1300

1""" 

2Abstract Continuous Function 

3============================ 

4 

5Define an abstract base class for continuous mathematical functions with 

6support for interpolation, extrapolation, and arithmetical operations: 

7 

8- :class:`colour.continuous.AbstractContinuousFunction` 

9""" 

10 

11from __future__ import annotations 

12 

13import typing 

14from abc import ABC, abstractmethod 

15from copy import deepcopy 

16 

17import numpy as np 

18 

19if typing.TYPE_CHECKING: 

20 from colour.hints import ( 

21 ArrayLike, 

22 Callable, 

23 DTypeFloat, 

24 Generator, 

25 Literal, 

26 NDArrayFloat, 

27 ProtocolExtrapolator, 

28 ProtocolInterpolator, 

29 Real, 

30 Self, 

31 Type, 

32 ) 

33 

34from colour.utilities import ( 

35 MixinCallback, 

36 as_float, 

37 attest, 

38 closest, 

39 is_uniform, 

40 optional, 

41) 

42 

43__author__ = "Colour Developers" 

44__copyright__ = "Copyright 2013 Colour Developers" 

45__license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" 

46__maintainer__ = "Colour Developers" 

47__email__ = "colour-developers@colour-science.org" 

48__status__ = "Production" 

49 

50__all__ = [ 

51 "AbstractContinuousFunction", 

52] 

53 

54 

55class AbstractContinuousFunction(ABC, MixinCallback): 

56 """ 

57 Define the base class for an abstract continuous function. 

58 

59 This is an :class:`ABCMeta` abstract class that must be inherited by 

60 sub-classes. 

61 

62 The sub-classes are expected to implement the 

63 :meth:`colour.continuous.AbstractContinuousFunction.function` method so 

64 that evaluating the function for any independent domain variable 

65 :math:`x \\in\\mathbb{R}` returns a corresponding range variable 

66 :math:`y \\in\\mathbb{R}`. A conventional implementation adopts an 

67 interpolating function encapsulated inside an extrapolating function. 

68 The resulting function independent domain, stored as discrete values in 

69 the :attr:`colour.continuous.AbstractContinuousFunction.domain` attribute, 

70 corresponds with the function dependent and already known range stored in 

71 the :attr:`colour.continuous.AbstractContinuousFunction.range` property. 

72 

73 Parameters 

74 ---------- 

75 name 

76 Continuous function name. 

77 

78 Attributes 

79 ---------- 

80 - :attr:`~colour.continuous.AbstractContinuousFunction.name` 

81 - :attr:`~colour.continuous.AbstractContinuousFunction.dtype` 

82 - :attr:`~colour.continuous.AbstractContinuousFunction.domain` 

83 - :attr:`~colour.continuous.AbstractContinuousFunction.range` 

84 - :attr:`~colour.continuous.AbstractContinuousFunction.interpolator` 

85 - :attr:`~colour.continuous.\ 

86AbstractContinuousFunction.interpolator_kwargs` 

87 - :attr:`~colour.continuous.AbstractContinuousFunction.extrapolator` 

88 - :attr:`~colour.continuous.\ 

89AbstractContinuousFunction.extrapolator_kwargs` 

90 - :attr:`~colour.continuous.AbstractContinuousFunction.function` 

91 

92 Methods 

93 ------- 

94 - :meth:`~colour.continuous.AbstractContinuousFunction.__init__` 

95 - :meth:`~colour.continuous.AbstractContinuousFunction.__str__` 

96 - :meth:`~colour.continuous.AbstractContinuousFunction.__repr__` 

97 - :meth:`~colour.continuous.AbstractContinuousFunction.__hash__` 

98 - :meth:`~colour.continuous.AbstractContinuousFunction.__getitem__` 

99 - :meth:`~colour.continuous.AbstractContinuousFunction.__setitem__` 

100 - :meth:`~colour.continuous.AbstractContinuousFunction.__contains__` 

101 - :meth:`~colour.continuous.AbstractContinuousFunction.__iter__` 

102 - :meth:`~colour.continuous.AbstractContinuousFunction.__len__` 

103 - :meth:`~colour.continuous.AbstractContinuousFunction.__eq__` 

104 - :meth:`~colour.continuous.AbstractContinuousFunction.__ne__` 

105 - :meth:`~colour.continuous.AbstractContinuousFunction.__iadd__` 

106 - :meth:`~colour.continuous.AbstractContinuousFunction.__add__` 

107 - :meth:`~colour.continuous.AbstractContinuousFunction.__isub__` 

108 - :meth:`~colour.continuous.AbstractContinuousFunction.__sub__` 

109 - :meth:`~colour.continuous.AbstractContinuousFunction.__imul__` 

110 - :meth:`~colour.continuous.AbstractContinuousFunction.__mul__` 

111 - :meth:`~colour.continuous.AbstractContinuousFunction.__idiv__` 

112 - :meth:`~colour.continuous.AbstractContinuousFunction.__div__` 

113 - :meth:`~colour.continuous.AbstractContinuousFunction.__ipow__` 

114 - :meth:`~colour.continuous.AbstractContinuousFunction.__pow__` 

115 - :meth:`~colour.continuous.AbstractContinuousFunction.\ 

116arithmetical_operation` 

117 - :meth:`~colour.continuous.AbstractContinuousFunction.fill_nan` 

118 - :meth:`~colour.continuous.AbstractContinuousFunction.domain_distance` 

119 - :meth:`~colour.continuous.AbstractContinuousFunction.is_uniform` 

120 - :meth:`~colour.continuous.AbstractContinuousFunction.copy` 

121 """ 

122 

123 def __init__(self, name: str | None = None) -> None: 

124 super().__init__() 

125 

126 self._name: str = f"{self.__class__.__name__} ({id(self)})" 

127 self.name = optional(name, self._name) 

128 

129 @property 

130 def name(self) -> str: 

131 """ 

132 Getter and setter for the abstract continuous function name. 

133 

134 Parameters 

135 ---------- 

136 value 

137 Value to set the abstract continuous function name with. 

138 

139 Returns 

140 ------- 

141 :class:`str` 

142 Abstract continuous function name. 

143 """ 

144 

145 return self._name 

146 

147 @name.setter 

148 def name(self, value: str) -> None: 

149 """Setter for the **self.name** property.""" 

150 

151 attest( 

152 isinstance(value, str), 

153 f'"name" property: "{value}" type is not "str"!', 

154 ) 

155 

156 self._name = value 

157 

158 @property 

159 @abstractmethod 

160 def dtype(self) -> Type[DTypeFloat]: 

161 """ 

162 Getter and setter for the abstract continuous function dtype. 

163 

164 This property must be reimplemented by sub-classes. 

165 

166 Parameters 

167 ---------- 

168 value 

169 Value to set the abstract continuous function dtype with. 

170 

171 Returns 

172 ------- 

173 Type[DTypeFloat] 

174 Abstract continuous function dtype. 

175 """ 

176 

177 ... # pragma: no cover 

178 

179 @dtype.setter 

180 @abstractmethod 

181 def dtype(self, value: Type[DTypeFloat]) -> None: 

182 """ 

183 Setter for the **self.dtype** property, must be reimplemented by 

184 sub-classes. 

185 """ 

186 

187 ... # pragma: no cover 

188 

189 @property 

190 @abstractmethod 

191 def domain(self) -> NDArrayFloat: 

192 """ 

193 Getter and setter for the abstract continuous function's independent 

194 domain variable :math:`x`. 

195 

196 This property must be reimplemented by sub-classes. 

197 

198 Parameters 

199 ---------- 

200 value 

201 Value to set the abstract continuous function independent domain 

202 variable :math:`x` with. 

203 

204 Returns 

205 ------- 

206 :class:`numpy.ndarray` 

207 Abstract continuous function independent domain variable 

208 :math:`x`. 

209 """ 

210 

211 ... # pragma: no cover 

212 

213 @domain.setter 

214 @abstractmethod 

215 def domain(self, value: ArrayLike) -> None: 

216 """ 

217 Setter for the **self.domain** property, must be reimplemented by 

218 sub-classes. 

219 """ 

220 

221 ... # pragma: no cover 

222 

223 @property 

224 @abstractmethod 

225 def range(self) -> NDArrayFloat: 

226 """ 

227 Getter and setter for the abstract continuous function's range 

228 variable :math:`y`. 

229 

230 This property must be reimplemented by sub-classes. 

231 

232 Parameters 

233 ---------- 

234 value 

235 Value to set the abstract continuous function's range variable 

236 :math:`y` with. 

237 

238 Returns 

239 ------- 

240 :class:`numpy.ndarray` 

241 Abstract continuous function's range variable :math:`y`. 

242 """ 

243 

244 ... # pragma: no cover 

245 

246 @range.setter 

247 @abstractmethod 

248 def range(self, value: ArrayLike) -> None: 

249 """ 

250 Setter for the **self.range** property, must be reimplemented by 

251 sub-classes. 

252 """ 

253 

254 ... # pragma: no cover 

255 

256 @property 

257 @abstractmethod 

258 def interpolator(self) -> Type[ProtocolInterpolator]: 

259 """ 

260 Getter and setter for the abstract continuous function interpolator 

261 type. 

262 

263 This property must be reimplemented by sub-classes. 

264 

265 Parameters 

266 ---------- 

267 value 

268 Value to set the abstract continuous function interpolator type 

269 with. 

270 

271 Returns 

272 ------- 

273 Type[ProtocolInterpolator] 

274 Abstract continuous function interpolator type. 

275 """ 

276 

277 ... # pragma: no cover 

278 

279 @interpolator.setter 

280 @abstractmethod 

281 def interpolator(self, value: Type[ProtocolInterpolator]) -> None: 

282 """ 

283 Setter for the **self.interpolator** property, must be reimplemented by 

284 sub-classes. 

285 """ 

286 

287 ... # pragma: no cover 

288 

289 @property 

290 @abstractmethod 

291 def interpolator_kwargs(self) -> dict: 

292 """ 

293 Getter and setter for the interpolator instantiation time arguments. 

294 

295 This property must be reimplemented by sub-classes. 

296 

297 Parameters 

298 ---------- 

299 value 

300 Value to set the abstract continuous function interpolator 

301 instantiation time arguments to. 

302 

303 Returns 

304 ------- 

305 :class:`dict` 

306 Abstract continuous function interpolator instantiation time 

307 arguments. 

308 """ 

309 

310 ... # pragma: no cover 

311 

312 @interpolator_kwargs.setter 

313 @abstractmethod 

314 def interpolator_kwargs(self, value: dict) -> None: 

315 """ 

316 Setter for the **self.interpolator_kwargs** property, must be 

317 reimplemented by sub-classes. 

318 """ 

319 

320 ... # pragma: no cover 

321 

322 @property 

323 @abstractmethod 

324 def extrapolator(self) -> Type[ProtocolExtrapolator]: 

325 """ 

326 Getter and setter for the abstract continuous function extrapolator 

327 type. 

328 

329 This property must be reimplemented by sub-classes. 

330 

331 Parameters 

332 ---------- 

333 value 

334 Value to set the abstract continuous function extrapolator type 

335 with. 

336 

337 Returns 

338 ------- 

339 Type[ProtocolExtrapolator] 

340 Abstract continuous function extrapolator type. 

341 """ 

342 

343 ... # pragma: no cover 

344 

345 @extrapolator.setter 

346 @abstractmethod 

347 def extrapolator(self, value: Type[ProtocolExtrapolator]) -> None: 

348 """ 

349 Setter for the **self.extrapolator** property, must be reimplemented by 

350 sub-classes. 

351 """ 

352 

353 ... # pragma: no cover 

354 

355 @property 

356 @abstractmethod 

357 def extrapolator_kwargs(self) -> dict: 

358 """ 

359 Getter and setter for the abstract continuous function extrapolator 

360 instantiation time arguments. 

361 

362 This property must be reimplemented by sub-classes. 

363 

364 Parameters 

365 ---------- 

366 value 

367 Value to set the abstract continuous function extrapolator 

368 instantiation time arguments to. 

369 

370 Returns 

371 ------- 

372 :class:`dict` 

373 Abstract continuous function extrapolator instantiation time 

374 arguments. 

375 """ 

376 

377 ... # pragma: no cover 

378 

379 @extrapolator_kwargs.setter 

380 @abstractmethod 

381 def extrapolator_kwargs(self, value: dict) -> None: 

382 """ 

383 Setter for the **self.extrapolator_kwargs** property, must be 

384 reimplemented by sub-classes. 

385 """ 

386 

387 ... # pragma: no cover 

388 

389 @property 

390 @abstractmethod 

391 def function(self) -> Callable: 

392 """ 

393 Getter for the abstract continuous function callable. 

394 

395 This property must be reimplemented by sub-classes. 

396 

397 Returns 

398 ------- 

399 Callable 

400 Abstract continuous function callable. 

401 """ 

402 

403 ... # pragma: no cover 

404 

405 @abstractmethod 

406 def __str__(self) -> str: 

407 """ 

408 Return a formatted string representation of the abstract continuous 

409 function. 

410 

411 This method must be reimplemented by sub-classes. 

412 

413 Returns 

414 ------- 

415 :class:`str` 

416 Formatted string representation. 

417 """ 

418 

419 return super().__repr__() # pragma: no cover 

420 

421 @abstractmethod 

422 def __repr__(self) -> str: 

423 """ 

424 Return an evaluable string representation of the abstract continuous 

425 function, must be reimplemented by sub-classes. 

426 

427 Returns 

428 ------- 

429 :class:`str` 

430 Evaluable string representation. 

431 """ 

432 

433 return super().__repr__() # pragma: no cover 

434 

435 @abstractmethod 

436 def __hash__(self) -> int: 

437 """ 

438 Compute the hash of the abstract continuous function. 

439 

440 Returns 

441 ------- 

442 :class:`int` 

443 Object hash. 

444 """ 

445 

446 ... # pragma: no cover 

447 

448 @abstractmethod 

449 def __getitem__(self, x: ArrayLike | slice) -> NDArrayFloat: 

450 """ 

451 Return the corresponding range variable :math:`y` for the specified 

452 independent domain variable :math:`x`. 

453 

454 This abstract method must be reimplemented by sub-classes. 

455 

456 Parameters 

457 ---------- 

458 x 

459 Independent domain variable :math:`x`. 

460 

461 Returns 

462 ------- 

463 :class:`numpy.ndarray` 

464 Variable :math:`y` range value. 

465 """ 

466 

467 ... # pragma: no cover 

468 

469 @abstractmethod 

470 def __setitem__(self, x: ArrayLike | slice, y: ArrayLike) -> None: 

471 """ 

472 Set the corresponding range variable :math:`y` for the specified 

473 independent domain variable :math:`x`. 

474 

475 This abstract method must be reimplemented by sub-classes. 

476 

477 Parameters 

478 ---------- 

479 x 

480 Independent domain variable :math:`x`. 

481 y 

482 Corresponding range variable :math:`y`. 

483 """ 

484 

485 ... # pragma: no cover 

486 

487 @abstractmethod 

488 def __contains__(self, x: ArrayLike | slice) -> bool: 

489 """ 

490 Determine whether the abstract continuous function contains the 

491 specified independent domain variable :math:`x`. 

492 

493 This abstract method must be reimplemented by sub-classes. 

494 

495 Parameters 

496 ---------- 

497 x 

498 Independent domain variable :math:`x`. 

499 

500 Returns 

501 ------- 

502 :class:`bool` 

503 Whether :math:`x` domain value is contained. 

504 """ 

505 

506 ... # pragma: no cover 

507 

508 def __iter__(self) -> Generator: 

509 """ 

510 Return a generator for the abstract continuous function. 

511 

512 Yields 

513 ------ 

514 Generator 

515 Abstract continuous function generator. 

516 """ 

517 

518 yield from np.column_stack([self.domain, self.range]) 

519 

520 def __len__(self) -> int: 

521 """ 

522 Return the number of elements in the abstract continuous function's 

523 independent domain variable :math:`x`. 

524 

525 Returns 

526 ------- 

527 :class:`int` 

528 Number of elements in the independent domain variable :math:`x`. 

529 """ 

530 

531 return len(self.domain) 

532 

533 @abstractmethod 

534 def __eq__(self, other: object) -> bool: 

535 """ 

536 Determine whether the abstract continuous function equals the specified 

537 object. 

538 

539 This abstract method must be reimplemented by sub-classes. 

540 

541 Parameters 

542 ---------- 

543 other 

544 Object to determine for equality with the abstract continuous function. 

545 

546 Returns 

547 ------- 

548 :class:`bool` 

549 Whether the specified object is equal to the abstract continuous 

550 function. 

551 """ 

552 

553 ... # pragma: no cover 

554 

555 @abstractmethod 

556 def __ne__(self, other: object) -> bool: 

557 """ 

558 Determine whether the abstract continuous function is not equal to the 

559 specified object. 

560 

561 This method must be reimplemented by sub-classes. 

562 

563 Parameters 

564 ---------- 

565 other 

566 Object to determine whether it is not equal to the abstract continuous 

567 function. 

568 

569 Returns 

570 ------- 

571 :class:`bool` 

572 Whether the specified object is not equal to the abstract 

573 continuous function. 

574 """ 

575 

576 ... # pragma: no cover 

577 

578 def __add__(self, a: ArrayLike | Self) -> Self: 

579 """ 

580 Implement support for addition. 

581 

582 Parameters 

583 ---------- 

584 a 

585 Variable :math:`a` to add to the continuous function. 

586 

587 Returns 

588 ------- 

589 :class:`colour.continuous.AbstractContinuousFunction` 

590 Abstract continuous function with the specified variable 

591 added. 

592 """ 

593 

594 return self.arithmetical_operation(a, "+") 

595 

596 def __iadd__(self, a: ArrayLike | Self) -> Self: 

597 """ 

598 Implement support for in-place addition. 

599 

600 Parameters 

601 ---------- 

602 a 

603 Variable :math:`a` to add in-place to the abstract continuous 

604 function. 

605 

606 Returns 

607 ------- 

608 :class:`colour.continuous.AbstractContinuousFunction` 

609 Abstract continuous function with in-place addition applied. 

610 """ 

611 

612 return self.arithmetical_operation(a, "+", True) 

613 

614 def __sub__(self, a: ArrayLike | Self) -> Self: 

615 """ 

616 Implement support for subtraction. 

617 

618 Parameters 

619 ---------- 

620 a 

621 Variable :math:`a` to subtract from the continuous function. 

622 

623 Returns 

624 ------- 

625 :class:`colour.continuous.AbstractContinuousFunction` 

626 Abstract continuous function with the specified variable 

627 subtracted. 

628 """ 

629 

630 return self.arithmetical_operation(a, "-") 

631 

632 def __isub__(self, a: ArrayLike | Self) -> Self: 

633 """ 

634 Implement support for in-place subtraction. 

635 

636 Parameters 

637 ---------- 

638 a 

639 Variable :math:`a` to subtract in-place from the abstract continuous 

640 function. 

641 

642 Returns 

643 ------- 

644 :class:`colour.continuous.AbstractContinuousFunction` 

645 Abstract continuous function with in-place subtraction applied. 

646 """ 

647 

648 return self.arithmetical_operation(a, "-", True) 

649 

650 def __mul__(self, a: ArrayLike | Self) -> Self: 

651 """ 

652 Implement support for multiplication. 

653 

654 Parameters 

655 ---------- 

656 a 

657 Variable :math:`a` to multiply the continuous function by. 

658 

659 Returns 

660 ------- 

661 :class:`colour.continuous.AbstractContinuousFunction` 

662 Abstract continuous function with the specified variable 

663 multiplied. 

664 """ 

665 

666 return self.arithmetical_operation(a, "*") 

667 

668 def __imul__(self, a: ArrayLike | Self) -> Self: 

669 """ 

670 Implement support for in-place multiplication. 

671 

672 Parameters 

673 ---------- 

674 a 

675 Variable :math:`a` to multiply in-place by the abstract continuous 

676 function. 

677 

678 Returns 

679 ------- 

680 :class:`colour.continuous.AbstractContinuousFunction` 

681 Abstract continuous function with in-place multiplication applied. 

682 """ 

683 

684 return self.arithmetical_operation(a, "*", True) 

685 

686 def __div__(self, a: ArrayLike | Self) -> Self: 

687 """ 

688 Implement support for division. 

689 

690 Parameters 

691 ---------- 

692 a 

693 Variable :math:`a` to divide the continuous function by. 

694 

695 Returns 

696 ------- 

697 :class:`colour.continuous.AbstractContinuousFunction` 

698 Abstract continuous function with the specified variable 

699 divided. 

700 """ 

701 

702 return self.arithmetical_operation(a, "/") 

703 

704 def __idiv__(self, a: ArrayLike | Self) -> Self: 

705 """ 

706 Implement support for in-place division. 

707 

708 Parameters 

709 ---------- 

710 a 

711 Variable :math:`a` to divide in-place by the abstract continuous 

712 function. 

713 

714 Returns 

715 ------- 

716 :class:`colour.continuous.AbstractContinuousFunction` 

717 Abstract continuous function with in-place division applied. 

718 """ 

719 

720 return self.arithmetical_operation(a, "/", True) 

721 

722 __itruediv__ = __idiv__ 

723 __truediv__ = __div__ 

724 

725 def __pow__(self, a: ArrayLike | Self) -> Self: 

726 """ 

727 Implement support for exponentiation. 

728 

729 Parameters 

730 ---------- 

731 a 

732 Variable :math:`a` to raise the continuous function to the power of. 

733 

734 Returns 

735 ------- 

736 :class:`colour.continuous.AbstractContinuousFunction` 

737 Abstract continuous function with the specified variable 

738 exponentiated. 

739 """ 

740 

741 return self.arithmetical_operation(a, "**") 

742 

743 def __ipow__(self, a: ArrayLike | Self) -> Self: 

744 """ 

745 Implement support for in-place exponentiation. 

746 

747 Parameters 

748 ---------- 

749 a 

750 Variable :math:`a` to raise in-place the abstract continuous 

751 function to the power of. 

752 

753 Returns 

754 ------- 

755 :class:`colour.continuous.AbstractContinuousFunction` 

756 Abstract continuous function with in-place exponentiation applied. 

757 """ 

758 

759 return self.arithmetical_operation(a, "**", True) 

760 

761 @abstractmethod 

762 def arithmetical_operation( 

763 self, 

764 a: ArrayLike | Self, 

765 operation: Literal["+", "-", "*", "/", "**"], 

766 in_place: bool = False, 

767 ) -> Self: 

768 """ 

769 Perform the specified arithmetical operation with operand :math:`a`, 

770 either on a copy or in-place. 

771 

772 This method must be reimplemented by sub-classes. 

773 

774 Parameters 

775 ---------- 

776 a 

777 Operand :math:`a`. Can be a numeric value, array-like object, or 

778 another continuous function instance. 

779 operation 

780 Operation to perform. 

781 in_place 

782 Operation happens in place. 

783 

784 Returns 

785 ------- 

786 :class:`colour.continuous.AbstractContinuousFunction` 

787 Abstract continuous function. 

788 """ 

789 

790 ... # pragma: no cover 

791 

792 @abstractmethod 

793 def fill_nan( 

794 self, 

795 method: Literal["Constant", "Interpolation"] | str = "Interpolation", 

796 default: Real = 0, 

797 ) -> Self: 

798 """ 

799 Fill NaNs in independent domain variable :math:`x` and corresponding 

800 range variable :math:`y` using the specified method. 

801 

802 This abstract method must be reimplemented by sub-classes. 

803 

804 Parameters 

805 ---------- 

806 method 

807 *Interpolation* method linearly interpolates through the NaNs, 

808 *Constant* method replaces NaNs with ``default``. 

809 default 

810 Value to use with the *Constant* method. 

811 

812 Returns 

813 ------- 

814 :class:`colour.continuous.AbstractContinuousFunction` 

815 NaNs filled abstract continuous function. 

816 """ 

817 

818 ... # pragma: no cover 

819 

820 def domain_distance(self, a: ArrayLike) -> NDArrayFloat: 

821 """ 

822 Return the Euclidean distance between specified array and the closest 

823 element of the independent domain :math:`x`. 

824 

825 Parameters 

826 ---------- 

827 a 

828 Variable :math:`a` to compute the Euclidean distance with the 

829 independent domain variable :math:`x`. 

830 

831 Returns 

832 ------- 

833 :class:`numpy.ndarray` 

834 Euclidean distance between independent domain variable :math:`x` 

835 and specified variable :math:`a`. 

836 """ 

837 

838 n = closest(self.domain, a) 

839 

840 return as_float(np.abs(a - n)) 

841 

842 def is_uniform(self) -> bool: 

843 """ 

844 Return whether the independent domain variable :math:`x` is uniform. 

845 

846 Returns 

847 ------- 

848 :class:`bool` 

849 Whether the independent domain variable :math:`x` is uniform. 

850 """ 

851 

852 return is_uniform(self.domain) 

853 

854 def copy(self) -> Self: 

855 """ 

856 Return a copy of the sub-class instance. 

857 

858 Returns 

859 ------- 

860 :class:`colour.continuous.AbstractContinuousFunction` 

861 Copy of the abstract continuous function. 

862 """ 

863 

864 return deepcopy(self)