Skip to content

Commit 46720ef

Browse files
author
iperov
committed
initial commit
0 parents  commit 46720ef

1,804 files changed

Lines changed: 49387 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DeepixLab/DatasetEditor/FMaskEditor.py

Lines changed: 514 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
from __future__ import annotations
2+
3+
import math
4+
from enum import Enum, auto
5+
from functools import cached_property
6+
from typing import Self
7+
8+
from core import qx
9+
from core.lib.functools import cached_method
10+
from core.lib.math import (FAffMat2, FBBoxf, FCirclef, FRectf, FVec2f, FVec2i,
11+
FWorldProj2D)
12+
13+
14+
class FTransformEditor(qx.FBaseWorldViewWidget):
15+
"""transform editor model"""
16+
17+
class ViewType(Enum):
18+
FREE = auto()
19+
CENTER_FIT = auto()
20+
21+
class FDrag:
22+
def __init__(self, mouse_cli_pt : FVec2f, mouse_world_pt : FVec2f):
23+
self._mouse_cli_pt : FVec2f = mouse_cli_pt
24+
self._mouse_world_pt : FVec2f = mouse_world_pt
25+
26+
@property
27+
def mouse_cli_pt(self) -> FVec2f: return self._mouse_cli_pt
28+
@property
29+
def mouse_world_pt(self) -> FVec2f: return self._mouse_world_pt
30+
31+
def clone(self):
32+
f = self.__class__.__new__(self.__class__)
33+
f._mouse_cli_pt = self._mouse_cli_pt
34+
f._mouse_world_pt = self._mouse_world_pt
35+
return f
36+
37+
class FDragViewProj(FDrag):
38+
def __init__(self, mouse_cli_pt : FVec2f, mouse_world_pt : FVec2f,
39+
view_proj : FWorldProj2D,
40+
):
41+
super().__init__(mouse_cli_pt=mouse_cli_pt, mouse_world_pt=mouse_world_pt)
42+
self._view_proj = view_proj
43+
44+
@property
45+
def view_proj(self) -> FWorldProj2D: return self._view_proj
46+
47+
def clone(self):
48+
f = super().clone()
49+
f._view_proj = self._view_proj
50+
return f
51+
52+
class FDragImagePos(FDrag):
53+
def __init__(self, mouse_cli_pt : FVec2f, mouse_world_pt : FVec2f,
54+
m2w_img_mat : FAffMat2 ):
55+
super().__init__(mouse_cli_pt=mouse_cli_pt, mouse_world_pt=mouse_world_pt)
56+
self._m2w_img_mat = m2w_img_mat
57+
@property
58+
def m2w_img_mat(self) -> FAffMat2: return self._m2w_img_mat
59+
def clone(self):
60+
f = super().clone()
61+
f._m2w_img_mat = self._m2w_img_mat
62+
return f
63+
64+
class FDragImageScaleRotation(FDrag):
65+
def __init__(self, mouse_cli_pt : FVec2f, mouse_world_pt : FVec2f,
66+
m2w_img_mat : FAffMat2 ):
67+
super().__init__(mouse_cli_pt=mouse_cli_pt, mouse_world_pt=mouse_world_pt)
68+
self._m2w_img_mat = m2w_img_mat
69+
@property
70+
def m2w_img_mat(self) -> FAffMat2: return self._m2w_img_mat
71+
def clone(self):
72+
f = super().clone()
73+
f._m2w_img_mat = self._m2w_img_mat
74+
return f
75+
76+
77+
def __init__(self, W : int, H : int):
78+
super().__init__()
79+
self._view_type = self.ViewType.CENTER_FIT
80+
self._view_proj = FWorldProj2D().set_vp_size(self.cli_size)
81+
self._img_size = FVec2i(W, H)
82+
self._m2w_img_mat = FAffMat2()
83+
84+
self._drag : FTransformEditor.FDrag|None = None
85+
86+
def clone(self) -> FTransformEditor:
87+
f = super().clone()
88+
f._view_type = self._view_type
89+
f._view_proj = self._view_proj
90+
f._img_size = self._img_size
91+
f._m2w_img_mat = self._m2w_img_mat
92+
f._drag = self._drag
93+
return f
94+
95+
# FBaseWidget
96+
def set_cli_size(old_self, *args) -> Self:
97+
self = super().set_cli_size(*args)
98+
if not (self is old_self):
99+
self = self.clone()
100+
self._view_proj = self.view_proj.set_vp_size(self.cli_size)
101+
return self
102+
103+
# FBaseWorldViewWidget
104+
@property
105+
def w2cli_mat(self) -> FAffMat2: return self.view_proj.w2vp_mat
106+
107+
def mouse_move(self, cli_pt : FVec2f, ctrl_pressed=False, shift_pressed=False) -> Self:
108+
self = super().mouse_move(cli_pt, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
109+
110+
if (mouse_cli_pt := self.mouse_cli_pt) is not None:
111+
if isinstance(drag := self._drag, self.FDragViewProj):
112+
diff = drag.mouse_cli_pt - mouse_cli_pt
113+
view_proj = drag.view_proj
114+
self._view_proj = view_proj.set_w_view_pos(view_proj.vp2w_mat.map(diff+view_proj.w2vp_mat.map(view_proj.w_view_pos)))
115+
116+
elif isinstance(drag, self.FDragImagePos):
117+
if (mouse_world_pt := self.mouse_world_pt) is not None:
118+
self = self.clone()
119+
self._m2w_img_mat = drag.m2w_img_mat * FAffMat2().translate(mouse_world_pt - drag.mouse_world_pt)
120+
121+
elif isinstance(drag , self.FDragImageScaleRotation):
122+
img_pc = FVec2f(self._img_size)/2
123+
w_img_pc = drag.m2w_img_mat.map(img_pc)
124+
125+
cli_img_cp = self.w2cli_mat.map(w_img_pc)
126+
127+
vp_diff = mouse_cli_pt - cli_img_cp
128+
vp_drag_diff = drag.mouse_cli_pt - cli_img_cp
129+
130+
rot_diff = vp_diff.atan2() - vp_drag_diff.atan2()
131+
scale_diff = vp_diff.length / vp_drag_diff.length
132+
133+
self = self.clone()
134+
self._m2w_img_mat = drag.m2w_img_mat * FAffMat2().translate(-w_img_pc).rotate(rot_diff).scale(scale_diff).translate(w_img_pc)
135+
136+
return self
137+
138+
139+
def mouse_lbtn_down(self, cli_pt: FVec2f, ctrl_pressed=False, shift_pressed=False) -> Self:
140+
self = super().mouse_lbtn_down(cli_pt, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
141+
142+
if (mouse_cli_pt := self.mouse_cli_pt) is not None and \
143+
(mouse_world_pt := self.mouse_world_pt) is not None:
144+
145+
if self._drag is None:
146+
if self.is_hovering_ctrl_circle_inside():
147+
self = self .set_view_type(self.ViewType.FREE) \
148+
.set_drag(self.FDragImagePos(mouse_cli_pt=mouse_cli_pt, mouse_world_pt=mouse_world_pt, m2w_img_mat=self._m2w_img_mat))
149+
150+
# Clicking on ellipse edge
151+
if self.is_hovering_ctrl_circle_edge():
152+
self = self .set_view_type(self.ViewType.FREE) \
153+
.set_drag(self.FDragImageScaleRotation(mouse_cli_pt=mouse_cli_pt, mouse_world_pt=mouse_world_pt, m2w_img_mat=self._m2w_img_mat))
154+
return self
155+
156+
def mouse_lbtn_up(self, cli_pt: FVec2f, ctrl_pressed=False, shift_pressed=False) -> Self:
157+
self = super().mouse_lbtn_up(cli_pt, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
158+
if self._drag is not None:
159+
self = self.set_drag(None)
160+
return self
161+
162+
def mouse_mbtn_down(self, cli_pt: FVec2f, ctrl_pressed=False, shift_pressed=False) -> Self:
163+
self = super().mouse_mbtn_down(cli_pt, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
164+
if self._drag is None:
165+
self = self .set_view_type(self.ViewType.FREE) \
166+
.set_drag(self.FDragViewProj(mouse_cli_pt=self.mouse_cli_pt, mouse_world_pt=self.mouse_world_pt, view_proj=self.view_proj))
167+
return self
168+
169+
def mouse_mbtn_up(self, cli_pt: FVec2f, ctrl_pressed=False, shift_pressed=False) -> Self:
170+
self = super().mouse_mbtn_up(cli_pt, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
171+
if isinstance(self._drag, self.FDragViewProj):
172+
self = self.set_drag(None)
173+
return self
174+
175+
def mouse_wheel(self, cli_pt : FVec2f, delta : float, ctrl_pressed = False, shift_pressed = False) -> Self:
176+
self = super().mouse_wheel(cli_pt, delta, ctrl_pressed=ctrl_pressed, shift_pressed=shift_pressed)
177+
178+
if self._drag is None and \
179+
(mouse_cli_pt := self.mouse_cli_pt) is not None:
180+
self = self.set_view_type(self.ViewType.FREE).clone()
181+
self._view_proj = self.view_proj.scale_at(mouse_cli_pt, (1/1.15) if delta > 0 else 1.15)
182+
183+
return self
184+
185+
# FTransformEditor
186+
187+
@property
188+
def view_type(self) -> ViewType: return self._view_type
189+
def set_view_type(self, view_type : ViewType) -> Self:
190+
if self._view_type != view_type:
191+
self = self.clone()
192+
self._view_proj = self.view_proj
193+
self._view_type = view_type
194+
return self
195+
196+
197+
198+
@property
199+
def view_proj(self) -> FWorldProj2D:
200+
view_proj = self._view_proj
201+
if self._view_type == self.ViewType.CENTER_FIT:
202+
view_proj = view_proj.center_fit(FBBoxf(FVec2f(0,0), FVec2f(self._img_size)), coverage=1.5)
203+
return view_proj
204+
def set_view_proj(self, view_proj : ViewType) -> Self:
205+
if self._view_proj != view_proj:
206+
self = self.set_view_type(self.ViewType.FREE).clone()
207+
self._view_proj = view_proj
208+
return self
209+
210+
@property
211+
def drag(self) -> FDrag|None: return self._drag
212+
def set_drag(self, drag : FDrag|None):
213+
if not (self._drag is drag):
214+
self = self.clone()
215+
self._drag = drag
216+
return self
217+
218+
@property
219+
def img_size(self) -> FVec2i:
220+
return self._img_size
221+
222+
@property
223+
def result_uni_mat(self) -> FAffMat2:
224+
"""uniform mat to transform w_stencil_rect to w_img_rect"""
225+
return FAffMat2.estimate(self.w_stencil_rect, self.w_img_rect).scale_space(1/FVec2f(self.img_size))
226+
227+
@cached_property
228+
def w_img_rect(self) -> FRectf:
229+
"""image FRectf in world space"""
230+
return FRectf(self._img_size).transform(self._m2w_img_mat)
231+
@cached_property
232+
def cli_img_rect(self) -> FRectf:
233+
"""image FRectf in cli space"""
234+
return self.w_img_rect.transform(self.w2cli_mat)
235+
236+
@cached_property
237+
def w_stencil_rect(self) -> FRectf:
238+
"""stencil rect in world space"""
239+
return FRectf(self._img_size)
240+
@cached_property
241+
def cli_stencil_rect(self) -> FRectf:
242+
"""stencil rect in cli space"""
243+
return self.w_stencil_rect.transform(self.w2cli_mat)
244+
@cached_property
245+
def cli_ctrl_circle(self) -> FCirclef:
246+
"""ctrl FCirclef in cli space"""
247+
w_img_rect = self.w_img_rect
248+
return FCirclef(w_img_rect.pc, math.sqrt(2)*max(w_img_rect.width, w_img_rect.height)/2 ).transform(self.w2cli_mat)
249+
@cached_property
250+
def cli_ctrl_center_pt(self) -> FVec2f:
251+
"""ctrl center FVec2f point in vp space"""
252+
return self.w2cli_mat.map(self.w_img_rect.pc)
253+
254+
@cached_method
255+
def is_mouse_on_ctrl_circle_edge(self) -> bool:
256+
if (mouse_cli_pt := self.mouse_cli_pt) is not None:
257+
return self.cli_ctrl_circle.is_point_on_edge(mouse_cli_pt, 0, 20)
258+
return False
259+
@cached_method
260+
def is_mouse_on_ctrl_circle_inside(self) -> bool:
261+
if (mouse_cli_pt := self.mouse_cli_pt) is not None:
262+
return self.cli_ctrl_circle.is_point_inside(mouse_cli_pt)
263+
return False
264+
265+
def is_hovering_ctrl_circle_edge(self) -> bool: return self.is_mouse_on_ctrl_circle_edge()
266+
def is_hovering_ctrl_circle_inside(self) -> bool: return not self.is_hovering_ctrl_circle_edge() and self.is_mouse_on_ctrl_circle_inside()
267+
268+
269+
270+
def recover_from_undo_redo(self, current : FTransformEditor) -> Self:
271+
self = self.set_cli_size(current.cli_size)
272+
self = self.set_view_type(current.view_type).clone()
273+
self._view_proj = current.view_proj
274+
if (mouse_cli_pt := current.mouse_cli_pt) is not None:
275+
self = self.mouse_move(mouse_cli_pt)
276+
return self
277+
278+
279+
280+
def is_changed_for_undo(self, other : FTransformEditor) -> bool:
281+
if isinstance(drag := self.drag, (self.FDragImagePos, self.FDragImageScaleRotation)):
282+
return type(drag) != type(other.drag)
283+
else:
284+
return self._m2w_img_mat != other._m2w_img_mat
285+
286+
287+

0 commit comments

Comments
 (0)