Merge pull request #35 from shenjackyuanjie/feature/py_camera

Feature/py camera
This commit is contained in:
shenjack 2023-06-25 15:45:53 +08:00 committed by GitHub
commit c51b86ba9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 530 additions and 393 deletions

View File

@ -0,0 +1,123 @@
# -------------------------------
# Difficult Rocket
# Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
# All rights reserved
# -------------------------------
# Huge thanks to pyglet developers
from typing import Tuple, Optional
class Camera:
"""
>>> from pyglet.window import Window
>>> window = Window()
>>> camera = Camera(window)
>>> @window.event
>>> def on_draw():
>>> camera.begin()
>>> window.clear()
>>> camera.end()
"""
def __init__(self,
window,
zoom: Optional[float] = 1.0,
dx: Optional[float] = 1.0,
dy: Optional[float] = 1.0,
min_zoom: Optional[float] = 1.0,
max_zoom: Optional[float] = 1.0) -> None:
self.window = window
self.dx = dx
self.dy = dy
self.zoom = zoom
self.min_zoom = min_zoom
self.max_zoom = max_zoom
def get_view(self):
return self.window.view
@property
def position(self) -> Tuple[float, float]:
return self.dx, self.dy
@position.setter
def position(self, value: Tuple[float, float]):
self.dx, self.dy = value
@property
def zoom_level(self) -> float:
return self.zoom
@zoom_level.setter
def zoom_level(self, value: float) -> None:
self.zoom = min(max(value, self.min_zoom), self.max_zoom)
def begin(self) -> None:
view = self.window.view
x = self.window.width / self.zoom + (self.dx / self.zoom)
y = self.window.height / self.zoom + (self.dy / self.zoom)
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
self.window.view = view_matrix
def end(self) -> None:
view = self.window.view
x = self.window.width / self.zoom + (self.dx / self.zoom)
y = self.window.height / self.zoom + (self.dy / self.zoom)
view_matrix = view.scale((1.0 / self.zoom, 1.0 / self.zoom, 1))
view_matrix = view_matrix.translate((-x * self.zoom, -y * self.zoom, 0))
self.window.view = view_matrix
def __enter__(self):
self.begin()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.end()
class CenterCamera(Camera):
"""
A camera that centers the view on the center of the window
>>> from pyglet.window import Window
>>> window = Window()
>>> camera = CenterCamera(window)
>>> @window.event
>>> def on_draw():
>>> camera.begin()
>>> window.clear()
>>> camera.end()
"""
def begin(self) -> None:
view = self.window.view
x = self.window.width / 2.0 / self.zoom + (self.dx / self.zoom)
y = self.window.height / 2.0 / self.zoom + (self.dy / self.zoom)
view_matrix = view.translate((x * self.zoom, y * self.zoom, 0))
view_matrix = view_matrix.scale((self.zoom, self.zoom, 1))
self.window.view = view_matrix
def end(self) -> None:
view = self.window.view
x = self.window.width / 2.0 / self.zoom + (self.dx / self.zoom)
y = self.window.height / 2.0 / self.zoom + (self.dy / self.zoom)
view_matrix = view.scale((1.0 / self.zoom, 1.0 / self.zoom, 1))
view_matrix = view_matrix.translate((-x * self.zoom, -y * self.zoom, 0))
self.window.view = view_matrix

View File

@ -12,16 +12,16 @@
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a> <a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/) [![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/)
[![Generic badge](https://img.shields.io/badge/编写于_Python_版本-3.8.10-blue.svg)](https://Python.org) [![Generic badge](https://img.shields.io/badge/编写于_Python_版本-3.8.10-blue.svg)](https://Python.org)
[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.7-blue.svg)](https://pyglet.org) [![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.8-blue.svg)](https://pyglet.org)
[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org) [![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org)
## 版本 ## 版本
[关于版本号的说明](./docs/src/version.md) [关于版本号的说明](./docs/src/version.md)
[![Generic badge](https://img.shields.io/badge/Release-0.8.3.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.4-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Devloping-0.8.5-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark) [![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)

View File

@ -7,8 +7,8 @@ fonts_folder = "libs/fonts"
[window] [window]
style = "None" style = "None"
width = 1041 width = 1918
height = 1050 height = 2088
visible = true visible = true
gui_scale = 1 gui_scale = 1
caption = "Difficult Rocket v{DR_version}" caption = "Difficult Rocket v{DR_version}"

View File

@ -12,16 +12,16 @@
<a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a> <a href="https://996.icu"><img src="https://img.shields.io/badge/link-996.icu-red.svg" alt="996.icu" /></a>
[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/) [![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/)
[![Generic badge](https://img.shields.io/badge/Write_with_Python-3.8.10-blue.svg)](https://Python.org) [![Generic badge](https://img.shields.io/badge/Write_with_Python-3.8.10-blue.svg)](https://Python.org)
[![Generic badge](https://img.shields.io/badge/Write_with_Pyglet-2.0.5-blue.svg)](https://pyglet.org) [![Generic badge](https://img.shields.io/badge/Write_with_Pyglet-2.0.8-blue.svg)](https://pyglet.org)
[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org) [![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org)
## Version ## Version
[About Versions](src/version.md) [About Versions](src/version.md)
[![Generic badge](https://img.shields.io/badge/Release-0.8.3.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Pre_Release-0.8.4.0-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![Generic badge](https://img.shields.io/badge/Devloping-0.8.4-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) [![Generic badge](https://img.shields.io/badge/Devloping-0.8.5-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases)
[![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark) [![language badge](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)](https://stats.deeptrain.net/repo/shenjackyuanjie/Difficult-Rocket?theme=dark)

View File

@ -3,7 +3,27 @@
- 最新版本号 - 最新版本号
- DR game: 0.2.0.0 - DR game: 0.2.0.0
- DR rs: 0.2.11.0 - DR rs: 0.2.13.0
## DR game 0.2.1.0
### 修改
- 将 `sr1_ship` 中的 `Camera_rs` 改为 `Difficult_Rocket.utils.camera.Camera`
## DR rs 0.2.13.0
### 删除
- 删除了 `render.rs`
- 没必要拿 rust 写这玩意(
- 用 `DR game``camera` 代替
## DR rs 0.2.12.0
### 添加
- 添加了 xml 的读取测试
## DR game 0.1.2.0 ## DR game 0.1.2.0

View File

@ -2,13 +2,23 @@
# DR SDK 更新日志 # DR SDK 更新日志
- 最新版本号 - 最新版本号
- DR sdk: 0.8.3.0 - DR sdk: 0.8.4.0
## DR sdk 0.8.4.1
### Add
- `utils.camera`
- `Camera`
- 一个 2D 摄影机,可以用于高效变换渲染坐标
- `CenterCamera`
- 一个中心对器的 2D 摄影机,可以用于高效变换渲染坐标
## DR sdk 0.8.4.0 ## DR sdk 0.8.4.0
### Fix ### Fix
- issue #33 (https://github.com/shenjackyuanjie/Difficult-Rocket/issues/33) - issue #33 (<https://github.com/shenjackyuanjie/Difficult-Rocket/issues/33>)
- 修复了实际上并不会加载 `.otf` 格式的字体文件的问题 - 修复了实际上并不会加载 `.otf` 格式的字体文件的问题
### language ### language

View File

@ -9,7 +9,7 @@ import sys
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
#: The release version #: The release version
version = '2.0.7' version = '2.0.8'
__version__ = version __version__ = version
MIN_PYTHON_VERSION = 3, 8 MIN_PYTHON_VERSION = 3, 8

View File

@ -4,6 +4,7 @@ import fcntl
import ctypes import ctypes
import warnings import warnings
from os import readv
from ctypes import c_uint16 as _u16 from ctypes import c_uint16 as _u16
from ctypes import c_int16 as _s16 from ctypes import c_int16 as _s16
from ctypes import c_uint32 as _u32 from ctypes import c_uint32 as _u32
@ -306,7 +307,6 @@ class EvdevDevice(XlibSelectDevice, Device):
self._filename = filename self._filename = filename
fileno = os.open(filename, os.O_RDONLY) fileno = os.open(filename, os.O_RDONLY)
# event_version = EVIOCGVERSION(fileno).value
self._id = EVIOCGID(fileno) self._id = EVIOCGID(fileno)
self.id_bustype = self._id.bustype self.id_bustype = self._id.bustype
@ -357,6 +357,9 @@ class EvdevDevice(XlibSelectDevice, Device):
self.controls.sort(key=lambda c: c._event_code) self.controls.sort(key=lambda c: c._event_code)
os.close(fileno) os.close(fileno)
self._event_size = ctypes.sizeof(InputEvent)
self._event_buffer = (InputEvent * 64)()
super().__init__(display, name) super().__init__(display, name)
def get_guid(self): def get_guid(self):
@ -405,17 +408,16 @@ class EvdevDevice(XlibSelectDevice, Device):
return return
try: try:
events = (InputEvent * 64)() bytes_read = readv(self._fileno, self._event_buffer)
bytes_read = os.readv(self._fileno, events)
except OSError: except OSError:
self.close() self.close()
return return
n_events = bytes_read // ctypes.sizeof(InputEvent) n_events = bytes_read // self._event_size
for event in events[:n_events]:
for event in self._event_buffer[:n_events]:
try: try:
control = self.control_map[(event.type, event.code)] self.control_map[(event.type, event.code)].value = event.value
control.value = event.value
except KeyError: except KeyError:
pass pass
@ -651,5 +653,4 @@ def _create_controller(device):
def get_controllers(display=None): def get_controllers(display=None):
return [controller for controller in return [controller for controller in
[_create_controller(device) for device in get_devices(display)] [_create_controller(device) for device in get_devices(display)] if controller]
if controller is not None]

View File

@ -135,17 +135,6 @@ class AudioData:
self.duration -= num_bytes / audio_format.bytes_per_second self.duration -= num_bytes / audio_format.bytes_per_second
self.timestamp += num_bytes / audio_format.bytes_per_second self.timestamp += num_bytes / audio_format.bytes_per_second
def get_string_data(self):
"""Return data as a bytestring.
Returns:
bytes: Data as a (byte)string.
"""
if self.data is None:
return b''
return memoryview(self.data).tobytes()[:self.length]
class SourceInfo: class SourceInfo:
"""Source metadata information. """Source metadata information.
@ -409,7 +398,7 @@ class StaticSource(Source):
audio_data = source.get_audio_data(buffer_size) audio_data = source.get_audio_data(buffer_size)
if not audio_data: if not audio_data:
break break
data.write(audio_data.get_string_data()) data.write(audio_data.data)
self._data = data.getvalue() self._data = data.getvalue()
self._duration = len(self._data) / self.audio_format.bytes_per_second self._duration = len(self._data) / self.audio_format.bytes_per_second

View File

@ -6,7 +6,7 @@
from .lib import * from .lib import *
from typing import TYPE_CHECKING, Dict, Tuple, Optional from typing import TYPE_CHECKING, Dict, Tuple, Optional, List, Tuple
if TYPE_CHECKING: if TYPE_CHECKING:
@ -18,56 +18,11 @@ if TYPE_CHECKING:
def get_version_str() -> str: ... def get_version_str() -> str: ...
def simluation() -> None: ...
""" 用来测试 rust 的物理模拟能不能用 """
def part_list_read_test(file_name: Optional[str] = "./configs/PartList.xml") -> None: ... def part_list_read_test(file_name: Optional[str] = "./configs/PartList.xml") -> None: ...
def read_ship_test(path: Optional[str] = "./configs/dock1.xml") -> None: ... def read_ship_test(path: Optional[str] = "./configs/dock1.xml") -> None: ...
class Camera_rs: def load_and_save_test(file_name: str): ...
""" 用于闲的没事 用 rust 写一个 camera """
def __new__(cls, window: Window,
zoom: float = 1.0,
dx: float = 1.0, dy: float = 1.0,
min_zoom: float = 1.0,
max_zoom: float = 1.0): ...
@property
def dx(self) -> float: ...
@property
def dy(self) -> float: ...
@property
def zoom(self) -> float: ...
@property
def position(self) -> Tuple[float, float]: ...
@dx.setter
def dx(self, value: float) -> None: ...
@dy.setter
def dy(self, value: float) -> None: ...
@zoom.setter
def zoom(self, value: float) -> None: ...
def begin(self) -> None: ...
def end(self) -> None: ...
def __enter__(self, window) -> None: ...
def __exit__(self, exc_type, exc_val, exc_tb) -> None: ...
class CenterCamera_rs(Camera_rs):
""" 用于依旧闲的没事 用 rust 写一个中央对齐的 camera """
class PartFrame_rs:
...
class SR1PartType_rs: class SR1PartType_rs:
@ -107,6 +62,20 @@ if TYPE_CHECKING:
def get_part_type(self, name: str) -> SR1PartType_rs: ... def get_part_type(self, name: str) -> SR1PartType_rs: ...
class SR1PartData_rs:
""" 用于从 rust 中读取 SR1PartData (其实好像也没啥用哈)
"""
@property
def part_type_id(self) -> str: ...
@property
def pos(self) -> Tuple[float, float]: ...
@property
def angle(self) -> float: ...
@property
def flip_x(self) -> bool: ...
@property
def flip_y(self) -> bool: ...
class SR1Ship_rs: class SR1Ship_rs:
""" 用于高效且省内存的读取 SR1Ship """ """ 用于高效且省内存的读取 SR1Ship """
def __init__(self, file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip'): ... def __init__(self, file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip'): ...
@ -122,6 +91,8 @@ if TYPE_CHECKING:
def img_pos(self) -> Tuple[int, int, int, int]: ... def img_pos(self) -> Tuple[int, int, int, int]: ...
""" -x -y +x +y 左下右上 """ """ -x -y +x +y 左下右上 """
def get_part_box(self, part_id: int) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]: ... def get_part_box(self, part_id: int) -> Optional[Tuple[Tuple[int, int], Tuple[int, int]]]: ...
def as_dict(self) -> Dict[int, List[Tuple[SR1PartType_rs, SR1PartData]]]:
"""用于返回一个包含所有已连接零件的字典"""
class Console_rs: class Console_rs:
def __init__(self) -> None: ... def __init__(self) -> None: ...

View File

@ -91,7 +91,7 @@ dependencies = [
"autocfg", "autocfg",
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"memoffset 0.9.0", "memoffset",
"scopeguard", "scopeguard",
] ]
@ -119,6 +119,7 @@ name = "difficult_rocket_rs"
version = "0.2.5" version = "0.2.5"
dependencies = [ dependencies = [
"pyo3", "pyo3",
"quick-xml",
"rapier2d-f64", "rapier2d-f64",
"serde", "serde",
"serde-xml-rs", "serde-xml-rs",
@ -182,13 +183,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "memoffset" name = "memchr"
version = "0.8.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
@ -350,14 +348,14 @@ dependencies = [
[[package]] [[package]]
name = "pyo3" name = "pyo3"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b1ac5b3731ba34fdaa9785f8d74d17448cd18f30cf19e0c7e7b1fdb5272109" checksum = "cffef52f74ec3b1a1baf295d9b8fcc3070327aefc39a6d00656b13c1d0b8885c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"indoc", "indoc",
"libc", "libc",
"memoffset 0.8.0", "memoffset",
"parking_lot", "parking_lot",
"pyo3-build-config", "pyo3-build-config",
"pyo3-ffi", "pyo3-ffi",
@ -367,9 +365,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-build-config" name = "pyo3-build-config"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cb946f5ac61bb61a5014924910d936ebd2b23b705f7a4a3c40b05c720b079a3" checksum = "713eccf888fb05f1a96eb78c0dbc51907fee42b3377272dc902eb38985f418d5"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"target-lexicon", "target-lexicon",
@ -377,9 +375,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-ffi" name = "pyo3-ffi"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd4d7c5337821916ea2a1d21d1092e8443cf34879e53a0ac653fbb98f44ff65c" checksum = "5b2ecbdcfb01cbbf56e179ce969a048fd7305a66d4cdf3303e0da09d69afe4c3"
dependencies = [ dependencies = [
"libc", "libc",
"pyo3-build-config", "pyo3-build-config",
@ -387,9 +385,9 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-macros" name = "pyo3-macros"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d39c55dab3fc5a4b25bbd1ac10a2da452c4aca13bb450f22818a002e29648d" checksum = "b78fdc0899f2ea781c463679b20cb08af9247febc8d052de941951024cd8aea0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"pyo3-macros-backend", "pyo3-macros-backend",
@ -399,15 +397,25 @@ dependencies = [
[[package]] [[package]]
name = "pyo3-macros-backend" name = "pyo3-macros-backend"
version = "0.18.3" version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97daff08a4c48320587b5224cc98d609e3c27b6d437315bd40b605c98eeb5918" checksum = "60da7b84f1227c3e2fe7593505de274dcf4c8928b4e0a1c23d551a14e4e80a0f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
] ]
[[package]]
name = "quick-xml"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51"
dependencies = [
"memchr",
"serde",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.28" version = "1.0.28"

View File

@ -16,11 +16,11 @@ opt-level = 3
[profile.release] [profile.release]
strip = true strip = true
opt-level = "s" opt-level = "s"
codegen-units = 1 # codegen-units = 1
#[dependencies.quick-xml] [dependencies.quick-xml]
#version = "0.27.1" version = "0.29.0"
#features = ["serialize"] features = ["serialize"]
[dependencies.serde] [dependencies.serde]
version = "1.0.164" version = "1.0.164"
@ -37,5 +37,5 @@ version = "0.17.2"
features = ["simd-stable"] features = ["simd-stable"]
[dependencies.pyo3] [dependencies.pyo3]
version = "0.18.3" version = "0.19.0"
features = ["extension-module"] features = ["extension-module"]

View File

@ -9,10 +9,8 @@
mod logger; mod logger;
mod plugin; mod plugin;
mod python; mod python;
mod render;
mod simulator; mod simulator;
mod sr1_data; mod sr1_data;
mod translate;
mod types; mod types;
use pyo3::prelude::*; use pyo3::prelude::*;
@ -27,7 +25,7 @@ enum LoadState {
} }
#[pyfunction] #[pyfunction]
fn get_version_str() -> String { "0.2.11.0".to_string() } fn get_version_str() -> String { "0.2.13.0".to_string() }
#[pyfunction] #[pyfunction]
fn test_call(py_obj: &PyAny) -> PyResult<bool> { fn test_call(py_obj: &PyAny) -> PyResult<bool> {
@ -45,9 +43,8 @@ fn module_init(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(test_call, m)?)?; m.add_function(wrap_pyfunction!(test_call, m)?)?;
m.add_function(wrap_pyfunction!(sr1_data::part_list::read_part_list_py, m)?)?; m.add_function(wrap_pyfunction!(sr1_data::part_list::read_part_list_py, m)?)?;
m.add_function(wrap_pyfunction!(sr1_data::ship::py_raw_ship_from_file, m)?)?; m.add_function(wrap_pyfunction!(sr1_data::ship::py_raw_ship_from_file, m)?)?;
m.add_class::<render::camera::CameraRs>()?; m.add_function(wrap_pyfunction!(python::data::load_and_save_test, m)?)?;
m.add_class::<render::camera::CenterCameraRs>()?; m.add_function(wrap_pyfunction!(python::serde_test::test_ship_read_and_write, m)?)?;
m.add_class::<render::screen::PartFrame>()?;
m.add_class::<python::data::PySR1Ship>()?; m.add_class::<python::data::PySR1Ship>()?;
m.add_class::<python::data::PySR1PartList>()?; m.add_class::<python::data::PySR1PartList>()?;
m.add_class::<python::data::PySR1PartType>()?; m.add_class::<python::data::PySR1PartType>()?;

View File

@ -55,7 +55,6 @@ pub mod data {
#[pyclass] #[pyclass]
#[pyo3(name = "SR1PartList_rs")] #[pyo3(name = "SR1PartList_rs")]
#[pyo3(text_signature = "(file_path = './configs/PartList.xml', list_name = 'NewPartList')")]
pub struct PySR1PartList { pub struct PySR1PartList {
pub data: SR1PartList, pub data: SR1PartList,
} }
@ -63,6 +62,7 @@ pub mod data {
#[pymethods] #[pymethods]
impl PySR1PartList { impl PySR1PartList {
#[new] #[new]
#[pyo3(text_signature = "(file_path = './configs/PartList.xml', list_name = 'NewPartList')")]
fn new(file_path: String, list_name: String) -> Self { fn new(file_path: String, list_name: String) -> Self {
let raw_part_list: RawPartList = RawPartList::from_file(file_path).unwrap(); let raw_part_list: RawPartList = RawPartList::from_file(file_path).unwrap();
let data = raw_part_list.to_sr_part_list(Some(list_name)); let data = raw_part_list.to_sr_part_list(Some(list_name));
@ -74,7 +74,7 @@ pub mod data {
} }
fn get_part_type(&self, name: String) -> Option<PySR1PartType> { fn get_part_type(&self, name: String) -> Option<PySR1PartType> {
let part_type = self.data.get_part_type(name.clone()); let part_type = self.data.get_part_type(&name);
if let Some(part_type) = part_type { if let Some(part_type) = part_type {
Some(PySR1PartType::new(part_type.clone())) Some(PySR1PartType::new(part_type.clone()))
} else { } else {
@ -89,9 +89,30 @@ pub mod data {
pub data: SR1PartData, pub data: SR1PartData,
} }
impl PySR1PartData {
pub fn new(data: SR1PartData) -> Self { Self { data } }
}
#[pymethods]
impl PySR1PartData {
#[getter]
fn get_part_type_id(&self) -> String { self.data.part_type_id.clone() }
#[getter]
fn get_pos(&self) -> (f64, f64) { (self.data.x, self.data.y) }
#[getter]
fn get_angle(&self) -> f64 { self.data.angle }
#[getter]
fn get_flip_x(&self) -> bool { self.data.flip_x }
#[getter]
fn get_flip_y(&self) -> bool { self.data.flip_y }
}
#[pyclass] #[pyclass]
#[pyo3(name = "SR1Ship_rs")] #[pyo3(name = "SR1Ship_rs")]
#[pyo3(text_signature = "(file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip')")]
pub struct PySR1Ship { pub struct PySR1Ship {
pub ship: SR1Ship, pub ship: SR1Ship,
pub part_list: SR1PartList, pub part_list: SR1PartList,
@ -100,9 +121,11 @@ pub mod data {
#[pymethods] #[pymethods]
impl PySR1Ship { impl PySR1Ship {
#[new] #[new]
#[pyo3(text_signature = "(file_path = './configs/dock1.xml', part_list = './configs/PartList.xml', ship_name = 'NewShip')")]
fn new(file_path: String, part_list: String, ship_name: String) -> Self { fn new(file_path: String, part_list: String, ship_name: String) -> Self {
let ship = SR1Ship::from_file(file_path, Some(ship_name)).unwrap(); let mut ship = SR1Ship::from_file(file_path, Some(ship_name)).unwrap();
let part_list = SR1PartList::from_file(part_list).unwrap(); let part_list = SR1PartList::from_file(part_list).unwrap();
ship.parse_part_list_to_part(&part_list); //
Self { ship, part_list } Self { ship, part_list }
} }
@ -132,14 +155,28 @@ pub mod data {
#[getter] #[getter]
fn get_touch_ground(&self) -> bool { self.ship.touch_ground.to_owned() } fn get_touch_ground(&self) -> bool { self.ship.touch_ground.to_owned() }
fn iter_parts(&self) -> HashMap<i64, (PySR1PartType, PySR1PartData)> { #[getter]
let mut parts = HashMap::new(); fn get_mass(&self) -> f64 {
let mut mass = 0_f64;
for part_data in self.ship.parts.iter() { for part_data in self.ship.parts.iter() {
let part_type = self.part_list.get_part_type(part_data.part_type_id.clone()).unwrap(); let part_type = self.part_list.get_part_type(&part_data.part_type_id).unwrap();
parts.insert( mass += part_type.mass
part_data.id, }
(PySR1PartType::new(part_type), PySR1PartData { data: part_data.clone() }), mass
); }
fn as_dict(&self) -> HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> {
let mut parts: HashMap<i64, Vec<(PySR1PartType, PySR1PartData)>> = HashMap::new();
for part_data in self.ship.parts.iter() {
if let Some(part_type) = self.part_list.get_part_type(&part_data.part_type_id) {
let part_type = PySR1PartType::new(part_type.clone());
let py_part_data = PySR1PartData::new(part_data.clone());
if let Some(part_list) = parts.get_mut(&part_data.id) {
part_list.push((part_type, py_part_data));
} else {
parts.insert(part_data.id, vec![(part_type, py_part_data)]);
}
}
} }
parts parts
} }
@ -147,7 +184,7 @@ pub mod data {
fn get_part_box(&self, part_id: i64) -> Option<((f64, f64), (f64, f64))> { fn get_part_box(&self, part_id: i64) -> Option<((f64, f64), (f64, f64))> {
let part_data = self.ship.parts.iter().find(|&x| x.id == part_id); let part_data = self.ship.parts.iter().find(|&x| x.id == part_id);
if let Some(part_data) = part_data { if let Some(part_data) = part_data {
let part_type = self.part_list.get_part_type(part_data.part_type_id.clone()).unwrap(); let part_type = self.part_list.get_part_type(&part_data.part_type_id).unwrap();
// rotate // rotate
let radius = part_data.angle; let radius = part_data.angle;
let ((x1, y1), (x2, y2)) = part_type.get_box(); let ((x1, y1), (x2, y2)) = part_type.get_box();
@ -162,11 +199,21 @@ pub mod data {
} }
None None
} }
}
}
pub mod translate { fn save(&self, file_path: String) -> PyResult<()> {
use crate::translate; self.ship.save(file_path).unwrap();
Ok(())
}
}
#[pyfunction]
pub fn load_and_save_test(file_name: String) -> PyResult<()> {
use crate::sr1_data::ship::RawShip;
use serde_xml_rs::to_string;
let ship = RawShip::from_file(file_name).unwrap();
let _save_string = to_string(&ship);
Ok(())
}
} }
pub mod console { pub mod console {
@ -238,3 +285,127 @@ pub mod console {
} }
} }
} }
pub mod serde_test {
use pyo3::prelude::*;
use quick_xml::de::from_str;
use quick_xml::se::to_string;
use quick_xml::Writer;
use serde::{Deserialize, Serialize};
use std::fs;
type IdType = i64;
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename = "Ship")]
pub struct TestShip {
#[serde(rename = "@version")]
pub version: i32,
#[serde(rename = "@liftedOff")]
pub lift_off: i8,
#[serde(rename = "@touchingGround")]
pub touching_ground: i8,
#[serde(rename = "Connections")]
pub connections: Connections,
#[serde(rename = "DisconnectedParts")]
pub disconnected_parts: Option<DisconnectedParts>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DisconnectedParts {
#[serde(rename = "DisconnectedPart")]
pub disconnected_part: Option<Vec<DisconnectedPart>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct DisconnectedPart {
#[serde(rename = "Parts")]
pub parts: Parts,
#[serde(rename = "Connections")]
pub connections: Connections,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Connections {
#[serde(rename = "Connection")]
pub connection: Option<Vec<Connection>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Connection {
#[serde(rename = "@parentAttachPoint")]
pub parent_attach_point: i32,
#[serde(rename = "@childAttachPoint")]
pub child_attach_point: i32,
#[serde(rename = "@parentPart")]
pub parent_part: IdType,
#[serde(rename = "@childPart")]
pub child_part: IdType,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Parts {
#[serde(rename = "Part")]
pub part: Vec<Part>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Part {
// #[serde(rename = "@Tank")]
// pub tank: Option<Tank>,
// #[serde(rename = "@Engine")]
// pub engine: Option<Engine>,
// #[serde(rename = "@Pod")]
// pub pod: Option<Pod>,
#[serde(rename = "@partType")]
pub part_type_id: String,
#[serde(rename = "@id")]
pub id: i64,
#[serde(rename = "@x")]
pub x: f64,
#[serde(rename = "@y")]
pub y: f64,
#[serde(rename = "@editorAngle")]
pub editor_angle: i32,
#[serde(rename = "@angle")]
pub angle: f64,
#[serde(rename = "@angleV")]
pub angle_v: f64,
#[serde(rename = "@flippedX")]
pub flip_x: Option<i8>,
#[serde(rename = "@flippedY")]
pub flip_y: Option<i8>,
#[serde(rename = "@chuteX")]
pub chute_x: Option<f64>,
#[serde(rename = "@chuteY")]
pub chute_y: Option<f64>,
#[serde(rename = "@chuteAngle")]
pub chute_angle: Option<f64>,
#[serde(rename = "@chuteHeight")]
pub chute_height: Option<f64>,
#[serde(rename = "@extension")]
pub extension: Option<f64>,
#[serde(rename = "@inflate")]
pub inflate: Option<i8>,
#[serde(rename = "@inflation")]
pub inflation: Option<f64>,
#[serde(rename = "@exploded")]
pub exploded: Option<i8>,
#[serde(rename = "@rope")]
pub rope: Option<i8>,
#[serde(rename = "@activated")]
pub activated: Option<i8>,
#[serde(rename = "@deployed")]
pub deployed: Option<i8>,
}
#[pyfunction]
#[pyo3(name = "test_ship_read_and_write")]
pub fn test_ship_read_and_write(file_name: String) -> PyResult<()> {
let file = fs::read_to_string(file_name).unwrap();
let ship: TestShip = from_str(&file).unwrap();
let save_string = to_string(&ship).unwrap();
fs::write("./test-xml-rs.xml", save_string).unwrap();
Ok(())
}
}

View File

@ -1,212 +0,0 @@
/*
* -------------------------------
* Difficult Rocket
* Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
* All rights reserved
* -------------------------------
*/
pub mod camera {
use pyo3::intern;
use pyo3::prelude::*;
#[pyclass(name = "Camera_rs", subclass)]
pub struct CameraRs {
pub window: Py<PyAny>,
#[pyo3(get, set)]
pub dx: f64,
#[pyo3(get, set)]
pub dy: f64,
pub zoom: f64,
#[pyo3(get, set)]
pub max_zoom: f64,
#[pyo3(get, set)]
pub min_zoom: f64,
}
#[pyclass(extends = CameraRs, name = "CenterCamera_rs")]
pub struct CenterCameraRs;
#[pymethods]
impl CenterCameraRs {
#[new]
#[pyo3(signature = (window, zoom=1.0, dx=1.0, dy=1.0, min_zoom=1.0, max_zoom=1.0))]
pub fn py_new(window: &PyAny, zoom: f64, dx: f64, dy: f64, min_zoom: f64, max_zoom: f64) -> PyResult<(Self, CameraRs)> {
return Ok((
CenterCameraRs {},
CameraRs {
dx,
dy,
zoom,
min_zoom,
max_zoom,
window: window.into(),
},
));
}
// pub fn __enter__(py_self: PyRef<Self>) -> PyResult<PyRef<Self>> {
// // println!("enter!");
// CenterCameraRs::begin()?;
// Ok(py_self)
// }
pub fn begin(self_: PyRef<'_, Self>) -> PyResult<()> {
println!("begin!");
let super_: &CameraRs = self_.as_ref();
// 获取父类
Python::with_gil(|py| -> PyResult<()> {
let view = super_.window.getattr(py, intern!(py, "view"))?;
// 获取存储的 view
let x: f64 = super_.window.getattr(py, intern!(py, "width"))?.extract(py)?;
let y: f64 = super_.window.getattr(py, intern!(py, "height"))?.extract(py)?;
let x: f64 = x / 2.0 / super_.zoom + (super_.dx / super_.zoom);
let y: f64 = y / 2.0 / super_.zoom + (super_.dy / super_.zoom);
// 计算中心点
// view.call_method1(py, "translate", (x, y))?;
// view.call_method1(py, "scale", (super_.zoom, super_.zoom))?;
let args = ((x * super_.zoom, y * super_.zoom, 0),);
let view_matrix = view.call_method1(py, intern!(py, "translate"), args)?;
// view_matrix = self.view.translate((x * zoom, y * zoom, 0))
let args = ((super_.zoom, super_.zoom, 1),);
let view_matrix = view_matrix.call_method1(py, intern!(py, "scale"), args)?;
// view_matrix = view_matrix.scale((zoom, zoom, 1))
super_.window.setattr(py, intern!(py, "view"), view_matrix)?;
// self.view = view_matrix
Ok(())
})?;
Ok(())
}
}
#[pymethods]
impl CameraRs {
#[new]
#[pyo3(signature = (window, zoom=1.0, dx=1.0, dy=1.0, min_zoom=1.0, max_zoom=1.0))]
pub fn py_new(window: &PyAny, zoom: f64, dx: f64, dy: f64, min_zoom: f64, max_zoom: f64) -> PyResult<Self> {
return Ok(CameraRs {
dx,
dy,
zoom,
min_zoom,
max_zoom,
window: window.into(),
});
}
pub fn get_view(&self) -> PyResult<PyObject> {
Ok(Python::with_gil(|py| -> PyResult<PyObject> {
Ok(self.window.getattr(py, intern!(py, "view"))?)
})?)
}
#[getter]
pub fn get_position(&self) -> (f64, f64) { return (self.dx, self.dy); }
#[setter]
pub fn set_position(&mut self, value: (f64, f64)) -> () {
self.dx = value.0;
self.dy = value.1;
}
#[getter]
pub fn get_zoom(&self) -> PyResult<f64> { Ok(self.zoom) }
#[setter]
pub fn set_zoom(&mut self, value: f64) -> PyResult<()> {
self.zoom = value.min(self.max_zoom).max(self.min_zoom);
Ok(())
}
pub fn begin(&self) -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
let view = self.window.getattr(py, intern!(py, "view"))?;
let x: f64 = self.window.getattr(py, intern!(py, "width"))?.extract(py)?;
let y: f64 = self.window.getattr(py, intern!(py, "height"))?.extract(py)?;
let x: f64 = x / 2.0 / self.zoom + (self.dx / self.zoom);
let y: f64 = y / 2.0 / self.zoom + (self.dy / self.zoom);
// use to get center of the screen
let args = ((x * self.zoom, y * self.zoom, 0),);
let view_matrix = view.call_method1(py, intern!(py, "translate"), args)?;
// view_matrix = self.view.translate((x * zoom, y * zoom, 0))
let args = ((self.zoom, self.zoom, 1),);
let view_matrix = view_matrix.call_method1(py, intern!(py, "scale"), args)?;
// view_matrix = view_matrix.scale((zoom, zoom, 1))
self.window.setattr(py, intern!(py, "view"), view_matrix)?;
// self.view = view_matrix
Ok(())
})?;
return Ok(());
}
pub fn end(&self) -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
let view = self.window.getattr(py, intern!(py, "view"))?;
let x: f64 = self.window.getattr(py, intern!(py, "width"))?.extract(py)?;
let y: f64 = self.window.getattr(py, intern!(py, "height"))?.extract(py)?;
let x: f64 = x / 2.0 / self.zoom + (self.dx / self.zoom);
let y: f64 = y / 2.0 / self.zoom + (self.dy / self.zoom);
let args = ((1.0 / self.zoom, 1.0 / self.zoom, 1),);
let view_matrix = view.call_method1(py, intern!(py, "scale"), args)?;
let args = ((-x * self.zoom, -y * self.zoom, 0),);
let view_matrix = view_matrix.call_method1(py, intern!(py, "translate"), args)?;
self.window.setattr(py, intern!(py, "view"), view_matrix)?;
Ok(())
})?;
return Ok(());
}
/// https://github.com/PyO3/pyo3/discussions/2931#discussioncomment-4820729 for finding this
/// https://github.com/PyO3/pyo3/issues/1205#issuecomment-1164096251 for advice on `__enter__`
pub fn __enter__(py_self: PyRef<Self>) -> PyResult<PyRef<Self>> {
// println!("enter!");
py_self.begin()?;
Ok(py_self)
}
pub fn __exit__(&self, _exc_type: PyObject, _exc_value: PyObject, _traceback: PyObject) -> PyResult<()> {
// println!("exit!");
self.end()?;
return Ok(());
}
}
}
pub mod screen {
use pyo3::prelude::*;
// use crate::types::sr1::SR1PartData;
#[pyclass]
#[pyo3(name = "PartFrame_rs")]
pub struct PartFrame {
pub box_size: i32,
pub width: i64,
pub height: i64,
// pub frame_box: Vec<Vec<SR1PartData>>
}
#[pymethods]
impl PartFrame {
#[new]
pub fn py_new() -> PyResult<Self> {
Ok(PartFrame {
box_size: 111,
width: 111,
height: 111,
})
}
}
}

View File

@ -64,8 +64,38 @@ pub fn simulation() -> () {
} }
} }
#[allow(unused)] pub struct Simulator {
pub rigid_body_set: RigidBodySet,
pub collider_set: ColliderSet,
pub gravity: Vector<f64>,
pub integration_parameters: IntegrationParameters,
pub physics_pipeline: PhysicsPipeline,
pub island_manager: IslandManager,
pub broad_phase: BroadPhase,
pub narrow_phase: NarrowPhase,
pub impulse_joint_set: ImpulseJointSet,
pub multibody_joint_set: MultibodyJointSet,
pub ccd_solver: CCDSolver,
pub physics_hooks: (),
pub event_handler: (),
}
pub mod python { pub mod python {
use pyo3::prelude::*; use pyo3::prelude::*;
use rapier2d_f64::prelude::*; use rapier2d_f64::prelude::*;
#[pyclass]
pub struct PyIntegrationParameters {
pub data: IntegrationParameters,
}
#[pymethods]
impl PyIntegrationParameters {
#[new]
fn new() -> Self {
PyIntegrationParameters {
data: IntegrationParameters::default(),
}
}
}
} }

View File

@ -607,6 +607,18 @@ pub mod ship {
} }
} }
} }
#[inline]
pub fn save(&self, file_name: String) -> Option<()> {
let part_list_file = serde_xml_rs::to_string(self);
print!("{:?}", part_list_file);
if let Ok(part_list_file) = part_list_file {
fs::write(file_name, part_list_file).unwrap();
Some(())
} else {
None
}
}
} }
#[pyfunction] #[pyfunction]

View File

@ -1,16 +0,0 @@
/*
* -------------------------------
* Difficult Rocket
* Copyright © 2020-2023 by shenjackyuanjie 3695888@qq.com
* All rights reserved
* -------------------------------
*/
use std::collections::HashMap;
type TranslateMapper = HashMap<String, HashMap<String, HashMap<String, String>>>;
pub struct Translater {
pub data: TranslateMapper,
pub language: String,
}

View File

@ -246,9 +246,9 @@ pub mod sr1 {
} }
#[inline] #[inline]
pub fn get_part_type(&self, type_name: String) -> Option<SR1PartType> { pub fn get_part_type(&self, type_name: &String) -> Option<SR1PartType> {
let cache = self.get_cache(); let cache = self.get_cache();
match cache.get(&type_name) { match cache.get(type_name) {
Some(part) => Some(part.clone()), Some(part) => Some(part.clone()),
None => None, None => None,
} }
@ -699,11 +699,10 @@ pub mod sr1 {
Some(ship.to_sr_ship(ship_name)) Some(ship.to_sr_ship(ship_name))
} }
pub fn parse_part_list_to_part(&mut self, part_list: SR1PartList) { pub fn parse_part_list_to_part(&mut self, part_list: &SR1PartList) {
// parse parts // parse parts
for part in self.parts.iter_mut() { for part in self.parts.iter_mut() {
let part_type_id = part.part_type_id.clone(); if let Some(part_type) = part_list.get_part_type(&part.part_type_id) {
if let Some(part_type) = part_list.get_part_type(part_type_id) {
part.part_type = part_type.p_type; part.part_type = part_type.p_type;
} else { } else {
part.part_type = SR1PartTypeEnum::strut; part.part_type = SR1PartTypeEnum::strut;
@ -712,8 +711,7 @@ pub mod sr1 {
for disconnects in self.disconnected.iter_mut() { for disconnects in self.disconnected.iter_mut() {
for (parts, _) in disconnects.iter_mut() { for (parts, _) in disconnects.iter_mut() {
for part in parts.iter_mut() { for part in parts.iter_mut() {
let part_type_id = part.part_type_id.clone(); if let Some(part_type) = part_list.get_part_type(&part.part_type_id) {
if let Some(part_type) = part_list.get_part_type(part_type_id) {
part.part_type = part_type.p_type; part.part_type = part_type.p_type;
} else { } else {
part.part_type = SR1PartTypeEnum::strut; part.part_type = SR1PartTypeEnum::strut;
@ -722,6 +720,27 @@ pub mod sr1 {
} }
} }
} }
pub fn part_as_hashmap(&self) -> HashMap<i64, Vec<SR1PartData>> {
// 返回一个 HashMap 用于快速查找
// 同时为了 防止出现多个相同的 PartID 造成的数据丢失
// 采用 Vec 存储
let mut result: HashMap<i64, Vec<SR1PartData>> = HashMap::new();
for part_data in self.parts.iter() {
if let Some(part_vec) = result.get_mut(&part_data.id) {
part_vec.push(part_data.clone());
} else {
result.insert(part_data.id, vec![part_data.clone()]);
}
}
result
}
pub fn save(&self, file_name: String) -> Option<()> {
let raw_ship = self.to_raw_ship();
raw_ship.save(file_name);
Some(())
}
} }
impl SR1ShipTrait for SR1Ship { impl SR1ShipTrait for SR1Ship {
@ -780,7 +799,7 @@ pub mod sr1 {
pub fn get_max_box(parts: &Vec<SR1PartData>, part_list: &SR1PartList) -> (f64, f64, f64, f64) { pub fn get_max_box(parts: &Vec<SR1PartData>, part_list: &SR1PartList) -> (f64, f64, f64, f64) {
let mut max_box = (0_f64, 0_f64, 0_f64, 0_f64); let mut max_box = (0_f64, 0_f64, 0_f64, 0_f64);
for part in parts.iter() { for part in parts.iter() {
let part_type = part_list.get_part_type(part.part_type_id.clone()).unwrap(); let part_type = part_list.get_part_type(&part.part_type_id).unwrap();
let ((x1, y1), (x2, y2)) = part_type.get_box(); let ((x1, y1), (x2, y2)) = part_type.get_box();
// rotate // rotate
let mut p1 = Point2D::new(x1, y1); let mut p1 = Point2D::new(x1, y1);

View File

@ -17,7 +17,7 @@ from Difficult_Rocket.api.mod import ModInfo
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
from Difficult_Rocket.api.types import Options, Version from Difficult_Rocket.api.types import Options, Version
DR_rust_version = Version("0.2.11.0") # DR_mod 的 Rust 编写部分的兼容版本 DR_rust_version = Version("0.2.12.0") # DR_mod 的 Rust 编写部分的兼容版本
logger = logging.getLogger('client.dr_game') logger = logging.getLogger('client.dr_game')

View File

@ -8,6 +8,6 @@ xml.load_done = "XML 文件加载完成"
xml.load_time = "XML 文件加载消耗时间: {} 秒" xml.load_time = "XML 文件加载消耗时间: {} 秒"
ship.load = "正在加载飞船: {}" ship.load = "正在加载飞船: {}"
ship.load_time = "飞船加载消耗时间: {} 秒" ship.load_time = "飞船加载消耗时间: {} 秒"
#ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}\n- 文件大小: {}"
ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}" ship.info = "飞船信息:\n- 部件数量: {}\n- 部件重量: {}"
ship.render.done = "飞船渲染完成" ship.render.done = "飞船渲染完成"
save.start = "正在保存飞船: {}"

View File

@ -30,13 +30,17 @@ from Difficult_Rocket.utils.translate import Tr
from Difficult_Rocket.api.types import Fonts, Options from Difficult_Rocket.api.types import Fonts, Options
from Difficult_Rocket.command.line import CommandText from Difficult_Rocket.command.line import CommandText
from Difficult_Rocket.client.screen import BaseScreen from Difficult_Rocket.client.screen import BaseScreen
from Difficult_Rocket.utils.camera import CenterCamera
from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool from .types import SR1Textures, SR1PartTexture, SR1PartData, SR1Rotation, xml_bool
if TYPE_CHECKING: if TYPE_CHECKING:
from Difficult_Rocket.client import ClientWindow from Difficult_Rocket.client import ClientWindow
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
from .Difficult_Rocket_rs import CenterCamera_rs, SR1PartList_rs, SR1Ship_rs from .Difficult_Rocket_rs import (SR1PartList_rs,
SR1Ship_rs,
load_and_save_test,
test_ship_read_and_write)
logger = logging.getLogger('client.dr_game_sr1_ship') logger = logging.getLogger('client.dr_game_sr1_ship')
sr_tr = Tr(lang_path=Path('./mods/dr_game/lang')) sr_tr = Tr(lang_path=Path('./mods/dr_game/lang'))
@ -134,9 +138,9 @@ class SR1ShipRender(BaseScreen):
load_end_time = time.time_ns() load_end_time = time.time_ns()
logger.info(sr_tr().mod.info.setup.use_time().format( logger.info(sr_tr().mod.info.setup.use_time().format(
(load_end_time - load_start_time) / 1000000000)) (load_end_time - load_start_time) / 1000000000))
if DR_mod_runtime.use_DR_rust: self.camera = CenterCamera(main_window,
self.camera_rs = CenterCamera_rs(main_window,
min_zoom=(1 / 2) ** 10, max_zoom=10) min_zoom=(1 / 2) ** 10, max_zoom=10)
if DR_mod_runtime.use_DR_rust:
self.rust_parts = None self.rust_parts = None
self.part_list_rs = SR1PartList_rs('configs/PartList.xml', 'default_part_list') self.part_list_rs = SR1PartList_rs('configs/PartList.xml', 'default_part_list')
@ -150,6 +154,8 @@ class SR1ShipRender(BaseScreen):
self.xml_name = file_path self.xml_name = file_path
if DR_mod_runtime.use_DR_rust: if DR_mod_runtime.use_DR_rust:
try: try:
load_and_save_test(self.xml_name)
test_ship_read_and_write(self.xml_name)
self.rust_ship = SR1Ship_rs(file_path, 'configs/PartList.xml', 'a_new_ship') self.rust_ship = SR1Ship_rs(file_path, 'configs/PartList.xml', 'a_new_ship')
print(self.rust_ship.name) print(self.rust_ship.name)
print(self.rust_ship.img_pos) print(self.rust_ship.img_pos)
@ -228,10 +234,9 @@ class SR1ShipRender(BaseScreen):
self.part_data: Dict[int, SR1PartData] = {} self.part_data: Dict[int, SR1PartData] = {}
self.parts_sprite: Dict[int, Sprite] = {} self.parts_sprite: Dict[int, Sprite] = {}
self.part_line_box = {} self.part_line_box = {}
self.camera_rs.zoom = 1.0 self.camera.zoom = 1.0
if DR_mod_runtime.use_DR_rust: self.camera.dx = 0
self.camera_rs.dx = 0 self.camera.dy = 0
self.camera_rs.dy = 0
parts = self.xml_root.find('Parts') parts = self.xml_root.find('Parts')
for part_xml in parts: for part_xml in parts:
if part_xml.tag != 'Part': if part_xml.tag != 'Part':
@ -260,11 +265,11 @@ class SR1ShipRender(BaseScreen):
def update_parts(self) -> bool: def update_parts(self) -> bool:
if not self.rendered: if not self.rendered:
return False return False
self.debug_line.x2, self.debug_line.y2 = self.camera_rs.dx + ( self.debug_line.x2, self.debug_line.y2 = self.camera.dx + (
self.window_pointer.width / 2), self.camera_rs.dy + ( self.window_pointer.width / 2), self.camera.dy + (
self.window_pointer.height / 2) self.window_pointer.height / 2)
self.debug_d_pos_label.text = f'x: {self.camera_rs.dx} y: {self.camera_rs.dy}' self.debug_d_pos_label.text = f'x: {self.camera.dx} y: {self.camera.dy}'
self.debug_d_pos_label.position = self.camera_rs.dx + (self.window_pointer.width / 2), self.camera_rs.dy + ( self.debug_d_pos_label.position = self.camera.dx + (self.window_pointer.width / 2), self.camera.dy + (
self.window_pointer.height / 2) + 10, 0 self.window_pointer.height / 2) + 10, 0
self.need_update_parts = False self.need_update_parts = False
@ -283,7 +288,7 @@ class SR1ShipRender(BaseScreen):
self.update_parts() self.update_parts()
self.need_update_parts = False self.need_update_parts = False
with self.camera_rs: with self.camera:
self.part_box_batch.draw() self.part_box_batch.draw()
self.part_batch.draw() self.part_batch.draw()
self.part_box_batch.draw() self.part_box_batch.draw()
@ -317,29 +322,29 @@ class SR1ShipRender(BaseScreen):
mouse_dx = x - (window.width / 2) mouse_dx = x - (window.width / 2)
mouse_dy = y - (window.height / 2) mouse_dy = y - (window.height / 2)
# 鼠标缩放位置相对于屏幕中心的位置 # 鼠标缩放位置相对于屏幕中心的位置
mouse_dx_d = mouse_dx - self.camera_rs.dx mouse_dx_d = mouse_dx - self.camera.dx
mouse_dy_d = mouse_dy - self.camera_rs.dy mouse_dy_d = mouse_dy - self.camera.dy
# 鼠标相对偏移量的偏移量 # 鼠标相对偏移量的偏移量
if scroll_y == 0: if scroll_y == 0:
zoom_d = 1 zoom_d = 1
else: else:
zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1 zoom_d = ((2 ** scroll_y) - 1) * 0.5 + 1
# 缩放的变换量 # 缩放的变换量
if not (self.camera_rs.zoom == 10 and scroll_y > 0): if not (self.camera.zoom == 10 and scroll_y > 0):
if self.camera_rs.zoom * zoom_d >= 10: if self.camera.zoom * zoom_d >= 10:
zoom_d = 10 / self.camera_rs.zoom zoom_d = 10 / self.camera.zoom
self.camera_rs.zoom = 10 self.camera.zoom = 10
else: else:
self.camera_rs.zoom *= zoom_d self.camera.zoom *= zoom_d
mouse_dx_d *= (1 - zoom_d) mouse_dx_d *= (1 - zoom_d)
mouse_dy_d *= (1 - zoom_d) mouse_dy_d *= (1 - zoom_d)
self.camera_rs.dx += mouse_dx_d self.camera.dx += mouse_dx_d
self.camera_rs.dy += mouse_dy_d self.camera.dy += mouse_dy_d
self.debug_mouse_line.x2, self.debug_mouse_line.y2 = x, y self.debug_mouse_line.x2, self.debug_mouse_line.y2 = x, y
self.debug_mouse_delta_line.x2 = (mouse_dx - self.camera_rs.dx) * (1 - (0.5 ** scroll_y)) + ( self.debug_mouse_delta_line.x2 = (mouse_dx - self.camera.dx) * (1 - (0.5 ** scroll_y)) + (
window.width / 2) window.width / 2)
self.debug_mouse_delta_line.y2 = (mouse_dy - self.camera_rs.dy) * (1 - (0.5 ** scroll_y)) + ( self.debug_mouse_delta_line.y2 = (mouse_dy - self.camera.dy) * (1 - (0.5 ** scroll_y)) + (
window.height / 2) window.height / 2)
self.debug_mouse_label.text = f'x: {mouse_dx} y: {mouse_dy}' self.debug_mouse_label.text = f'x: {mouse_dx} y: {mouse_dy}'
self.debug_mouse_label.position = x, y + 10, 0 self.debug_mouse_label.position = x, y + 10, 0
@ -349,9 +354,9 @@ class SR1ShipRender(BaseScreen):
def on_command(self, command: CommandText, window: "ClientWindow"): def on_command(self, command: CommandText, window: "ClientWindow"):
if command.find('render'): if command.find('render'):
if command.find('reset'): if command.find('reset'):
self.camera_rs.zoom = 1 self.camera.zoom = 1
self.camera_rs.dx = 0 self.camera.dx = 0
self.camera_rs.dy = 0 self.camera.dy = 0
self.window_pointer.view = Vec4() self.window_pointer.view = Vec4()
else: else:
self.need_draw = True self.need_draw = True
@ -430,11 +435,20 @@ class SR1ShipRender(BaseScreen):
img.paste(pil_image, (int(self.part_data[part].x * 60 + img_center[0]), int(-self.part_data[part].y * 60 + img_center[1])), pil_image) img.paste(pil_image, (int(self.part_data[part].x * 60 + img_center[0]), int(-self.part_data[part].y * 60 + img_center[1])), pil_image)
img.save(f'test{time.time()}.png', 'PNG') img.save(f'test{time.time()}.png', 'PNG')
elif command.find('test'):
if command.find('save'):
if not self.rendered:
return
if not DR_mod_runtime.use_DR_rust:
return
logger.info(sr_tr().sr1.ship.save.start().format(self.rust_ship))
self.rust_ship.save('./test-save.xml')
def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"): def on_mouse_drag(self, x: int, y: int, dx: int, dy: int, buttons: int, modifiers: int, window: "ClientWindow"):
if not self.focus: if not self.focus:
return return
self.camera_rs.dx += dx self.camera.dx += dx
self.camera_rs.dy += dy self.camera.dy += dy
self.need_update_parts = True self.need_update_parts = True
# self.update_parts() # self.update_parts()