3 """CtrModel edit TK widget.
5 CtrModel is widget for model edit.
17 from common.color import Color
18 from common.history import History
19 import common.model as model
20 from common.polygon import Polygon
21 from common.vertex import Vertex
27 COL_GRID_LINE1 = (32, 32, 32)
28 COL_GRID_LINE2 = (128, 128, 128)
37 __all__ = ["CtrModel"]
40 class CtrModel(Frame):
42 def _getdepth(self, x, y):
43 """get drawed object at position.
45 This method read depth buffer and return return tuple.
47 flag : flag(0:None, 1:Polygon, 2:Vertexmark)
51 return self.imdp.getpixel((x, y))
53 def _drawgrid(self, draw):
60 step = self.scale_self * GRID_STEP
62 end = (((max(self.width, self.height) // 2) // step) + 1) * step
63 for d in range(begin, end, step):
64 draw.line((cx + d, 0, cx + d, self.height), fill=col)
65 draw.line((cx - d, 0, cx - d, self.height), fill=col)
66 draw.line((0, cy + d, self.width, cy + d), fill=col)
67 draw.line((0, cy - d, self.width, cy - d), fill=col)
70 step = self.scale_self * GRID_STEP
72 end = (((max(self.width, self.height) // 2) // step) + 1) * step
73 for d in range(begin, end, step):
74 draw.line((cx + d, 0, cx + d, self.height), fill=col)
75 draw.line((cx - d, 0, cx - d, self.height), fill=col)
76 draw.line((0, cy + d, self.width, cy + d), fill=col)
77 draw.line((0, cy - d, self.width, cy - d), fill=col)
79 def _drawmodel(self, md):
82 draw = ImageDraw.Draw(self.im)
83 drawdp = ImageDraw.Draw(self.imdp) # depth buffer
85 box = (0, 0, w - 1, h - 1)
86 draw.rectangle(box, fill=COL_BACK)
87 drawdp.rectangle(box, fill=(FLAG_NONE, 0, 0))
90 for p_idx, polygon in enumerate(md.polygons):
91 vl = [(v.x, v.y) for v in polygon.vertexs]
92 fill = tuple(polygon.fill)
93 outline = tuple(polygon.outline)
94 draw.polygon(vl, fill=fill, outline=outline)
95 drawdp.polygon(vl, fill=(FLAG_POLYGON, p_idx, 0))
96 for v_idx, vertex in enumerate(polygon.vertexs):
100 mark = Mark(x, y, p_idx, v_idx)
101 box = (x - r, y - r, x + r, y + r)
102 drawdp.chord(box, 0, 360,
103 fill=(FLAG_VERTEXMARK, p_idx, v_idx))
104 if self.mark_display:
105 if self.is_markselected(p_idx, v_idx):
106 fill = Mark.getoutline()
107 outline = Mark.getfill()
109 fill = Mark.getfill()
110 outline = Mark.getoutline()
111 draw.chord(box, 0, 360, fill=fill, outline=outline)
112 self.marks.append(mark)
114 def _drawcalcmodel(self):
115 """scale, move and draw model
117 move = (- self.width / 2, - self.height / 2, 0)
118 mdc = self.md.calc(move, None, None, None, self.scale_self, None)
121 def __init__(self, master=None, fname='', width=480, height=480, scale=4):
122 Frame.__init__(self, master)
126 self.scale_self = scale
129 self.filename = "%s/%s" % (settings.DATAPATH, fname)
130 self.md = model.load(self.filename)
132 self.filename = "%s" % settings.DATAPATH
133 self.md = model.new()
135 self.marks_selected = []
136 self.click_pos = None
138 self.mark_display = True
141 self.history = History()
145 self.imdp = Image.new('RGB', (self.width, self.height), (0, 0, 0))
146 self.im = Image.new('RGB', (self.width, self.height), (0, 0, 0))
147 self._drawcalcmodel()
148 self.imagetk = ImageTk.PhotoImage(self.im)
151 self.la = Label(self,
156 self.la.bind("<Button-1>", self.selectmark)
157 self.la.bind("<Control-Button-1>", self.addmark)
158 self.la.bind("<Button-2>", self.shortcut)
159 self.la.bind("<Button-3>", self.popupmenu)
160 self.la.bind("<B1-Motion>", self.movevertex)
161 self.la.bind("<B2-Motion>", self.movevertex)
162 self.la.bind("<ButtonRelease-1>", self.release)
163 self.la.bind("<ButtonRelease-2>", self.release)
164 self.la.pack(side=LEFT)
167 self.mn_polygon = Menu(self, tearoff=0)
168 self.mn_polygon.add_command(label="Bring to front",
170 self.mn_polygon.add_command(label="Bring forward",
171 command=self.forward)
172 self.mn_polygon.add_command(label="Send backward",
173 command=self.backward)
174 self.mn_polygon.add_command(label="Send to back",
176 self.mn_polygon.add("separator")
177 self.mn_polygon.add_command(label="clone", command=self.clonepolygon)
180 self.mn_vertex = Menu(self, tearoff=0)
181 self.mn_vertex.add_command(label="copy this vertex",
182 command=self.addvertex)
183 self.mn_vertex.add_command(label="del this vertex",
184 command=self.delvertex)
186 def getundodata(self):
187 """get undo data object
191 'height': self.height,
192 'scale_self': self.scale_self,
193 'filename': self.filename,
196 'marks_selected': self.marks_selected,
197 'click_pos': self.click_pos,
198 'mark_display': self.mark_display,
201 def setundodata(self, data):
202 """set undo data object
204 self.width = data['width']
205 self.height = data['height']
206 self.scale_self = data['scale_self']
207 self.filename = data['filename']
209 self.marks = data['marks']
210 self.marks_selected = data['marks_selected']
211 self.click_pos = data['click_pos']
212 self.mark_display = data['mark_display']
216 self.history.push(self.getundodata())
220 self.setundodata(self.history.undo())
225 self.setundodata(self.history.redo())
228 def is_markselected(self, p_idx, v_idx):
229 for mark in self.marks_selected:
230 if mark.issame(p_idx, v_idx):
234 def search_mark(self, p_idx, v_idx):
235 for mark in self.marks:
236 if not mark.issame(p_idx, v_idx):
242 self._drawcalcmodel()
243 self.imagetk = ImageTk.PhotoImage(self.im)
244 self.la.config(image=self.imagetk)
247 def getfilename(self):
250 def load(self, filename):
251 self.md = model.load(filename)
252 self.filename = filename
258 model.save(self.filename, self.md)
260 def saveas(self, filename):
261 model.save(filename, self.md)
262 self.filename = filename
265 if len(self.marks_selected):
266 p_idx = self.marks_selected[-1].p_idx
267 fill = self.md.getfill(p_idx)
268 return (fill.r, fill.g, fill.b)
272 def setfill(self, r, g, b):
273 if len(self.marks_selected):
274 p_idx = self.marks_selected[-1].p_idx
275 self.md.setfill(p_idx, Color(r, g, b))
279 def getoutline(self):
280 if len(self.marks_selected):
281 p_idx = self.marks_selected[-1].p_idx
282 outline = self.md.getoutline(p_idx)
283 return (outline.r, outline.g, outline.b)
287 def setoutline(self, r, g, b):
288 if len(self.marks_selected):
289 p_idx = self.marks_selected[-1].p_idx
290 self.md.setoutline(p_idx, Color(r, g, b))
295 self.mark_display = True
299 self.mark_display = False
305 def setname(self, name):
309 def addpolygon(self):
310 if len(self.md.polygons) < MAX_POLYGONS:
311 if len(self.marks_selected):
312 p_idx = self.marks_selected[-1].p_idx
321 self.md.addPolygon(p_idx, polygon)
325 def clonepolygon(self):
326 if len(self.md.polygons) < MAX_POLYGONS:
327 if len(self.marks_selected):
328 p_idx = self.marks_selected[-1].p_idx
332 polygon = copy.deepcopy(self.md.polygons[p_idx])
333 self.md.addPolygon(p_idx, polygon)
337 def delpolygon(self):
338 if len(self.md.polygons) > 1:
339 if len(self.marks_selected):
340 p_idx = self.marks_selected[-1].p_idx
341 self.md.delPolygon(p_idx)
342 self.marks_selected = []
348 if not self.click_pos:
350 mx = self.click_pos['x']
351 my = self.click_pos['y']
352 (flag, p_idx, v_idx) = self._getdepth(mx, my)
353 if flag == FLAG_POLYGON:
355 elif flag == FLAG_VERTEXMARK:
356 if self._lenvertexs(p_idx) < 256:
357 mark = self.search_mark(p_idx, v_idx)
358 self.marks_selected = [mark]
359 v = self.md.getvertex(p_idx, v_idx)
360 self.md.addvertex(p_idx, v_idx, Vertex(v.x, v.y, v.z))
361 mark.v_idx = v_idx + 1
366 if not self.click_pos:
368 mx = self.click_pos['x']
369 my = self.click_pos['y']
370 self.click_pos = None
371 (flag, p_idx, v_idx) = self._getdepth(mx, my)
372 if flag == FLAG_POLYGON:
374 elif flag == FLAG_VERTEXMARK:
375 if self._lenvertexs(p_idx) > 3:
376 self.md.delvertex(p_idx, v_idx)
377 self.marks_selected = []
378 self.click_pos = None
383 def _lenpolygons(self):
384 return len(self.md.polygons)
386 def _lenvertexs(self, p_idx):
387 return len(self.md.polygons[p_idx].vertexs)
389 def _addmarkpolygon(self, p_idx):
390 """add mark to maks_selected
392 for mark in self.marks:
393 if mark.p_idx == p_idx:
394 self.marks_selected.append(mark)
396 def _delmarkpolygon(self, p_idx):
397 """del mark to maks_selected
400 for mark in self.marks_selected:
401 if mark.p_idx != p_idx:
403 self.marks_selected = marks
405 def _ismarkpolygon(self, p_idx):
406 """is polygon selected
409 for mark in self.marks_selected:
410 if mark.p_idx == p_idx:
411 vidxs.append(mark.v_idx)
413 for mark in self.marks:
414 if mark.p_idx == p_idx:
415 if mark.v_idx in vidxs:
422 """sync mark position
424 for mark in self.marks_selected:
425 org = self.search_mark(mark.p_idx, mark.v_idx)
433 for mark in self.marks_selected:
434 if mark.p_idx in pidxs:
436 pidxs.append(mark.p_idx)
440 def _screentomodel(self, sx, sy):
441 x = (sx - self.width / 2) / self.scale_self
442 y = (sy - self.height / 2) / self.scale_self
452 def isscalemax(self):
453 if self.scale_self == self.scalemax():
457 def isscalemin(self):
458 if self.scale_self == self.scalemin():
462 def setscale(self, scale):
463 self.scale_self = scale
464 self.marks_selected = []
469 def update_markidx(self, p_idx, new_idx):
470 for mark in self.marks_selected:
471 if mark.p_idx == p_idx:
475 pidxs = self._pidxlist()
477 ismove = self.md.backward(p_idx)
479 self.update_markidx(p_idx, p_idx - 1)
484 pidxs = self._pidxlist()
487 ismove = self.md.forward(p_idx)
489 self.update_markidx(p_idx, p_idx + 1)
494 pidxs = self._pidxlist()
496 for idx, p_idx in enumerate(pidxs):
497 self.md.back(p_idx + idx)
499 for idx, p_idx in enumerate(pidxs):
500 self.update_markidx(p_idx, idx + self._lenpolygons())
501 for idx, p_idx in enumerate(pidxs):
502 self.update_markidx(idx + self._lenpolygons(), idx)
507 pidxs = self._pidxlist()
508 for idx, p_idx in enumerate(pidxs):
509 self.md.front(p_idx - idx)
511 for idx, p_idx in enumerate(pidxs):
512 self.update_markidx(p_idx, idx + self._lenpolygons())
513 for idx, p_idx in enumerate(pidxs):
514 self.update_markidx(idx + self._lenpolygons(),
515 self._lenpolygons() - len(pidxs) + idx)
520 def selectmark(self, event):
521 self.marks_selected = []
524 def addmark(self, event):
527 self.click_pos = None
528 (flag, p_idx, v_idx) = self._getdepth(mx, my)
529 if flag == FLAG_NONE:
531 elif flag == FLAG_POLYGON:
532 if self._ismarkpolygon(p_idx):
533 self._delmarkpolygon(p_idx)
535 self._addmarkpolygon(p_idx)
538 elif flag == FLAG_VERTEXMARK:
539 mark_new = self.search_mark(p_idx, v_idx)
540 for mark in self.marks_selected:
541 if mark.issame(p_idx, v_idx):
542 self.marks_selected.remove(mark)
545 self.marks_selected.append(mark_new)
557 def movevertex(self, event):
558 if len(self.marks_selected) and self.click_pos:
561 ox = self.click_pos['x']
562 oy = self.click_pos['y']
565 for mark in self.marks_selected:
568 v = self.md.getvertex(p_idx, v_idx)
569 v.x, v.y = self._screentomodel(mark.x + rx, mark.y + ry)
577 def release(self, event):
578 if self.click_pos and self.drag_pos:
580 self.click_pos = None
583 def shortcut(self, event):
584 self.marks_selected = []
587 self.click_pos = None
588 (flag, p_idx, v_idx) = self._getdepth(mx, my)
589 if flag == FLAG_POLYGON:
591 elif flag == FLAG_VERTEXMARK:
598 def popupmenu(self, event):
601 self.click_pos = None
602 (flag, p_idx, v_idx) = self._getdepth(mx, my)
603 if flag == FLAG_POLYGON:
604 self.mn_polygon.post(event.x_root, event.y_root)
605 elif flag == FLAG_VERTEXMARK:
610 self.mn_vertex.post(event.x_root, event.y_root)
613 self.center(move_x=True)
616 self.center(move_y=True)
619 self.center(move_x=True, move_y=True)
621 def center(self, move_x=False, move_y=False, move_z=False):
624 This method move selected polygons to center.
626 if not len(self.marks_selected):
628 pidxs = self._pidxlist()
637 for v in self.md.polygons[pidx].vertexs:
638 x_max = max(v.x, x_max)
639 x_min = min(v.x, x_min)
640 y_max = max(v.y, y_max)
641 y_min = min(v.y, y_min)
642 z_max = max(v.z, z_max)
643 z_min = min(v.z, z_min)
645 x_ave = (x_min + x_max) // 2
646 y_ave = (y_min + y_max) // 2
647 z_ave = (z_min + z_max) // 2
660 for v in self.md.polygons[pidx].vertexs:
669 def flipcopy_lr(self):
672 # vertex in left side
675 self.flipcopy(-1, 1, 1, check)
677 def flipcopy_rl(self):
680 # vertex in right side
683 self.flipcopy(-1, 1, 1, check)
685 def flipcopy_ud(self):
691 self.flipcopy(1, -1, 1, check)
693 def flipcopy_du(self):
696 # vertex in down side
699 self.flipcopy(1, -1, 1, check)
701 def flipcopy(self, flip_x, flip_y, flip_z, func_check):
702 """flip copy left to right
704 Delete vertexs in one side, copy and flip vertexs in other side
705 and join these vertexs.
707 if len(self.marks_selected):
708 edited_pidxs = [] # edited polygon idx
709 pidxs = self._pidxlist()
716 for v_idx, v in enumerate(self.md.polygons[p_idx].vertexs):
717 if not func_check(v):
722 vc = Vertex(v.x, v.y, v.z)
723 vertexs_copy.append(vc)
727 vertexs_copy = (vertexs_copy[other_idx:] +
728 vertexs_copy[:other_idx])
732 for v in vertexs_copy:
733 vf = Vertex(v.x * flip_x, v.y * flip_y, v.z * flip_z)
734 vertexs_flip.insert(0, vf)
738 vertexs_copy.extend(vertexs_flip)
739 polygon_flip = Polygon(vertexs_copy,
740 self.md.getfill(p_idx),
741 self.md.getoutline(p_idx),
742 self.md.polygons[p_idx].name)
743 self.md.addPolygon(p_idx, polygon_flip)
744 self.md.delPolygon(p_idx)
745 edited_pidxs.append(p_idx)
748 polygon_flip = Polygon(vertexs_flip,
749 self.md.getfill(p_idx),
750 self.md.getoutline(p_idx),
751 self.md.polygons[p_idx].name)
752 self.md.addPolygon(p_idx, polygon_flip)
753 edited_pidxs = [idx + 1 for idx in edited_pidxs]
754 edited_pidxs.append(p_idx)
755 edited_pidxs.append(p_idx + 1)
759 self.marks_selected = []
760 for p_idx in edited_pidxs:
761 self._addmarkpolygon(p_idx)
766 def smoothpolygon(self, div=5):
767 """make smooth polygon
769 add vertexs in polygon, and make smooth line.
771 pidxs = self._pidxlist()
773 if self._lenvertexs(p_idx) >= 256 // div:
775 self.md.smoothPolygon(p_idx, div)
779 self.marks_selected = []
781 self._addmarkpolygon(p_idx)
787 if __name__ == "__main__":