Skip to content

Commit f34c8f0

Browse files
committed
Colorize drawings
1 parent 4ed0f5c commit f34c8f0

11 files changed

+198
-46
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ venv/
1818
# build artifacts
1919
*.zip
2020

21-
# obj
21+
# OBJ
2222
*.obj
23+
*.mtl

Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ release: submission.zip
3838
$(QT_OBJ)/%.py: $(QT_SRC)/%.ui
3939
$(QT_COMPILER) $< -o $@
4040

41-
submission.zip: $(PY_SOURCES) $(QT_OBJECTS) Makefile README.md objs/scene.obj
41+
submission.zip: $(PY_SOURCES) $(QT_OBJECTS) Makefile README.md objs/scene.obj objs/palette.mtl
4242
@ make test
4343
@ make mostlyclean
4444
mkdir PyCG
@@ -48,5 +48,6 @@ submission.zip: $(PY_SOURCES) $(QT_OBJECTS) Makefile README.md objs/scene.obj
4848
cp README.md PyCG
4949
mkdir PyCG/objs
5050
cp objs/scene.obj PyCG/objs
51+
cp objs/palette.mtl PyCG/objs
5152
zip -r submission.zip PyCG
5253
rm -r PyCG

objs/.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
# keep the OBJs in this folder
1+
# keep the OBJ stuff in this folder
22
!*.obj
3+
!*.mtl

objs/palette.mtl

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
newmtl red
2+
Kd 1 0 0
3+
4+
newmtl green
5+
Kd 0 1 0
6+
7+
newmtl blue
8+
Kd 0 0 1
9+
10+
newmtl black
11+
Kd 0 0 0
12+
13+
newmtl white
14+
Kd 1 1 1

objs/sample.mtl

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
newmtl red
2+
Kd 1.000000 0.000000 0.000000
3+
4+
newmtl texture
5+
Kd 0.800000 0.800000 0.800000

objs/scene.obj

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
mtllib palette.mtl
2+
13
v -950 0 1.0 1.0
24
v 1605 0 1.0 1.0
35
v -575 0 1.0 1.0
@@ -12,11 +14,19 @@ v 478 75 1.0 1.0
1214
v 696 134 1.0 1.0
1315
v 475 250 1.0 1.0
1416
v 950 0 1.0 1.0
17+
1518
o horizon
19+
usemtl green
1620
l 1 2
21+
1722
o mountain
23+
usemtl red
1824
f 3 4 5 6 7
25+
1926
o peak
27+
usemtl white
2028
p 6
29+
2130
o snow_mountain
31+
usemtl blue
2232
l 8 9 10 11 12 13 14 8

pycg/app.py

+35-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from math import inf, radians
44
from sys import argv
5+
from os import path
56
from typing import Optional, Callable, Dict, Sequence, Tuple
67
from ast import literal_eval
78

@@ -13,7 +14,7 @@
1314

1415
from blas import Vector
1516
from graphics import (Painter, Camera, Transformation, Drawable, Point, Line,
16-
Wireframe, Polygon)
17+
Wireframe, Polygon, Color)
1718
from utilities import experp, begin, sign, to_float
1819
import obj as wavefront_obj
1920
from ui.main import Ui_MainWindow
@@ -95,15 +96,28 @@ def handle_save_action():
9596
else:
9697
name = self.displayFile.currentItem().text()
9798
model = self.display_file[name]
98-
path, _ = QFileDialog.getSaveFileName(
99+
100+
modelpath, _ = QFileDialog.getSaveFileName(
99101
self,
100102
"Select .obj file to save",
101103
name + '.obj',
102104
)
103-
if path.strip() != '':
104-
with wavefront_obj.open(path, 'w+') as file:
105-
file.write(model, name)
106-
self.log(f"Saved object '{name}' to '{path}'")
105+
if modelpath.strip() == '': return
106+
107+
mtlpath, _ = QFileDialog.getSaveFileName(
108+
self,
109+
"Select .mtl file to save",
110+
name + '.mtl',
111+
)
112+
if mtlpath.strip() != '':
113+
color = model.color
114+
with open(mtlpath, 'w+') as file:
115+
file.write(f"newmtl color\n")
116+
file.write(f"Kd {color.r/255} {color.g/255} {color.b/255}\n")
117+
118+
with wavefront_obj.open(modelpath, 'w+', mtllib=path.basename(mtlpath)) as file:
119+
file.write(model, name, usemtl='color')
120+
self.log(f"Saved object '{name}' to '{mtlpath}'")
107121

108122
def handle_new_action():
109123
new_obj = None
@@ -185,7 +199,8 @@ def new_object():
185199

186200
name = self.nameEdit.text()
187201
typename = self.typeBox.currentText()
188-
color = self.colorEdit.text()
202+
color = QColor(self.colorEdit.text())
203+
color = Color(color.red(), color.green(), color.blue())
189204

190205
# indexes of QLayout.itemAt() depend on the order of UI elements
191206
obj = None
@@ -257,7 +272,7 @@ def enableRotateLabels():
257272
self.log("Interactive Graphical System initialized.")
258273

259274
def insert_object(self, obj: Drawable, name,
260-
index: int = None, color: str = None) -> int:
275+
index: int = None, color: Color = None) -> int:
261276
"""Put an object in the Display File, returning its position."""
262277

263278
n = self.displayFile.count()
@@ -275,7 +290,10 @@ def insert_object(self, obj: Drawable, name,
275290
name = prefix + str(count)
276291
self.display_file[name] = obj
277292

278-
obj.color = color or QPalette().color(QPalette.Foreground).name()
293+
if not color:
294+
color = QPalette().color(QPalette.Foreground)
295+
color = Color(color.red(), color.green(), color.blue())
296+
obj.color = color
279297
self.displayFile.insertItem(index, name)
280298
self.log(f"Added {type(obj).__name__} '{name}' to Display File.")
281299
self.displayFile.setCurrentRow(index)
@@ -309,8 +327,12 @@ def load_obj(self, path: str = None):
309327
path = path or QFileDialog.getOpenFileName(self, "Select .obj file to load")[0]
310328
if path.strip() != '':
311329
with wavefront_obj.open(path, 'r') as file:
312-
for model, name in file:
313-
self.insert_object(model, name)
330+
for model, attributes in file:
331+
self.insert_object(
332+
model,
333+
name=attributes['name'],
334+
color=attributes.get('color', None),
335+
)
314336

315337

316338
class QtViewport(QWidget):
@@ -374,8 +396,8 @@ def paintEvent(self, event): # this is where we draw our scene
374396
painter.drawRect(40, 40, w - 80, h-80)
375397

376398
for drawable in self._display_file.values():
377-
painter.setPen(QColor(drawable.color))
378-
painter.setBrush(QColor(drawable.color))
399+
painter.setPen(QColor(str(drawable.color)))
400+
painter.setBrush(QColor(str(drawable.color)))
379401
drawable.draw(self.camera)
380402

381403
painter.end()

pycg/graphics.py

+46-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Iterable, Tuple, Sequence
55

66
from blas import Vector, Matrix
7-
from utilities import pairwise
7+
from utilities import pairwise, clamp
88

99

1010
class Painter():
@@ -513,3 +513,48 @@ def clip_polygon(points: Sequence[Tuple[float, float]]) -> Sequence[Tuple[float,
513513
points = new
514514

515515
return points
516+
517+
518+
class Color:
519+
def __init__(self, r, g, b, a = 0xFF):
520+
self._r = clamp(int(r), 0x00, 0xFF)
521+
self._g = clamp(int(g), 0x00, 0xFF)
522+
self._b = clamp(int(b), 0x00, 0xFF)
523+
self._a = clamp(int(a), 0x00, 0xFF)
524+
525+
def __repr__(self):
526+
return '#' + ''.join('%02x' % x for x in (self.r, self.g, self.b))
527+
528+
def __eq__(self, other) -> bool:
529+
return self.r == other.r \
530+
and self.g == other.g \
531+
and self.b == other.b \
532+
and self.a == other.a
533+
534+
@property
535+
def r(self):
536+
return self._r
537+
538+
@r.setter
539+
def r(self, r):
540+
self._r = clamp(int(r), 0x00, 0xFF)
541+
542+
@property
543+
def g(self):
544+
return self._g
545+
546+
@g.setter
547+
def g(self, g):
548+
self._g = clamp(int(g), 0x00, 0xFF)
549+
550+
@property
551+
def b(self):
552+
return self._b
553+
554+
@b.setter
555+
def b(self, b):
556+
self._b = clamp(int(b), 0x00, 0xFF)
557+
558+
@property
559+
def a(self):
560+
return self._a

0 commit comments

Comments
 (0)