salabim 24.0.13__py3-none-any.whl → 24.0.14.post4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
salabim/salabim.py CHANGED
@@ -1,13 +1,13 @@
1
- # _ _ _ ____ _ _ ___ _ _____
2
- # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | || | / _ \ / ||___ /
3
- # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) || || |_ | | | | | | |_ \
4
- # \__ \| (_| || || (_| || |_) || || | | | | | / __/ |__ _| _ | |_| | _ | | ___) |
5
- # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____| |_| (_) \___/ (_)|_||____/
1
+ # _ _ _ ____ _ _ ___ _ _ _
2
+ # ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | || | / _ \ / || || |
3
+ # / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) || || |_ | | | | | || || |_
4
+ # \__ \| (_| || || (_| || |_) || || | | | | | / __/ |__ _| _ | |_| | _ | ||__ _|
5
+ # |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____| |_| (_) \___/ (_)|_| |_|
6
6
  # discrete event simulation
7
7
  #
8
8
  # see www.salabim.org for more information, the documentation and license information
9
9
 
10
- __version__ = "24.0.13"
10
+ __version__ = "24.0.14"
11
11
 
12
12
  import heapq
13
13
  import random
@@ -9055,7 +9055,6 @@ by adding:
9055
9055
  else:
9056
9056
  return
9057
9057
 
9058
-
9059
9058
  def _trywait(self):
9060
9059
  if self.status.value == interrupted:
9061
9060
  return False
@@ -10207,7 +10206,7 @@ class Event(Component):
10207
10206
  """
10208
10207
  return self._action_taken
10209
10208
 
10210
-
10209
+
10211
10210
  class Environment:
10212
10211
  """
10213
10212
  environment object
@@ -11349,13 +11348,13 @@ class Environment:
11349
11348
 
11350
11349
  if width is not None:
11351
11350
  if self._width != width:
11352
- self._width = width
11351
+ self._width = int(width)
11353
11352
  frame_changed = True
11354
11353
  width_changed = True
11355
11354
 
11356
11355
  if height is not None:
11357
11356
  if self._height != height:
11358
- self._height = height
11357
+ self._height = int(height)
11359
11358
  frame_changed = True
11360
11359
  height_changed = True
11361
11360
 
@@ -11804,7 +11803,7 @@ class Environment:
11804
11803
  for ao in an_objects:
11805
11804
  ao.make_pil_image(self.t())
11806
11805
  if ao._image_visible and (include_topleft or not ao.getattr("in_topleft", False)):
11807
- image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])), ao._image)
11806
+ image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])), ao._image.convert("RGBA"))
11808
11807
 
11809
11808
  return image.convert(mode)
11810
11809
 
@@ -12779,6 +12778,31 @@ class Environment:
12779
12778
  self.animation_parameters(synced=value, animate=None)
12780
12779
  return self._synced
12781
12780
 
12781
+ def minimized(self, value: bool=None)-> bool:
12782
+ """
12783
+ minimized
12784
+
12785
+ Parameters
12786
+ ----------
12787
+ value : bool
12788
+ if True, minimize the curent animation window
12789
+
12790
+ if False, (re)show the current animation window
12791
+
12792
+ if None (default): no action
12793
+
12794
+ Returns
12795
+ -------
12796
+ current state of the animation window : bool
12797
+ True if current animation windows is minimized, False otherwise
12798
+ """
12799
+ if value is not None:
12800
+ if value:
12801
+ self.root.withdraw()
12802
+ else:
12803
+ self.root.deiconify()
12804
+ return not bool(self.root.winfo_viewable())
12805
+
12782
12806
  def speed(self, value: float = None) -> float:
12783
12807
  """
12784
12808
  speed
@@ -13201,7 +13225,7 @@ class Environment:
13201
13225
  if co is None:
13202
13226
  if len(g.canvas_objects) >= self._maximum_number_of_bitmaps:
13203
13227
  if overflow_image is None:
13204
- overflow_image = Image.new("RGBA", (self._width, self._height), (0, 0, 0, 0))
13228
+ overflow_image = Image.new("RGBA", (int(self._width), int(self._height)), (0, 0, 0, 0))
13205
13229
  overflow_image.paste(ao._image, (int(ao._image_x), int(self._height - ao._image_y - ao._image.size[1])), ao._image)
13206
13230
  ao.canvas_object = None
13207
13231
  else:
@@ -15426,10 +15450,20 @@ class Animate2dBase(DynamicClass):
15426
15450
  spec = self.image(t)
15427
15451
  image_container = ImageContainer(spec)
15428
15452
  width = self.width(t)
15453
+ height = self.height(t)
15454
+
15429
15455
  if width is None:
15430
- width = image_container.images[0].size[0]
15456
+ if height is None:
15457
+ width = image_container.images[0].size[0]
15458
+ height = image_container.images[0].size[1]
15459
+ else:
15460
+ width = height * image_container.images[0].size[0] / image_container.images[0].size[1]
15461
+ else:
15462
+ if height is None:
15463
+ height = width * image_container.images[0].size[1] / image_container.images[0].size[0]
15464
+ else:
15465
+ ...
15431
15466
 
15432
- height = width * image_container.images[0].size[1] / image_container.images[0].size[0]
15433
15467
  if not self.screen_coordinates:
15434
15468
  width *= self.env._scale
15435
15469
  height *= self.env._scale
@@ -15448,7 +15482,6 @@ class Animate2dBase(DynamicClass):
15448
15482
  offsety = offsety * self.env._scale
15449
15483
 
15450
15484
  alpha = int(self.alpha(t))
15451
-
15452
15485
  image, id = image_container.get_image(
15453
15486
  (t - self.animation_start(t)) * self.animation_speed(t),
15454
15487
  repeat=self.animation_repeat(t),
@@ -15456,6 +15489,7 @@ class Animate2dBase(DynamicClass):
15456
15489
  t_from=self.animation_from(t),
15457
15490
  t_to=self.animation_to(t),
15458
15491
  )
15492
+
15459
15493
  self._image_ident = (spec, id, width, height, angle, alpha, flip_horizontal, flip_vertical)
15460
15494
 
15461
15495
  if self._image_ident != self._image_ident_prev:
@@ -15736,6 +15770,9 @@ class AnimateClassic(Animate2dBase):
15736
15770
  def width(self, t):
15737
15771
  return self.master.width(t)
15738
15772
 
15773
+ def height(self, t):
15774
+ return self.master.height(t)
15775
+
15739
15776
  def anchor(self, t):
15740
15777
  return self.master.anchor(t)
15741
15778
 
@@ -15963,6 +16000,11 @@ class Animate:
15963
16000
 
15964
16001
  if omitted or None, no scaling
15965
16002
 
16003
+ height0 : float
16004
+ width of the image to be displayed at time t0
16005
+
16006
+ if omitted or None, no scaling
16007
+
15966
16008
  t1 : float
15967
16009
  time of end of the animation (default inf)
15968
16010
 
@@ -16031,6 +16073,9 @@ class Animate:
16031
16073
  width1 : float
16032
16074
  width of the image to be displayed at time t1 (default: width0)
16033
16075
 
16076
+ height1 : float
16077
+ width of the image to be displayed at time t1 (default: height0)
16078
+
16034
16079
  over3d : bool
16035
16080
  if True, this object will be rendered to the OpenGL window
16036
16081
 
@@ -16099,6 +16144,7 @@ class Animate:
16099
16144
  font -
16100
16145
  fontsize0,fontsize1 -
16101
16146
  width0,width1 -
16147
+ height0,height1 -
16102
16148
  ====================== ========= ========= ========= ========= ========= =========
16103
16149
  """
16104
16150
 
@@ -16134,6 +16180,7 @@ class Animate:
16134
16180
  alpha0: float = 255,
16135
16181
  fontsize0: float = 20,
16136
16182
  width0: float = None,
16183
+ height0: float = None,
16137
16184
  t1: float = None,
16138
16185
  x1: float = None,
16139
16186
  y1: float = None,
@@ -16152,6 +16199,7 @@ class Animate:
16152
16199
  alpha1: float = None,
16153
16200
  fontsize1: float = None,
16154
16201
  width1: float = None,
16202
+ height1: float = None,
16155
16203
  xy_anchor: str = "",
16156
16204
  over3d: bool = None,
16157
16205
  flip_horizontal: bool = False,
@@ -16198,10 +16246,12 @@ class Animate:
16198
16246
  self.text0 = text
16199
16247
 
16200
16248
  if image is None:
16201
- self.width0 = 0 # just to be able to interpolate
16249
+ self.width0 = 0 # just to be able to interpolat
16250
+ self.height0 = 0
16202
16251
  else:
16203
16252
  self.image0 = image
16204
16253
  self.width0 = width0 # None means original size
16254
+ self.height0 = height0
16205
16255
 
16206
16256
  self.as_points0 = as_points
16207
16257
  self.font0 = font
@@ -16263,7 +16313,7 @@ class Animate:
16263
16313
  self.alpha1 = self.alpha0 if alpha1 is None else alpha1
16264
16314
  self.fontsize1 = self.fontsize0 if fontsize1 is None else fontsize1
16265
16315
  self.width1 = self.width0 if width1 is None else width1
16266
-
16316
+ self.height1 = self.height0 if height1 is None else height1
16267
16317
  self.t1 = inf if t1 is None else t1
16268
16318
  if self.env._animate_debug:
16269
16319
  self.caller = self.env._frame_to_lineno(_get_caller_frame(), add_filename=True)
@@ -16322,6 +16372,7 @@ class Animate:
16322
16372
  alpha0=None,
16323
16373
  fontsize0=None,
16324
16374
  width0=None,
16375
+ height0=None,
16325
16376
  xy_anchor1=None,
16326
16377
  as_points=None,
16327
16378
  t1=None,
@@ -16342,6 +16393,7 @@ class Animate:
16342
16393
  alpha1=None,
16343
16394
  fontsize1=None,
16344
16395
  width1=None,
16396
+ height1=None,
16345
16397
  flip_horizontal=None,
16346
16398
  flip_vertical=None,
16347
16399
  animation_start=None,
@@ -16494,6 +16546,11 @@ class Animate:
16494
16546
 
16495
16547
  if None, the original width of the image will be used
16496
16548
 
16549
+ height0 : float
16550
+ height of the image to be displayed at time t0 (default see below)
16551
+
16552
+ if None, the original height of the image will be used
16553
+
16497
16554
  t1 : float
16498
16555
  time of end of the animation (default: inf)
16499
16556
 
@@ -16560,6 +16617,8 @@ class Animate:
16560
16617
  width1 : float
16561
16618
  width of the image to be displayed at time t1 (default: width0)
16562
16619
 
16620
+ height1 : float
16621
+ height of the image to be displayed at time t1 (default: height0)
16563
16622
 
16564
16623
  Note
16565
16624
  ----
@@ -16595,6 +16654,8 @@ class Animate:
16595
16654
  self.max_lines0 = max_lines
16596
16655
 
16597
16656
  self.width0 = self.width() if width0 is None else width0
16657
+ self.height0 = self.height() if height0 is None else height0
16658
+
16598
16659
  if image is not None:
16599
16660
  self.image0 = image
16600
16661
 
@@ -16641,6 +16702,7 @@ class Animate:
16641
16702
  self.alpha1 = self.alpha0 if alpha1 is None else alpha1
16642
16703
  self.fontsize1 = self.fontsize0 if fontsize1 is None else fontsize1
16643
16704
  self.width1 = self.width0 if width1 is None else width1
16705
+ self.height1 = self.height0 if height1 is None else height1
16644
16706
  self.xy_anchor1 = self.xy_anchor0 if xy_anchor1 is None else xy_anchor1
16645
16707
 
16646
16708
  self.t1 = inf if t1 is None else t1
@@ -16938,7 +17000,7 @@ class Animate:
16938
17000
 
16939
17001
  def width(self, t=None):
16940
17002
  """
16941
- width position of an animated image object. May be overridden.
17003
+ width of an animated image object. May be overridden.
16942
17004
 
16943
17005
  Parameters
16944
17006
  ----------
@@ -16963,6 +17025,33 @@ class Animate:
16963
17025
 
16964
17026
  return interpolate((self.env._now if t is None else t), self.t0, self.t1, width0, width1)
16965
17027
 
17028
+ def height(self, t=None):
17029
+ """
17030
+ height of an animated image object. May be overridden.
17031
+
17032
+ Parameters
17033
+ ----------
17034
+ t : float
17035
+ current time
17036
+
17037
+ Returns
17038
+ -------
17039
+ height : float
17040
+ default behaviour: linear interpolation between self.height0 and self.height1
17041
+
17042
+ if None, the original height of the image will be used
17043
+ """
17044
+ height0 = self.height0
17045
+ height1 = self.height1
17046
+ if height0 is None and height1 is None:
17047
+ return None
17048
+ if height0 is None:
17049
+ height0 = ImageContainer(self.image0).images[0].size[0]
17050
+ if height1 is None:
17051
+ height1 = ImageContainer(self.image1).images[0].size[0]
17052
+
17053
+ return interpolate((self.env._now if t is None else t), self.t0, self.t1, height0, height1)
17054
+
16966
17055
  def fontsize(self, t=None):
16967
17056
  """
16968
17057
  fontsize of an animate object. May be overridden.
@@ -19656,6 +19745,8 @@ class AnimateImage(Animate2dBase):
19656
19745
  width : float
19657
19746
  width of the image (default: None = no scaling)
19658
19747
 
19748
+ heighth : float
19749
+ height of the image (default: None = no scaling)
19659
19750
 
19660
19751
  text : str, tuple or list
19661
19752
  the text to be displayed
@@ -19791,6 +19882,7 @@ class AnimateImage(Animate2dBase):
19791
19882
  x: Union[float, Callable] = None,
19792
19883
  y: Union[float, Callable] = None,
19793
19884
  width: Union[float, Callable] = None,
19885
+ height: Union[float, Callable] = None,
19794
19886
  text: Union[str, Callable] = None,
19795
19887
  fontsize: Union[float, Callable] = None,
19796
19888
  textcolor: Union[ColorType, Callable] = None,
@@ -19833,6 +19925,7 @@ class AnimateImage(Animate2dBase):
19833
19925
  x=0,
19834
19926
  y=0,
19835
19927
  width=None,
19928
+ height=None,
19836
19929
  text="",
19837
19930
  fontsize=15,
19838
19931
  textcolor="bg",
@@ -19915,10 +20008,10 @@ class ComponentGenerator(Component):
19915
20008
 
19916
20009
  Parameters
19917
20010
  ----------
19918
- component_class : callable, usually a subclass of Component or Pdf or Cdf distribution
20011
+ component_class : callable, usually a subclass of Component or Pdf/Pmf or Cdf distribution
19919
20012
  the type of components to be generated
19920
20013
 
19921
- in case of a distribution, the Pdf or Cdf should return a callable
20014
+ in case of a distribution, the Pdf/Pmf or Cdf should return a callable
19922
20015
 
19923
20016
  generator_name : str
19924
20017
  name of the component generator.
@@ -20478,7 +20571,7 @@ class _Distribution:
20478
20571
  If, after number_of_tries retries, the sampled value is still not within the given bounds,
20479
20572
  fail_value will be returned
20480
20573
 
20481
- Samples that cannot be converted (only possible with Pdf and CumPdf) to float
20574
+ Samples that cannot be converted (only possible with /Pmf and CumPdf/CumPmf) to float
20482
20575
  are assumed to be within the bounds.
20483
20576
  """
20484
20577
  return Bounded(self, lowerbound, upperbound, fail_value, number_of_retries, include_lowerbound, include_upperbound).sample()
@@ -20718,7 +20811,7 @@ class Bounded(_Distribution):
20718
20811
  If, after number_of_tries retries, the sampled value is still not within the given bounds,
20719
20812
  fail_value will be returned
20720
20813
 
20721
- Samples that cannot be converted to float (only possible with Pdf and CumPdf)
20814
+ Samples that cannot be converted to float (only possible with Pdf/Pmf and CumPdf)
20722
20815
  are assumed to be within the bounds.
20723
20816
  """
20724
20817
 
@@ -22214,6 +22307,9 @@ class Pdf(_Distribution):
22214
22307
 
22215
22308
  If it is a salabim distribution, not the distribution,
22216
22309
  but a sample will be returned when calling sample.
22310
+
22311
+
22312
+ This method is also available under the name Pmf
22217
22313
  """
22218
22314
 
22219
22315
  def __init__(self, spec: Union[Iterable, Dict], probabilities=None, time_unit: str = None, randomstream: Any = None, env: "Environment" = None):
@@ -22358,9 +22454,151 @@ class Pdf(_Distribution):
22358
22454
  return self._mean
22359
22455
 
22360
22456
 
22457
+ class Pmf(Pdf):
22458
+ """
22459
+ Probability mass function
22460
+
22461
+ Parameters
22462
+ ----------
22463
+ spec : list, tuple or dict
22464
+ either
22465
+
22466
+ - if no probabilities specified:
22467
+
22468
+ list/tuple with x-values and corresponding probability
22469
+ dict where the keys are re x-values and the values are probabilities
22470
+ (x0, p0, x1, p1, ...xn,pn)
22471
+
22472
+ - if probabilities is specified:
22473
+
22474
+ list with x-values
22475
+
22476
+ probabilities : iterable or float
22477
+ if omitted, spec contains the probabilities
22478
+
22479
+ the iterable (p0, p1, ...pn) contains the probabilities of the corresponding
22480
+ x-values from spec.
22481
+
22482
+ alternatively, if a float is given (e.g. 1), all x-values
22483
+ have equal probability. The value is not important.
22484
+
22485
+ time_unit : str
22486
+ specifies the time unit
22487
+
22488
+ must be one of "years", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds"
22489
+
22490
+ default : no conversion
22491
+
22492
+
22493
+ randomstream : randomstream
22494
+ if omitted, random will be used
22495
+
22496
+ if used as random.Random(12299)
22497
+ it assigns a new stream with the specified seed
22498
+
22499
+ env : Environment
22500
+ environment where the distribution is defined
22501
+
22502
+ if omitted, default_env will be used
22503
+
22504
+ Note
22505
+ ----
22506
+ p0+p1=...+pn>0
22507
+
22508
+ all densities are auto scaled according to the sum of p0 to pn,
22509
+ so no need to have p0 to pn add up to 1 or 100.
22510
+
22511
+ The x-values can be any type.
22512
+
22513
+ If it is a salabim distribution, not the distribution,
22514
+ but a sample will be returned when calling sample.
22515
+
22516
+ This method is also available under the name Pdf
22517
+
22518
+ """
22519
+
22520
+ def __repr__(self):
22521
+ return "Pmf"
22522
+
22523
+ def print_info(self, as_str: bool = False, file: TextIO = None) -> str:
22524
+ """
22525
+ prints information about the distribution
22526
+
22527
+ Parameters
22528
+ ----------
22529
+ as_str: bool
22530
+ if False (default), print the info
22531
+ if True, return a string containing the info
22532
+
22533
+ file: file
22534
+ if None(default), all output is directed to stdout
22535
+
22536
+ otherwise, the output is directed to the file
22537
+
22538
+ Returns
22539
+ -------
22540
+ info (if as_str is True) : str
22541
+ """
22542
+ result = []
22543
+ result.append("Pmf distribution " + hex(id(self)))
22544
+ result.append(" randomstream=" + hex(id(self.randomstream)))
22545
+ return return_or_print(result, as_str, file)
22546
+
22547
+ def sample(self, n: int = None) -> Any:
22548
+ """
22549
+ Parameters
22550
+ ----------
22551
+ n : number of samples : int
22552
+ if not specified, specifies just return one sample, as usual
22553
+
22554
+ if specified, return a list of n sampled values from the distribution without replacement.
22555
+ This requires that all probabilities are equal.
22556
+
22557
+ If n > number of values in the Pmf distribution, n is assumed to be the number of values
22558
+ in the distribution.
22559
+
22560
+ If a sampled value is a distribution, a sample from that distribution will be returned.
22561
+
22562
+ Returns
22563
+ -------
22564
+ Sample of the distribution : any (usually float) or list
22565
+ In case n is specified, returns a list of n values
22566
+
22567
+ """
22568
+ if self.supports_n:
22569
+ if n is None:
22570
+ return self.randomstream.sample(self._x, 1)[0]
22571
+ else:
22572
+ if n < 0:
22573
+ raise ValueError("n < 0")
22574
+ n = min(n, len(self._x))
22575
+ xs = self.randomstream.sample(self._x, n)
22576
+ return [x.sample() if isinstance(x, _Distribution) else x for x in xs]
22577
+ else:
22578
+ if n is None:
22579
+ r = self.randomstream.random()
22580
+ for cum, x in zip([0] + self._cum, [0] + self._x):
22581
+ if r <= cum:
22582
+ if isinstance(x, _Distribution):
22583
+ return x.sample()
22584
+ return x
22585
+ else:
22586
+ raise ValueError("not all probabilities are the same")
22587
+
22588
+ def mean(self) -> float:
22589
+ """
22590
+ Returns
22591
+ -------
22592
+ mean of the distribution : float
22593
+ if the mean can't be calculated (if not all x-values are scalars or distributions),
22594
+ nan will be returned.
22595
+ """
22596
+ return self._mean
22597
+
22598
+
22361
22599
  class CumPdf(_Distribution):
22362
22600
  """
22363
- Cumulative Probability distribution function
22601
+ Cumulative Probability mass function
22364
22602
 
22365
22603
  Parameters
22366
22604
  ----------
@@ -22413,6 +22651,8 @@ class CumPdf(_Distribution):
22413
22651
 
22414
22652
  If it is a salabim distribution, not the distribution,
22415
22653
  but a sample will be returned when calling sample.
22654
+
22655
+ This method is also available under the name CumPmf
22416
22656
  """
22417
22657
 
22418
22658
  def __init__(
@@ -22528,6 +22768,116 @@ class CumPdf(_Distribution):
22528
22768
  return self._mean
22529
22769
 
22530
22770
 
22771
+ class CumPmf(CumPdf):
22772
+ """
22773
+ Cumulative Probability mass function
22774
+
22775
+ Parameters
22776
+ ----------
22777
+ spec : list or tuple
22778
+ either
22779
+
22780
+ - if no cumprobabilities specified:
22781
+
22782
+ list with x-values and corresponding cumulative probability
22783
+ (x0, p0, x1, p1, ...xn,pn)
22784
+
22785
+ - if cumprobabilities is specified:
22786
+
22787
+ list with x-values
22788
+
22789
+ cumprobabilities : list, tuple or float
22790
+ if omitted, spec contains the probabilities
22791
+
22792
+ the list (p0, p1, ...pn) contains the cumulative probabilities of the corresponding
22793
+ x-values from spec.
22794
+
22795
+
22796
+ time_unit : str
22797
+ specifies the time unit
22798
+
22799
+ must be one of "years", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds"
22800
+
22801
+ default : no conversion
22802
+
22803
+
22804
+ randomstream : randomstream
22805
+ if omitted, random will be used
22806
+
22807
+ if used as random.Random(12299)
22808
+ it assigns a new stream with the specified seed
22809
+
22810
+ env : Environment
22811
+ environment where the distribution is defined
22812
+
22813
+ if omitted, default_env will be used
22814
+
22815
+ Note
22816
+ ----
22817
+ p0<=p1<=..pn>0
22818
+
22819
+ all densities are auto scaled according to pn,
22820
+ so no need to have pn be 1 or 100.
22821
+
22822
+ The x-values can be any type.
22823
+
22824
+ If it is a salabim distribution, not the distribution,
22825
+ but a sample will be returned when calling sample.
22826
+
22827
+ This method is also available under the name CumPdf
22828
+ """
22829
+
22830
+ def __repr__(self):
22831
+ return "CumPmf"
22832
+
22833
+ def print_info(self, as_str: bool = False, file: TextIO = None) -> str:
22834
+ """
22835
+ prints information about the distribution
22836
+
22837
+ Parameters
22838
+ ----------
22839
+ as_str: bool
22840
+ if False (default), print the info
22841
+ if True, return a string containing the info
22842
+
22843
+ file: file
22844
+ if None(default), all output is directed to stdout
22845
+
22846
+ otherwise, the output is directed to the file
22847
+
22848
+ Returns
22849
+ -------
22850
+ info (if as_str is True) : str
22851
+ """
22852
+ result = []
22853
+ result.append("CumPmf distribution " + hex(id(self)))
22854
+ result.append(" randomstream=" + hex(id(self.randomstream)))
22855
+ return return_or_print(result, as_str, file)
22856
+
22857
+ def sample(self) -> Any:
22858
+ """
22859
+ Returns
22860
+ -------
22861
+ Sample of the distribution : any (usually float)
22862
+ """
22863
+ r = self.randomstream.random()
22864
+ for cum, x in zip([0] + self._cum, [0] + self._x):
22865
+ if r <= cum:
22866
+ if isinstance(x, _Distribution):
22867
+ return x.sample()
22868
+ return x
22869
+
22870
+ def mean(self) -> float:
22871
+ """
22872
+ Returns
22873
+ -------
22874
+ mean of the distribution : float
22875
+ if the mean can't be calculated (if not all x-values are scalars or distributions),
22876
+ nan will be returned.
22877
+ """
22878
+ return self._mean
22879
+
22880
+
22531
22881
  class External(_Distribution):
22532
22882
  """
22533
22883
  External distribution function
@@ -24127,7 +24477,7 @@ class _APNG:
24127
24477
 
24128
24478
  def to_bytes(self):
24129
24479
  CHUNK_BEFORE_IDAT = {"cHRM", "gAMA", "iCCP", "sBIT", "sRGB", "bKGD", "hIST", "tRNS", "pHYs", "sPLT", "tIME", "PLTE"}
24130
- PNG_SIGN = b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"
24480
+ PNG_SIGN = b"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
24131
24481
  out = [PNG_SIGN]
24132
24482
  other_chunks = []
24133
24483
  seq = 0
@@ -25060,6 +25410,11 @@ class Animate3dObj(Animate3dBase):
25060
25410
  ):
25061
25411
  super().__init__(visible=visible, arg=arg, layer=layer, parent=parent, env=env, **kwargs)
25062
25412
 
25413
+ global pywavefront
25414
+ global visualization
25415
+ global pyglet
25416
+
25417
+
25063
25418
  self.x = x
25064
25419
  self.y = y
25065
25420
  self.z = z
@@ -25081,18 +25436,26 @@ class Animate3dObj(Animate3dBase):
25081
25436
  self.y_offset = 0
25082
25437
  self.z_offset = 0
25083
25438
 
25084
- if "pywavefront" not in sys.modules:
25085
- global pywavefront
25086
- global visualization
25087
- try:
25088
- import pywavefront
25089
- from pywavefront import visualization
25090
- except ImportError:
25091
- pywavefront = None
25439
+ try:
25440
+ import pywavefront
25441
+ except ImportError:
25442
+ pywavefront=None
25443
+
25444
+ try:
25445
+ import pyglet # this is a requirement for visualization!
25446
+ except ImportError:
25447
+ pyglet=None
25448
+
25449
+ from pywavefront import visualization
25092
25450
 
25093
25451
  def draw(self, t):
25452
+ global pywavefront
25453
+ global visualization
25454
+ global pyglet
25094
25455
  if pywavefront is None:
25095
25456
  raise ImportError("Animate3dObj requires pywavefront. Not found")
25457
+ if pyglet is None:
25458
+ raise ImportError("Animate3dObj requires pyglet. Not found")
25096
25459
 
25097
25460
  obj_filename = Path(self.filename(t))
25098
25461
  if not obj_filename.suffix:
@@ -26601,8 +26964,8 @@ def getfont(fontname, fontsize):
26601
26964
  return getfont.lookup[(fontname, fontsize)]
26602
26965
  else:
26603
26966
  getfont.lookup = {}
26604
- if fontname=="":
26605
- a=1
26967
+ if fontname == "":
26968
+ a = 1
26606
26969
  if isinstance(fontname, str):
26607
26970
  fontlist1 = [fontname]
26608
26971
  else:
@@ -26837,17 +27200,23 @@ def can_animate3d(try_only: bool = True) -> bool:
26837
27200
  import OpenGL.GL as gl
26838
27201
  import OpenGL.GLU as glu
26839
27202
  import OpenGL.GLUT as glut
27203
+ import OpenGL
26840
27204
  except ImportError:
26841
27205
  if try_only:
26842
27206
  return False
26843
27207
  else:
26844
27208
  raise ImportError("OpenGL is required for animation3d. Install with pip install PyOpenGL or see salabim manual")
27209
+ try:
27210
+ glut.glutInit()
27211
+ except OpenGL.error.NullFunctionError:
27212
+ raise ImportError("Installed OpenGL does not support glut. Try 'pip install OpenGL-glut' or see the salabim documentation")
27213
+
26845
27214
  return True
26846
27215
  else:
26847
27216
  if try_only:
26848
27217
  return False
26849
27218
  else:
26850
- raise ImportError("cannot even animate, let alone animate3d")
27219
+ raise ImportError("cannot animate, let alone animate3d")
26851
27220
 
26852
27221
 
26853
27222
  def can_video(try_only: bool = True) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: salabim
3
- Version: 24.0.13
3
+ Version: 24.0.14.post4
4
4
  Summary: salabim - discrete event simulation in Python
5
5
  Author-email: Ruud van der Ham <rt.van.der.ham@gmail.com>
6
6
  Project-URL: Homepage, https://salabim.org
@@ -46,7 +46,7 @@ In contrast to some other Python DES packages, salabim does not require the use
46
46
  ### Features and documentation
47
47
 
48
48
  - Cross-platform support: salabim runs on Windows, macOS, Linux, iOS/iPadOS (Pythonista), and can even be used with "Python In Excel".
49
- - Comprehensive documentation: Visit [www.salabim.org/manual](www.salabim.org/manual) for detailed documentation.
49
+ - Comprehensive documentation: Visit [www.salabim.org/manual](https://www.salabim.org/manual) for detailed documentation.
50
50
 
51
51
  ### Resources
52
52
 
@@ -0,0 +1,10 @@
1
+ salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
2
+ salabim/LICENSE.txt,sha256=qHlBa-POyexatCxDTjSKMlYtkBFQDn9lu-YV_1L6V0U,1106
3
+ salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
4
+ salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
5
+ salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
6
+ salabim/salabim.py,sha256=g2f1Dxf-6GkrS2AvM6EJhV7dta33KFqvsrdUKVUQYNI,1114883
7
+ salabim-24.0.14.post4.dist-info/METADATA,sha256=atwt9cNwaB9q37H5sqAzSRavt-KnAUuCRozQyUPRIXE,3464
8
+ salabim-24.0.14.post4.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
9
+ salabim-24.0.14.post4.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
10
+ salabim-24.0.14.post4.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,10 +0,0 @@
1
- salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
2
- salabim/LICENSE.txt,sha256=qHlBa-POyexatCxDTjSKMlYtkBFQDn9lu-YV_1L6V0U,1106
3
- salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
4
- salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
5
- salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
6
- salabim/salabim.py,sha256=NzhLkqRi8niqF9eQQPZRxJueQBROP0084q6qHrY6Qzc,1103953
7
- salabim-24.0.13.dist-info/METADATA,sha256=-xWemNdEOZ-EbstXw49IqGKhr97mg85QxyuDLa2LtGA,3450
8
- salabim-24.0.13.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
9
- salabim-24.0.13.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
10
- salabim-24.0.13.dist-info/RECORD,,