physicsLab 1.4.7__tar.gz → 1.4.8__tar.gz

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.
Files changed (39) hide show
  1. {physicslab-1.4.7 → physicslab-1.4.8}/PKG-INFO +1 -1
  2. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/__init__.py +2 -2
  3. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/_elementBase.py +0 -2
  4. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/wire.py +6 -3
  5. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/element.py +4 -6
  6. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/experiment.py +6 -1
  7. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/music/music.py +98 -70
  8. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab.egg-info/PKG-INFO +1 -1
  9. {physicslab-1.4.7 → physicslab-1.4.8}/setup.py +1 -1
  10. {physicslab-1.4.7 → physicslab-1.4.8}/LICENSE +0 -0
  11. {physicslab-1.4.7 → physicslab-1.4.8}/README.md +0 -0
  12. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/_colorUtils.py +0 -0
  13. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/_tools.py +0 -0
  14. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/celestial/__init__.py +0 -0
  15. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/celestial/elementsClass.py +0 -0
  16. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/__init__.py +0 -0
  17. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elementXYZ.py +0 -0
  18. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/__init__.py +0 -0
  19. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/artificialCircuit.py +0 -0
  20. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/basicCircuit.py +0 -0
  21. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/logicCircuit.py +0 -0
  22. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/circuit/elements/otherCircuit.py +0 -0
  23. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/electromagnetism/__init__.py +0 -0
  24. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/electromagnetism/elements.py +0 -0
  25. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/elementBase.py +0 -0
  26. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/errors.py +0 -0
  27. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/experimentType.py +0 -0
  28. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/lib/__init__.py +0 -0
  29. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/lib/_unionClassHead.py +0 -0
  30. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/lib/logic.py +0 -0
  31. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/lib/wires.py +0 -0
  32. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/music/__init__.py +0 -0
  33. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/savTemplate.py +0 -0
  34. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab/typehint.py +0 -0
  35. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab.egg-info/SOURCES.txt +0 -0
  36. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab.egg-info/dependency_links.txt +0 -0
  37. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab.egg-info/requires.txt +0 -0
  38. {physicslab-1.4.7 → physicslab-1.4.8}/physicsLab.egg-info/top_level.txt +0 -0
  39. {physicslab-1.4.7 → physicslab-1.4.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: physicsLab
3
- Version: 1.4.7
3
+ Version: 1.4.8
4
4
  Summary: Python API for Physics-Lab-AR
5
5
  Home-page: https://gitee.com/script2000/physicsLab
6
6
  Author: Goodenough
@@ -29,8 +29,8 @@ if platform.system() == "Windows":
29
29
  if not os.path.exists(Experiment.FILE_HEAD):
30
30
  raise RuntimeError("The folder does not exist, try launching Physics-Lab-AR and try it out")
31
31
  else:
32
- if not os.path.exists("physicsLabSav"):
33
- os.mkdir("physicsLabSav")
32
+ if not os.path.exists(Experiment.FILE_HEAD):
33
+ os.mkdir(Experiment.FILE_HEAD)
34
34
 
35
35
  # 获取 Physics-Lab-AR 版本
36
36
  def get_Physics_Lab_AR_version() -> Optional[str]:
@@ -95,8 +95,6 @@ class CircuitBase(ElementBase, metaclass=CircuitMeta):
95
95
  if elementXYZ is True or _elementXYZ.is_elementXYZ() is True and elementXYZ is None:
96
96
  x, y, z = _elementXYZ.xyzTranslate(x, y, z, self.is_bigElement)
97
97
  self.is_elementXYZ = True
98
- # if self.is_bigElement:
99
- # x, y, z = _elementXYZ.amend_big_Element(x, y, z)
100
98
 
101
99
  for _, self_list in stack_Experiment.top().elements_Position.items():
102
100
  if self in self_list:
@@ -2,7 +2,7 @@
2
2
  from physicsLab import errors
3
3
  from physicsLab.experiment import get_Experiment
4
4
  from physicsLab.experimentType import experimentType
5
- from physicsLab.typehint import WireDict, Optional
5
+ from physicsLab.typehint import WireDict, Optional, Callable
6
6
 
7
7
  # 电学元件引脚类, 模电元件引脚无明确的输入输出之分, 因此用这个
8
8
  class Pin:
@@ -56,9 +56,12 @@ class Wire:
56
56
  if not isinstance(Source, Pin) or not isinstance(Target, Pin):
57
57
  raise TypeError
58
58
 
59
- if Source.element_self.experiment != Target.element_self.experiment:
59
+ if Source.element_self.experiment is not Target.element_self.experiment:
60
60
  raise errors.ExperimentError("can't link wire in two experiment")
61
61
 
62
+ if Source == Target:
63
+ raise errors.ExperimentError()
64
+
62
65
  if color in ("black", "blue", "red", "green", "yellow"):
63
66
  color = {"black": "黑", "blue": "蓝", "red": "红", "green": "绿", "yellow": "黄"}[color]
64
67
  if color not in ('蓝', '绿', '黄', '红', '紫'):
@@ -101,7 +104,7 @@ class Wire:
101
104
  }
102
105
 
103
106
  # 检查函数参数是否是导线
104
- def _check_typeWire(func: callable):
107
+ def _check_typeWire(func: Callable):
105
108
  def result(SourcePin: Pin, TargetPin: Pin, *args, **kwargs) -> None:
106
109
  if not (
107
110
  isinstance(SourcePin, Pin) and
@@ -9,8 +9,6 @@ from physicsLab.experiment import stack_Experiment
9
9
  from physicsLab.typehint import numType, Optional, Union, List
10
10
  from physicsLab.circuit.elements._elementBase import CircuitBase
11
11
 
12
- NnumType = Optional[numType]
13
-
14
12
  def crt_Element(name: str,
15
13
  x: numType = 0,
16
14
  y: numType = 0,
@@ -39,11 +37,11 @@ def crt_Element(name: str,
39
37
  return eval(f"elements.{name.replace(' ', '_').replace('-', '_')}"
40
38
  f"({x}, {y}, {z}, {elementXYZ}, *{args}, **{kwargs})")
41
39
 
42
- def get_Element(x: NnumType=None,
43
- y: NnumType=None,
44
- z: NnumType=None,
40
+ def get_Element(x: Optional[numType] = None,
41
+ y: Optional[numType] = None,
42
+ z: Optional[numType] = None,
45
43
  *,
46
- index: NnumType=None,
44
+ index: Optional[numType] = None,
47
45
  **kwargs
48
46
  ) -> Union[CircuitBase, List[CircuitBase]]:
49
47
  ''' 获取对应坐标的id '''
@@ -45,10 +45,15 @@ def get_Experiment() -> "Experiment":
45
45
 
46
46
  class Experiment:
47
47
  ''' 实验(存档)类 '''
48
- FILE_HEAD = "physicsLabSav"
49
48
  if platform.system() == "Windows":
50
49
  from getpass import getuser
51
50
  FILE_HEAD = f"C:/Users/{getuser()}/AppData/LocalLow/CIVITAS/Quantum Physics/Circuit"
51
+ else:
52
+ _home = os.environ.get('PHYSICSLAB_HOME_PATH')
53
+ if _home is None:
54
+ FILE_HEAD = "physicsLabSav"
55
+ else:
56
+ FILE_HEAD = f"{_home}/physicsLabSav"
52
57
 
53
58
  def __init__(self, sav_name: Optional[str] = None) -> None:
54
59
  self.is_open_or_crt: bool = False
@@ -14,7 +14,6 @@ from physicsLab.typehint import Optional, Union, List, Iterator, Dict, Self, num
14
14
 
15
15
  def _format_velocity(velocity: float) -> float:
16
16
  velocity = min(1, velocity)
17
- velocity = max(0.05, velocity)
18
17
 
19
18
  return velocity
20
19
 
@@ -188,31 +187,35 @@ class Midi:
188
187
  return self
189
188
 
190
189
  # 返回 [Note(...), Chord(...), ...]
191
- def _get_notes_list(self, div_time: numType, max_notes: Optional[int]) -> List[Union["Note", "Chord"]]:
192
- res: List[Union[Note, Chord]] = []
190
+ def _get_notes_list(self,
191
+ div_time: numType, max_notes: Optional[int],
192
+ is_fix_strange_note: bool = False,
193
+ ) -> List[Union["Note", "Chord"]]:
193
194
 
195
+ res: List[Union[Note, Chord]] = []
194
196
  wait_time: int = 0
195
197
  len_res: int = 0
198
+
196
199
  for msg in self.messages:
197
- if msg.type == "note_on": # type: ignore -> Message/MetaMessage must have attr type
200
+ if msg.type == "note_on":
201
+ velocity: float = _format_velocity(msg.velocity / 127) # 音符的响度
202
+ ins: int = self.channels[msg.channel]
203
+
204
+ if velocity == 0 or (is_fix_strange_note and ins == 0 and velocity >= 0.85):
205
+ if msg.time != 0:
206
+ wait_time += msg.time
207
+ continue
208
+
198
209
  len_res += 1
199
210
  note_time = round((msg.time + wait_time) / div_time)
200
211
 
201
- velocity = _format_velocity(msg.velocity / 127) # 音符的响度
202
-
203
212
  if note_time != 0 or len(res) == 0:
204
213
  if note_time == 0:
205
214
  note_time = 1
206
- res.append(
207
- Note(note_time,
208
- instrument=self.channels[msg.channel],
209
- pitch=msg.note, velocity=velocity)
210
- )
215
+ res.append(Note(time=note_time, instrument=ins, pitch=msg.note, velocity=velocity))
211
216
  else:
212
217
  # res[-1]是`Note`或`Chord`且在赋值之后一定是Chord, 此时Note的time的值不重要(因为和弦的音符是同时播放的)
213
- res[-1] = res[-1].append(
214
- Note(time=1, instrument=self.channels[msg.channel], pitch=msg.note, velocity=velocity)
215
- )
218
+ res[-1] = res[-1].append(Note(time=1, instrument=ins, pitch=msg.note, velocity=velocity))
216
219
  wait_time = 0
217
220
  elif msg.time != 0:
218
221
  wait_time += msg.time
@@ -222,9 +225,15 @@ class Midi:
222
225
 
223
226
  return res
224
227
 
225
- # 转换为physicsLab的piece类
226
- def to_piece(self, div_time: numType = 100, max_notes: Optional[int] = 800) -> "Piece":
227
- return Piece(self._get_notes_list(div_time, max_notes))
228
+ def to_piece(self,
229
+ div_time: numType = 100,
230
+ max_notes: Optional[int] = 800,
231
+ is_optimize: bool = True, # 是否将多个音符优化为和弦
232
+ is_fix_strange_note: bool = False, # 是否修正一些奇怪的音符
233
+ ) -> "Piece":
234
+ ''' 转换为Piece类 '''
235
+ return Piece(self._get_notes_list(div_time, max_notes, is_fix_strange_note),
236
+ is_optimize=is_optimize)
228
237
 
229
238
  ''' *.pl.py文件:
230
239
  pl即为 physicsLab file
@@ -238,8 +247,8 @@ class Midi:
238
247
  而且是个Py文件, 大家想要自己修改也是很方便的
239
248
  '''
240
249
 
241
- # 导出一个 .mido.py 文件
242
250
  def write_midopy(self, path: str="temp.mido.py") -> Self:
251
+ ''' 导出一个 .mido.py 文件 '''
243
252
  if not path.endswith(".mido.py"):
244
253
  path += ".mido.py"
245
254
 
@@ -297,9 +306,9 @@ class Midi:
297
306
 
298
307
  return self
299
308
 
300
- # 音符类
301
309
  # TODO 增加更多的设置pitch的方法 -> 参考Simple_Instrument
302
310
  class Note:
311
+ ''' 音符类 '''
303
312
  def __init__(self,
304
313
  time: int, # 间隔多少时间才播放此Note
305
314
  playTime: int = 1, # 音符发出声音的时长 暂时不支持相关机制
@@ -330,8 +339,8 @@ class Note:
330
339
  def append(self, other: "Note") -> "Chord":
331
340
  return Chord(self, other, time=self.time)
332
341
 
333
- # 和弦类
334
342
  class Chord:
343
+ ''' 和弦类 '''
335
344
  def __init__(self, *notes: Note, time: int) -> None:
336
345
  if len(notes) < 1 or time < 1:
337
346
  raise TypeError
@@ -384,30 +393,50 @@ class Chord:
384
393
  x: numType = 0,
385
394
  y: numType = 0,
386
395
  z: numType = 0,
387
- elementXYZ: Optional[bool] = None
388
- ) -> elements.Simple_Instrument:
396
+ elementXYZ: Optional[bool] = None,
397
+ is_optimize: bool = True,
398
+ ) -> elements.Simple_Instrument:
389
399
  # 元件坐标系,如果输入坐标不是元件坐标系就强转为元件坐标系
390
400
  if not (elementXYZ is True or (_elementXYZ.is_elementXYZ() is True and elementXYZ is None)):
391
401
  x, y, z = _elementXYZ.translateXYZ(x, y, z)
392
402
  x, y, z = roundData(x, y, z) # type: ignore -> result type: tuple
393
403
 
394
- first_ins = None # 第一个音符
395
- for delta_z, ins in enumerate(self.ins_notes):
396
- notes: List[Note] = self.ins_notes[ins]
397
- temp: elements.Simple_Instrument = elements.Simple_Instrument(
398
- x, y, z + delta_z, elementXYZ=True,instrument=ins,
399
- pitch=notes[0].pitch, is_ideal_model=True, velocity=self._get_velocity(notes, is_average=True)
400
- ).set_Rotation(0, 0, 0)
401
- if first_ins is None:
402
- first_ins = temp
403
- else:
404
- temp.i - first_ins.i
405
- temp.o - first_ins.o
404
+ first_ins: Optional[elements.Simple_Instrument] = None # 第一个音符
405
+ if is_optimize:
406
+ for delta_z, ins in enumerate(self.ins_notes):
407
+ notes: List[Note] = self.ins_notes[ins]
408
+ temp: elements.Simple_Instrument = elements.Simple_Instrument(
409
+ x, y, z + delta_z, elementXYZ=True,instrument=ins,
410
+ pitch=notes[0].pitch, is_ideal_model=True, velocity=self._get_velocity(notes, is_average=True)
411
+ ).set_Rotation(0, 0, 0)
412
+
413
+ if first_ins is None:
414
+ first_ins = temp
415
+ else:
416
+ temp.i - first_ins.i
417
+ temp.o - first_ins.o
406
418
 
407
- for a_note in notes:
408
- temp.add_note(a_note.pitch) # type: ignore
419
+ for a_note in notes:
420
+ temp.add_note(a_note.pitch)
421
+ else:
422
+ delta_z = 0
423
+ for ins, notes in self.ins_notes.items():
424
+ for a_note in notes:
425
+ temp = elements.Simple_Instrument(
426
+ x, y, z + delta_z, elementXYZ=True,instrument=ins,
427
+ pitch=a_note.pitch, is_ideal_model=True, velocity=a_note.velocity
428
+ ).set_Rotation(0, 0, 0)
429
+
430
+ if first_ins is None:
431
+ first_ins = temp
432
+ else:
433
+ temp.i - first_ins.i
434
+ temp.o - first_ins.o
409
435
 
410
- return first_ins # type: ignore -> first_ins 不会是None
436
+ delta_z += 1
437
+
438
+ assert first_ins is not None
439
+ return first_ins
411
440
 
412
441
  # 循环类,用于创建一段循环的音乐片段
413
442
  # TODO: 完善Loop存储的数据结构
@@ -437,38 +466,36 @@ class Loop:
437
466
  def __next__(self):
438
467
  pass
439
468
 
440
- # 乐曲类
469
+ class Rest_symbol:
470
+ ''' 休止符 '''
471
+ __singleton: Optional[Self] = None
472
+ def __new__(cls) -> Self:
473
+ if cls.__singleton is None:
474
+ cls.__singleton = super().__new__(cls)
475
+ return cls.__singleton
476
+
441
477
  class Piece:
478
+ ''' 乐曲类 '''
442
479
  def __init__(self,
443
480
  notes: Optional[List[Union[Note, Chord]]] = None, # TODO: support Loop
444
- # 设置整个音轨的默认参数 Track global variable
445
- instrument: int = 0, # 演奏的乐器,暂时只支持传入数字
446
- pitch: int = 60, # 音高/音调
447
- bpm: int = 100, # 节奏
448
- volume: float = 1.0 # 音量/响度
449
- ) -> None:
450
- if not (
451
- isinstance(instrument, int) and
452
- isinstance(pitch, int) and
453
- isinstance(bpm, int) and
454
- 0 < volume <= 1
455
- ) or (
456
- ( not isinstance(notes, (list, Loop))
481
+ is_optimize: bool = True, # 是否将多个音符优化为和弦
482
+ ) -> None:
483
+ if (
484
+ ( not isinstance(notes, list)
457
485
  or not all(isinstance(val, (Note, Chord)) for val in notes) )
458
486
  and notes is not None
459
487
  ):
460
488
  raise TypeError
461
489
 
462
- if notes is None:
463
- notes = []
490
+ self.is_optimize = is_optimize
464
491
 
465
- self.instrument = instrument
466
- self.pitch = pitch
467
- self.bpm = bpm
468
- self.volume = volume
469
- self.notes: List[Optional[Union[Note, Chord]]] = []
470
- for a_note in notes:
471
- self.append(a_note)
492
+ # self.notes会将Note与Chord中用time表示的休止符展开为Rest_symbol
493
+ if notes is None:
494
+ self.notes = []
495
+ else:
496
+ self.notes: List[Union[Note, Chord, Rest_symbol]] = []
497
+ for a_note in notes:
498
+ self.append(a_note)
472
499
 
473
500
  # 向Piece类添加数据成员
474
501
  def append(self, other: Union[Note, Chord]) -> Self:
@@ -476,7 +503,7 @@ class Piece:
476
503
  raise TypeError
477
504
 
478
505
  while other.time > 1:
479
- self.notes.append(None)
506
+ self.notes.append(Rest_symbol())
480
507
  other.time -= 1
481
508
  self.notes.append(other)
482
509
  return self
@@ -521,7 +548,7 @@ class Piece:
521
548
  # 500_000 / 100, 500_000是Midi.tempo的默认数字,100是self.bpm的默认数字
522
549
  track.append(mido.MetaMessage("set_tempo", tempo=self.bpm * 5000, time=0))
523
550
  for a_note in self.notes:
524
- if a_note is None:
551
+ if isinstance(a_note, Rest_symbol):
525
552
  none_counter += 1
526
553
  elif isinstance(a_note, Chord):
527
554
  for note_list in a_note.ins_notes.values():
@@ -547,7 +574,7 @@ class Piece:
547
574
 
548
575
  # 将Piece转换为物实对应的电路
549
576
  def release(self, x: numType = 0, y: numType = 0, z: numType = 0, elementXYZ = None) -> "Player":
550
- return Player(self, x, y, z, elementXYZ)
577
+ return Player(self, x, y, z, elementXYZ, self.is_optimize)
551
578
 
552
579
  # Piece中所有Notes与Chord的数量
553
580
  def count_notes(self) -> int:
@@ -560,7 +587,7 @@ class Piece:
560
587
  def __len__(self) -> int:
561
588
  return len(self.notes)
562
589
 
563
- def __getitem__(self, item: int) -> Optional[Union[Note, Chord]]:
590
+ def __getitem__(self, item: int) -> Union[Note, Chord, Rest_symbol]:
564
591
  if not isinstance(item, int):
565
592
  raise TypeError
566
593
  return self.notes[item]
@@ -587,8 +614,9 @@ class Player:
587
614
  x: numType = 0,
588
615
  y: numType = 0,
589
616
  z: numType = 0,
590
- elementXYZ = None
591
- ) -> None:
617
+ elementXYZ = None,
618
+ is_optimize: bool = True,
619
+ ) -> None:
592
620
  from physicsLab.element import count_Elements
593
621
  count_elements_start: int = count_Elements()
594
622
 
@@ -604,9 +632,9 @@ class Player:
604
632
  x, y, z = _elementXYZ.translateXYZ(x, y, z)
605
633
 
606
634
  # 给乐器增加休止符
607
- while musicArray.notes[-1] is None:
635
+ while isinstance(musicArray.notes[-1], Rest_symbol):
608
636
  musicArray.notes.pop()
609
- while musicArray.notes[0] is None:
637
+ while isinstance(musicArray.notes[0], Rest_symbol):
610
638
  musicArray.notes.pop(0)
611
639
 
612
640
  len_musicArray: int = len(musicArray)
@@ -661,7 +689,7 @@ class Player:
661
689
  # main
662
690
  xcor, ycor = -1, 0
663
691
  for a_note in musicArray:
664
- if a_note is None:
692
+ if isinstance(a_note, Rest_symbol):
665
693
  xcor += 1
666
694
  if xcor == side:
667
695
  xcor = 0
@@ -675,7 +703,7 @@ class Player:
675
703
  xcor = 0
676
704
  ycor += 2
677
705
  if isinstance(a_note, Chord):
678
- ins = a_note.release(1 + x + xcor, 4 + y + ycor, z, elementXYZ=True)
706
+ ins = a_note.release(1 + x + xcor, 4 + y + ycor, z, elementXYZ=True, is_optimize=is_optimize)
679
707
  elif isinstance(a_note, Note):
680
708
  ins = elements.Simple_Instrument(
681
709
  1 + x + xcor, 4 + y + ycor, z, pitch=a_note.pitch,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: physicsLab
3
- Version: 1.4.7
3
+ Version: 1.4.8
4
4
  Summary: Python API for Physics-Lab-AR
5
5
  Home-page: https://gitee.com/script2000/physicsLab
6
6
  Author: Goodenough
@@ -3,7 +3,7 @@ import setuptools
3
3
 
4
4
  setuptools.setup(
5
5
  name="physicsLab",
6
- version="1.4.7",
6
+ version="1.4.8",
7
7
  license="MIT",
8
8
  author="Goodenough",
9
9
  author_email="2381642961@qq.com",
File without changes
File without changes
File without changes