diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..68859ad --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[pep8] +max-line-length = 120 diff --git a/src/dialog_about.py b/src/dialog_about.py index 303d64d..157527c 100644 --- a/src/dialog_about.py +++ b/src/dialog_about.py @@ -18,7 +18,9 @@ from ui_dialog_about import Ui_AboutDialog from constants import ZOXEL_VERSION + class AboutDialog(QtGui.QDialog): + def __init__(self, parent=None): # Initialise the UI super(AboutDialog, self).__init__(parent) diff --git a/src/dialog_resize.py b/src/dialog_resize.py index 4c421ff..9e8795a 100644 --- a/src/dialog_resize.py +++ b/src/dialog_resize.py @@ -17,7 +17,9 @@ from PySide import QtGui from ui_dialog_resize import Ui_ResizeDialog + class ResizeDialog(QtGui.QDialog): + def __init__(self, parent=None): # Initialise the UI super(ResizeDialog, self).__init__(parent) @@ -26,7 +28,7 @@ def __init__(self, parent=None): self.ui.button_auto.clicked.connect(self.on_button_auto_clicked) def on_button_auto_clicked(self): - _,_,_,x,y,z = self.parent().display.voxels.get_bounding_box() + _, _, _, x, y, z = self.parent().display.voxels.get_bounding_box() self.ui.width.setValue(x) self.ui.height.setValue(y) self.ui.depth.setValue(z) diff --git a/src/euclid.py b/src/euclid.py index 74b4ddc..ecf3349 100644 --- a/src/euclid.py +++ b/src/euclid.py @@ -54,7 +54,10 @@ _use_slots = True # Implement _use_slots magic. + + class _EuclidMetaclass(type): + def __new__(cls, name, bases, dct): if '__slots__' in dct: dct['__getstate__'] = cls._create_getstate(dct['__slots__']) @@ -84,6 +87,7 @@ def __setstate__(self, state): __metaclass__ = _EuclidMetaclass + class Vector2: __slots__ = ['x', 'y'] __hash__ = None @@ -103,11 +107,11 @@ def __repr__(self): def __eq__(self, other): if isinstance(other, Vector2): return self.x == other.x and \ - self.y == other.y + self.y == other.y else: assert hasattr(other, '__len__') and len(other) == 2 return self.x == other[0] and \ - self.y == other[1] + self.y == other[1] def __ne__(self, other): return not self.__eq__(other) @@ -131,10 +135,10 @@ def __iter__(self): def __getattr__(self, name): try: - return tuple([(self.x, self.y)['xy'.index(c)] \ + return tuple([(self.x, self.y)['xy'.index(c)] for c in name]) except ValueError: - raise AttributeError, name + raise AttributeError(name) if _enable_swizzle_set: # This has detrimental performance on ordinary setattr as well @@ -149,7 +153,7 @@ def __setattr__(self, name, value): l['xy'.index(c)] = v self.x, self.y = l except ValueError: - raise AttributeError, name + raise AttributeError(name) def __add__(self, other): if isinstance(other, Vector2): @@ -193,7 +197,6 @@ def __sub__(self, other): return Vector2(self.x - other[0], self.y - other[1]) - def __rsub__(self, other): if isinstance(other, Vector2): return Vector2(other.x - self.x, @@ -221,7 +224,6 @@ def __div__(self, other): return Vector2(operator.div(self.x, other), operator.div(self.y, other)) - def __rdiv__(self, other): assert type(other) in (int, long, float) return Vector2(operator.div(other, self.x), @@ -232,7 +234,6 @@ def __floordiv__(self, other): return Vector2(operator.floordiv(self.x, other), operator.floordiv(self.y, other)) - def __rfloordiv__(self, other): assert type(other) in (int, long, float) return Vector2(operator.floordiv(other, self.x), @@ -243,7 +244,6 @@ def __truediv__(self, other): return Vector2(operator.truediv(self.x, other), operator.truediv(self.y, other)) - def __rtruediv__(self, other): assert type(other) in (int, long, float) return Vector2(operator.truediv(other, self.x), @@ -251,19 +251,19 @@ def __rtruediv__(self, other): def __neg__(self): return Vector2(-self.x, - -self.y) + -self.y) __pos__ = __copy__ def __abs__(self): - return math.sqrt(self.x ** 2 + \ + return math.sqrt(self.x ** 2 + self.y ** 2) magnitude = __abs__ def magnitude_squared(self): return self.x ** 2 + \ - self.y ** 2 + self.y ** 2 def normalize(self): d = self.magnitude() @@ -282,7 +282,7 @@ def normalized(self): def dot(self, other): assert isinstance(other, Vector2) return self.x * other.x + \ - self.y * other.y + self.y * other.y def cross(self): return Vector2(self.y, -self.x) @@ -296,12 +296,13 @@ def reflect(self, normal): def angle(self, other): """Return the angle to the vector other""" - return math.acos(self.dot(other) / (self.magnitude()*other.magnitude())) + return math.acos(self.dot(other) / (self.magnitude() * other.magnitude())) def project(self, other): """Return one vector projected on the vector other""" n = other.normalized() - return self.dot(n)*n + return self.dot(n) * n + class Vector3: __slots__ = ['x', 'y', 'z'] @@ -325,13 +326,13 @@ def __repr__(self): def __eq__(self, other): if isinstance(other, Vector3): return self.x == other.x and \ - self.y == other.y and \ - self.z == other.z + self.y == other.y and \ + self.z == other.z else: assert hasattr(other, '__len__') and len(other) == 3 return self.x == other[0] and \ - self.y == other[1] and \ - self.z == other[2] + self.y == other[1] and \ + self.z == other[2] def __ne__(self, other): return not self.__eq__(other) @@ -355,10 +356,10 @@ def __iter__(self): def __getattr__(self, name): try: - return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \ + return tuple([(self.x, self.y, self.z)['xyz'.index(c)] for c in name]) except ValueError: - raise AttributeError, name + raise AttributeError(name) if _enable_swizzle_set: # This has detrimental performance on ordinary setattr as well @@ -373,8 +374,7 @@ def __setattr__(self, name, value): l['xyz'.index(c)] = v self.x, self.y, self.z = l except ValueError: - raise AttributeError, name - + raise AttributeError(name) def __add__(self, other): if isinstance(other, Vector3): @@ -424,7 +424,6 @@ def __sub__(self, other): self.y - other[1], self.z - other[2]) - def __rsub__(self, other): if isinstance(other, Vector3): return Vector3(other.x - self.x, @@ -467,7 +466,6 @@ def __div__(self, other): operator.div(self.y, other), operator.div(self.z, other)) - def __rdiv__(self, other): assert type(other) in (int, long, float) return Vector3(operator.div(other, self.x), @@ -480,7 +478,6 @@ def __floordiv__(self, other): operator.floordiv(self.y, other), operator.floordiv(self.z, other)) - def __rfloordiv__(self, other): assert type(other) in (int, long, float) return Vector3(operator.floordiv(other, self.x), @@ -493,7 +490,6 @@ def __truediv__(self, other): operator.truediv(self.y, other), operator.truediv(self.z, other)) - def __rtruediv__(self, other): assert type(other) in (int, long, float) return Vector3(operator.truediv(other, self.x), @@ -502,22 +498,22 @@ def __rtruediv__(self, other): def __neg__(self): return Vector3(-self.x, - -self.y, - -self.z) + -self.y, + -self.z) __pos__ = __copy__ def __abs__(self): - return math.sqrt(self.x ** 2 + \ - self.y ** 2 + \ + return math.sqrt(self.x ** 2 + + self.y ** 2 + self.z ** 2) magnitude = __abs__ def magnitude_squared(self): return self.x ** 2 + \ - self.y ** 2 + \ - self.z ** 2 + self.y ** 2 + \ + self.z ** 2 def normalize(self): d = self.magnitude() @@ -538,8 +534,8 @@ def normalized(self): def dot(self, other): assert isinstance(other, Vector3) return self.x * other.x + \ - self.y * other.y + \ - self.z * other.z + self.y * other.y + \ + self.z * other.z def cross(self, other): assert isinstance(other, Vector3) @@ -560,7 +556,7 @@ def rotate_around(self, axis, theta): # Adapted from equations published by Glenn Murray. # http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.html - x, y, z = self.x, self.y,self.z + x, y, z = self.x, self.y, self.z u, v, w = axis.x, axis.y, axis.z # Extracted common factors for simplicity and efficiency @@ -568,24 +564,25 @@ def rotate_around(self, axis, theta): r = math.sqrt(r2) ct = math.cos(theta) st = math.sin(theta) / r - dt = (u*x + v*y + w*z) * (1 - ct) / r2 + dt = (u * x + v * y + w * z) * (1 - ct) / r2 return Vector3((u * dt + x * ct + (-w * y + v * z) * st), - (v * dt + y * ct + ( w * x - u * z) * st), + (v * dt + y * ct + (w * x - u * z) * st), (w * dt + z * ct + (-v * x + u * y) * st)) def angle(self, other): """Return the angle to the vector other""" - return math.acos(self.dot(other) / (self.magnitude()*other.magnitude())) + return math.acos(self.dot(other) / (self.magnitude() * other.magnitude())) def project(self, other): """Return one vector projected on the vector other""" n = other.normalized() - return self.dot(n)*n + return self.dot(n) * n # a b c # e f g # i j k + class Matrix3: __slots__ = list('abcefgijk') @@ -606,13 +603,14 @@ def __copy__(self): return M copy = __copy__ + def __repr__(self): - return ('Matrix3([% 8.2f % 8.2f % 8.2f\n' \ - ' % 8.2f % 8.2f % 8.2f\n' \ + return ('Matrix3([% 8.2f % 8.2f % 8.2f\n' + ' % 8.2f % 8.2f % 8.2f\n' ' % 8.2f % 8.2f % 8.2f])') \ - % (self.a, self.b, self.c, - self.e, self.f, self.g, - self.i, self.j, self.k) + % (self.a, self.b, self.c, + self.e, self.f, self.g, + self.i, self.j, self.k) def __getitem__(self, key): return [self.a, self.e, self.i, @@ -758,12 +756,12 @@ def new_rotate(cls, angle): new_rotate = classmethod(new_rotate) def determinant(self): - return (self.a*self.f*self.k - + self.b*self.g*self.i - + self.c*self.e*self.j - - self.a*self.g*self.j - - self.b*self.e*self.k - - self.c*self.f*self.i) + return (self.a * self.f * self.k + + self.b * self.g * self.i + + self.c * self.e * self.j + - self.a * self.g * self.j + - self.b * self.e * self.k + - self.c * self.f * self.i) def inverse(self): tmp = Matrix3() @@ -775,15 +773,15 @@ def inverse(self): else: d = 1.0 / d - tmp.a = d * (self.f*self.k - self.g*self.j) - tmp.b = d * (self.c*self.j - self.b*self.k) - tmp.c = d * (self.b*self.g - self.c*self.f) - tmp.e = d * (self.g*self.i - self.e*self.k) - tmp.f = d * (self.a*self.k - self.c*self.i) - tmp.g = d * (self.c*self.e - self.a*self.g) - tmp.i = d * (self.e*self.j - self.f*self.i) - tmp.j = d * (self.b*self.i - self.a*self.j) - tmp.k = d * (self.a*self.f - self.b*self.e) + tmp.a = d * (self.f * self.k - self.g * self.j) + tmp.b = d * (self.c * self.j - self.b * self.k) + tmp.c = d * (self.b * self.g - self.c * self.f) + tmp.e = d * (self.g * self.i - self.e * self.k) + tmp.f = d * (self.a * self.k - self.c * self.i) + tmp.g = d * (self.c * self.e - self.a * self.g) + tmp.i = d * (self.e * self.j - self.f * self.i) + tmp.j = d * (self.b * self.i - self.a * self.j) + tmp.k = d * (self.a * self.f - self.b * self.e) return tmp @@ -792,6 +790,7 @@ def inverse(self): # i j k l # m n o p + class Matrix4: __slots__ = list('abcdefghijklmnop') @@ -820,16 +819,15 @@ def __copy__(self): copy = __copy__ - def __repr__(self): - return ('Matrix4([% 8.2f % 8.2f % 8.2f % 8.2f\n' \ - ' % 8.2f % 8.2f % 8.2f % 8.2f\n' \ - ' % 8.2f % 8.2f % 8.2f % 8.2f\n' \ + return ('Matrix4([% 8.2f % 8.2f % 8.2f % 8.2f\n' + ' % 8.2f % 8.2f % 8.2f % 8.2f\n' + ' % 8.2f % 8.2f % 8.2f % 8.2f\n' ' % 8.2f % 8.2f % 8.2f % 8.2f])') \ - % (self.a, self.b, self.c, self.d, - self.e, self.f, self.g, self.h, - self.i, self.j, self.k, self.l, - self.m, self.n, self.o, self.p) + % (self.a, self.b, self.c, self.d, + self.e, self.f, self.g, self.h, + self.i, self.j, self.k, self.l, + self.m, self.n, self.o, self.p) def __getitem__(self, key): return [self.a, self.e, self.i, self.m, @@ -979,7 +977,7 @@ def transform(self, other): P.x = A.a * B.x + A.b * B.y + A.c * B.z + A.d P.y = A.e * B.x + A.f * B.y + A.g * B.z + A.h P.z = A.i * B.x + A.j * B.y + A.k * B.z + A.l - w = A.m * B.x + A.n * B.y + A.o * B.z + A.p + w = A.m * B.x + A.n * B.y + A.o * B.z + A.p if w != 0: P.x /= w P.y /= w @@ -989,7 +987,7 @@ def transform(self, other): def identity(self): self.a = self.f = self.k = self.p = 1. self.b = self.c = self.d = self.e = self.g = self.h = \ - self.i = self.j = self.l = self.m = self.n = self.o = 0 + self.i = self.j = self.l = self.m = self.n = self.o = 0 return self def scale(self, x, y, z): @@ -1029,10 +1027,10 @@ def transpose(self): self.b, self.f, self.j, self.n, self.c, self.g, self.k, self.o, self.d, self.h, self.l, self.p) = \ - (self.a, self.b, self.c, self.d, - self.e, self.f, self.g, self.h, - self.i, self.j, self.k, self.l, - self.m, self.n, self.o, self.p) + (self.a, self.b, self.c, self.d, + self.e, self.f, self.g, self.h, + self.i, self.j, self.k, self.l, + self.m, self.n, self.o, self.p) def transposed(self): M = self.copy() @@ -1145,23 +1143,23 @@ def new_rotate_euler(cls, heading, attitude, bank): new_rotate_euler = classmethod(new_rotate_euler) def new_rotate_triple_axis(cls, x, y, z): - m = cls() + m = cls() - m.a, m.b, m.c = x.x, y.x, z.x - m.e, m.f, m.g = x.y, y.y, z.y - m.i, m.j, m.k = x.z, y.z, z.z + m.a, m.b, m.c = x.x, y.x, z.x + m.e, m.f, m.g = x.y, y.y, z.y + m.i, m.j, m.k = x.z, y.z, z.z - return m + return m new_rotate_triple_axis = classmethod(new_rotate_triple_axis) def new_look_at(cls, eye, at, up): - z = (eye - at).normalized() - x = up.cross(z).normalized() - y = z.cross(x) + z = (eye - at).normalized() + x = up.cross(z).normalized() + y = z.cross(x) - m = cls.new_rotate_triple_axis(x, y, z) - m.d, m.h, m.l = eye.x, eye.y, eye.z - return m + m = cls.new_rotate_triple_axis(x, y, z) + m.d, m.h, m.l = eye.x, eye.y, eye.z + return m new_look_at = classmethod(new_look_at) def new_perspective(cls, fov_y, aspect, near, far): @@ -1180,49 +1178,65 @@ def new_perspective(cls, fov_y, aspect, near, far): def determinant(self): return ((self.a * self.f - self.e * self.b) - * (self.k * self.p - self.o * self.l) - - (self.a * self.j - self.i * self.b) - * (self.g * self.p - self.o * self.h) - + (self.a * self.n - self.m * self.b) - * (self.g * self.l - self.k * self.h) - + (self.e * self.j - self.i * self.f) - * (self.c * self.p - self.o * self.d) - - (self.e * self.n - self.m * self.f) - * (self.c * self.l - self.k * self.d) - + (self.i * self.n - self.m * self.j) - * (self.c * self.h - self.g * self.d)) + * (self.k * self.p - self.o * self.l) + - (self.a * self.j - self.i * self.b) + * (self.g * self.p - self.o * self.h) + + (self.a * self.n - self.m * self.b) + * (self.g * self.l - self.k * self.h) + + (self.e * self.j - self.i * self.f) + * (self.c * self.p - self.o * self.d) + - (self.e * self.n - self.m * self.f) + * (self.c * self.l - self.k * self.d) + + (self.i * self.n - self.m * self.j) + * (self.c * self.h - self.g * self.d)) def inverse(self): tmp = Matrix4() - d = self.determinant(); + d = self.determinant() if abs(d) < 0.001: # No inverse, return identity return tmp else: - d = 1.0 / d; - - tmp.a = d * (self.f * (self.k * self.p - self.o * self.l) + self.j * (self.o * self.h - self.g * self.p) + self.n * (self.g * self.l - self.k * self.h)); - tmp.e = d * (self.g * (self.i * self.p - self.m * self.l) + self.k * (self.m * self.h - self.e * self.p) + self.o * (self.e * self.l - self.i * self.h)); - tmp.i = d * (self.h * (self.i * self.n - self.m * self.j) + self.l * (self.m * self.f - self.e * self.n) + self.p * (self.e * self.j - self.i * self.f)); - tmp.m = d * (self.e * (self.n * self.k - self.j * self.o) + self.i * (self.f * self.o - self.n * self.g) + self.m * (self.j * self.g - self.f * self.k)); - - tmp.b = d * (self.j * (self.c * self.p - self.o * self.d) + self.n * (self.k * self.d - self.c * self.l) + self.b * (self.o * self.l - self.k * self.p)); - tmp.f = d * (self.k * (self.a * self.p - self.m * self.d) + self.o * (self.i * self.d - self.a * self.l) + self.c * (self.m * self.l - self.i * self.p)); - tmp.j = d * (self.l * (self.a * self.n - self.m * self.b) + self.p * (self.i * self.b - self.a * self.j) + self.d * (self.m * self.j - self.i * self.n)); - tmp.n = d * (self.i * (self.n * self.c - self.b * self.o) + self.m * (self.b * self.k - self.j * self.c) + self.a * (self.j * self.o - self.n * self.k)); - - tmp.c = d * (self.n * (self.c * self.h - self.g * self.d) + self.b * (self.g * self.p - self.o * self.h) + self.f * (self.o * self.d - self.c * self.p)); - tmp.g = d * (self.o * (self.a * self.h - self.e * self.d) + self.c * (self.e * self.p - self.m * self.h) + self.g * (self.m * self.d - self.a * self.p)); - tmp.k = d * (self.p * (self.a * self.f - self.e * self.b) + self.d * (self.e * self.n - self.m * self.f) + self.h * (self.m * self.b - self.a * self.n)); - tmp.o = d * (self.m * (self.f * self.c - self.b * self.g) + self.a * (self.n * self.g - self.f * self.o) + self.e * (self.b * self.o - self.n * self.c)); - - tmp.d = d * (self.b * (self.k * self.h - self.g * self.l) + self.f * (self.c * self.l - self.k * self.d) + self.j * (self.g * self.d - self.c * self.h)); - tmp.h = d * (self.c * (self.i * self.h - self.e * self.l) + self.g * (self.a * self.l - self.i * self.d) + self.k * (self.e * self.d - self.a * self.h)); - tmp.l = d * (self.d * (self.i * self.f - self.e * self.j) + self.h * (self.a * self.j - self.i * self.b) + self.l * (self.e * self.b - self.a * self.f)); - tmp.p = d * (self.a * (self.f * self.k - self.j * self.g) + self.e * (self.j * self.c - self.b * self.k) + self.i * (self.b * self.g - self.f * self.c)); + d = 1.0 / d - return tmp; + tmp.a = d * (self.f * (self.k * self.p - self.o * self.l) + self.j * (self.o * + self.h - self.g * self.p) + self.n * (self.g * self.l - self.k * self.h)) + tmp.e = d * (self.g * (self.i * self.p - self.m * self.l) + self.k * (self.m * + self.h - self.e * self.p) + self.o * (self.e * self.l - self.i * self.h)) + tmp.i = d * (self.h * (self.i * self.n - self.m * self.j) + self.l * (self.m * + self.f - self.e * self.n) + self.p * (self.e * self.j - self.i * self.f)) + tmp.m = d * (self.e * (self.n * self.k - self.j * self.o) + self.i * (self.f * + self.o - self.n * self.g) + self.m * (self.j * self.g - self.f * self.k)) + + tmp.b = d * (self.j * (self.c * self.p - self.o * self.d) + self.n * (self.k * + self.d - self.c * self.l) + self.b * (self.o * self.l - self.k * self.p)) + tmp.f = d * (self.k * (self.a * self.p - self.m * self.d) + self.o * (self.i * + self.d - self.a * self.l) + self.c * (self.m * self.l - self.i * self.p)) + tmp.j = d * (self.l * (self.a * self.n - self.m * self.b) + self.p * (self.i * + self.b - self.a * self.j) + self.d * (self.m * self.j - self.i * self.n)) + tmp.n = d * (self.i * (self.n * self.c - self.b * self.o) + self.m * (self.b * + self.k - self.j * self.c) + self.a * (self.j * self.o - self.n * self.k)) + + tmp.c = d * (self.n * (self.c * self.h - self.g * self.d) + self.b * (self.g * + self.p - self.o * self.h) + self.f * (self.o * self.d - self.c * self.p)) + tmp.g = d * (self.o * (self.a * self.h - self.e * self.d) + self.c * (self.e * + self.p - self.m * self.h) + self.g * (self.m * self.d - self.a * self.p)) + tmp.k = d * (self.p * (self.a * self.f - self.e * self.b) + self.d * (self.e * + self.n - self.m * self.f) + self.h * (self.m * self.b - self.a * self.n)) + tmp.o = d * (self.m * (self.f * self.c - self.b * self.g) + self.a * (self.n * + self.g - self.f * self.o) + self.e * (self.b * self.o - self.n * self.c)) + + tmp.d = d * (self.b * (self.k * self.h - self.g * self.l) + self.f * (self.c * + self.l - self.k * self.d) + self.j * (self.g * self.d - self.c * self.h)) + tmp.h = d * (self.c * (self.i * self.h - self.e * self.l) + self.g * (self.a * + self.l - self.i * self.d) + self.k * (self.e * self.d - self.a * self.h)) + tmp.l = d * (self.d * (self.i * self.f - self.e * self.j) + self.h * (self.a * + self.j - self.i * self.b) + self.l * (self.e * self.b - self.a * self.f)) + tmp.p = d * (self.a * (self.f * self.k - self.j * self.g) + self.e * (self.j * + self.c - self.b * self.k) + self.i * (self.b * self.g - self.f * self.c)) + + return tmp class Quaternion: @@ -1263,9 +1277,9 @@ def __mul__(self, other): Bz = other.z Bw = other.w Q = Quaternion() - Q.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx + Q.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx Q.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By - Q.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz + Q.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz Q.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw return Q elif isinstance(other, Vector3): @@ -1288,16 +1302,16 @@ def __mul__(self, other): yy = y * y yz2 = 2 * y * z zz = z * z - return other.__class__(\ - ww * Vx + wy2 * Vz - wz2 * Vy + \ - xx * Vx + xy2 * Vy + xz2 * Vz - \ - zz * Vx - yy * Vx, - xy2 * Vx + yy * Vy + yz2 * Vz + \ - wz2 * Vx - zz * Vy + ww * Vy - \ - wx2 * Vz - xx * Vy, - xz2 * Vx + yz2 * Vy + \ - zz * Vz - wy2 * Vx - yy * Vz + \ - wx2 * Vy - xx * Vz + ww * Vz) + return other.__class__( + ww * Vx + wy2 * Vz - wz2 * Vy + + xx * Vx + xy2 * Vy + xz2 * Vz - + zz * Vx - yy * Vx, + xy2 * Vx + yy * Vy + yz2 * Vz + + wz2 * Vx - zz * Vy + ww * Vy - + wx2 * Vz - xx * Vy, + xz2 * Vx + yz2 * Vy + + zz * Vz - wy2 * Vx - yy * Vz + + wx2 * Vy - xx * Vz + ww * Vz) else: other = other.copy() other._apply_transform(self) @@ -1313,25 +1327,25 @@ def __imul__(self, other): By = other.y Bz = other.z Bw = other.w - self.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx + self.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx self.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By - self.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz + self.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz self.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw return self def __abs__(self): - return math.sqrt(self.w ** 2 + \ - self.x ** 2 + \ - self.y ** 2 + \ + return math.sqrt(self.w ** 2 + + self.x ** 2 + + self.y ** 2 + self.z ** 2) magnitude = __abs__ def magnitude_squared(self): return self.w ** 2 + \ - self.x ** 2 + \ - self.y ** 2 + \ - self.z ** 2 + self.x ** 2 + \ + self.y ** 2 + \ + self.z ** 2 def identity(self): self.w = 1 @@ -1468,49 +1482,49 @@ def new_rotate_euler(cls, heading, attitude, bank): new_rotate_euler = classmethod(new_rotate_euler) def new_rotate_matrix(cls, m): - if m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] > 0.00000001: - t = m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] + 1.0 - s = 0.5/math.sqrt(t) - - return cls( - s*t, - (m[1*4 + 2] - m[2*4 + 1])*s, - (m[2*4 + 0] - m[0*4 + 2])*s, - (m[0*4 + 1] - m[1*4 + 0])*s - ) - - elif m[0*4 + 0] > m[1*4 + 1] and m[0*4 + 0] > m[2*4 + 2]: - t = m[0*4 + 0] - m[1*4 + 1] - m[2*4 + 2] + 1.0 - s = 0.5/math.sqrt(t) - - return cls( - (m[1*4 + 2] - m[2*4 + 1])*s, - s*t, - (m[0*4 + 1] + m[1*4 + 0])*s, - (m[2*4 + 0] + m[0*4 + 2])*s - ) - - elif m[1*4 + 1] > m[2*4 + 2]: - t = -m[0*4 + 0] + m[1*4 + 1] - m[2*4 + 2] + 1.0 - s = 0.5/math.sqrt(t) - - return cls( - (m[2*4 + 0] - m[0*4 + 2])*s, - (m[0*4 + 1] + m[1*4 + 0])*s, - s*t, - (m[1*4 + 2] + m[2*4 + 1])*s - ) - - else: - t = -m[0*4 + 0] - m[1*4 + 1] + m[2*4 + 2] + 1.0 - s = 0.5/math.sqrt(t) - - return cls( - (m[0*4 + 1] - m[1*4 + 0])*s, - (m[2*4 + 0] + m[0*4 + 2])*s, - (m[1*4 + 2] + m[2*4 + 1])*s, - s*t - ) + if m[0 * 4 + 0] + m[1 * 4 + 1] + m[2 * 4 + 2] > 0.00000001: + t = m[0 * 4 + 0] + m[1 * 4 + 1] + m[2 * 4 + 2] + 1.0 + s = 0.5 / math.sqrt(t) + + return cls( + s * t, + (m[1 * 4 + 2] - m[2 * 4 + 1]) * s, + (m[2 * 4 + 0] - m[0 * 4 + 2]) * s, + (m[0 * 4 + 1] - m[1 * 4 + 0]) * s + ) + + elif m[0 * 4 + 0] > m[1 * 4 + 1] and m[0 * 4 + 0] > m[2 * 4 + 2]: + t = m[0 * 4 + 0] - m[1 * 4 + 1] - m[2 * 4 + 2] + 1.0 + s = 0.5 / math.sqrt(t) + + return cls( + (m[1 * 4 + 2] - m[2 * 4 + 1]) * s, + s * t, + (m[0 * 4 + 1] + m[1 * 4 + 0]) * s, + (m[2 * 4 + 0] + m[0 * 4 + 2]) * s + ) + + elif m[1 * 4 + 1] > m[2 * 4 + 2]: + t = -m[0 * 4 + 0] + m[1 * 4 + 1] - m[2 * 4 + 2] + 1.0 + s = 0.5 / math.sqrt(t) + + return cls( + (m[2 * 4 + 0] - m[0 * 4 + 2]) * s, + (m[0 * 4 + 1] + m[1 * 4 + 0]) * s, + s * t, + (m[1 * 4 + 2] + m[2 * 4 + 1]) * s + ) + + else: + t = -m[0 * 4 + 0] - m[1 * 4 + 1] + m[2 * 4 + 2] + 1.0 + s = 0.5 / math.sqrt(t) + + return cls( + (m[0 * 4 + 1] - m[1 * 4 + 0]) * s, + (m[2 * 4 + 0] + m[0 * 4 + 2]) * s, + (m[1 * 4 + 2] + m[2 * 4 + 1]) * s, + s * t + ) new_rotate_matrix = classmethod(new_rotate_matrix) def new_interpolate(cls, q1, q2, t): @@ -1554,14 +1568,16 @@ def new_interpolate(cls, q1, q2, t): # Much maths thanks to Paul Bourke, http://astronomy.swin.edu.au/~pbourke # --------------------------------------------------------------------------- + class Geometry: + def _connect_unimplemented(self, other): - raise AttributeError, 'Cannot connect %s to %s' % \ - (self.__class__, other.__class__) + raise AttributeError('Cannot connect %s to %s' % + (self.__class__, other.__class__)) def _intersect_unimplemented(self, other): - raise AttributeError, 'Cannot intersect %s and %s' % \ - (self.__class__, other.__class__) + raise AttributeError('Cannot intersect %s and %s' % + (self.__class__, other.__class__)) _intersect_point2 = _intersect_unimplemented _intersect_line2 = _intersect_unimplemented @@ -1591,9 +1607,11 @@ def distance(self, other): return c.length return 0.0 + def _intersect_point2_circle(P, C): return abs(P - C.c) <= C.r + def _intersect_line2_line2(A, B): d = B.v.y * A.v.x - B.v.x * A.v.y if d == 0: @@ -1611,9 +1629,10 @@ def _intersect_line2_line2(A, B): return Point2(A.p.x + ua * A.v.x, A.p.y + ua * A.v.y) + def _intersect_line2_circle(L, C): a = L.v.magnitude_squared() - b = 2 * (L.v.x * (L.p.x - C.c.x) + \ + b = 2 * (L.v.x * (L.p.x - C.c.x) + L.v.y * (L.p.y - C.c.y)) c = C.c.magnitude_squared() + \ L.p.magnitude_squared() - \ @@ -1640,10 +1659,11 @@ def _intersect_line2_circle(L, C): Point2(L.p.x + u2 * L.v.x, L.p.y + u2 * L.v.y)) + def _connect_point2_line2(P, L): d = L.v.magnitude_squared() assert d != 0 - u = ((P.x - L.p.x) * L.v.x + \ + u = ((P.x - L.p.x) * L.v.x + (P.y - L.p.y) * L.v.y) / d if not L._u_in(u): u = max(min(u, 1.0), 0.0) @@ -1651,12 +1671,14 @@ def _connect_point2_line2(P, L): Point2(L.p.x + u * L.v.x, L.p.y + u * L.v.y)) + def _connect_point2_circle(P, C): v = P - C.c v.normalize() v *= C.r return LineSegment2(P, Point2(C.c.x + v.x, C.c.y + v.y)) + def _connect_line2_line2(A, B): d = B.v.y * A.v.x - B.v.x * A.v.y if d == 0: @@ -1680,6 +1702,7 @@ def _connect_line2_line2(A, B): return LineSegment2(Point2(A.p.x + ua * A.v.x, A.p.y + ua * A.v.y), Point2(B.p.x + ub * B.v.x, B.p.y + ub * B.v.y)) + def _connect_circle_line2(C, L): d = L.v.magnitude_squared() assert d != 0 @@ -1692,23 +1715,25 @@ def _connect_circle_line2(C, L): v *= C.r return LineSegment2(Point2(C.c.x + v.x, C.c.y + v.y), point) + def _connect_circle_circle(A, B): v = B.c - A.c d = v.magnitude() if A.r >= B.r and d < A.r: - #centre B inside A - s1,s2 = +1, +1 + # centre B inside A + s1, s2 = +1, +1 elif B.r > A.r and d < B.r: - #centre A inside B - s1,s2 = -1, -1 + # centre A inside B + s1, s2 = -1, -1 elif d >= A.r and d >= B.r: - s1,s2 = +1, -1 + s1, s2 = +1, -1 v.normalize() return LineSegment2(Point2(A.c.x + s1 * v.x * A.r, A.c.y + s1 * v.y * A.r), Point2(B.c.x + s2 * v.x * B.r, B.c.y + s2 * v.y * B.r)) class Point2(Vector2, Geometry): + def __repr__(self): return 'Point2(%.2f, %.2f)' % (self.x, self.y) @@ -1734,14 +1759,15 @@ def _connect_circle(self, other): if c: return c._swap() + class Line2(Geometry): __slots__ = ['p', 'v'] def __init__(self, *args): if len(args) == 3: assert isinstance(args[0], Point2) and \ - isinstance(args[1], Vector2) and \ - type(args[2]) == float + isinstance(args[1], Vector2) and \ + type(args[2]) == float self.p = args[0].copy() self.v = args[1] * args[2] / abs(args[1]) elif len(args) == 2: @@ -1752,18 +1778,18 @@ def __init__(self, *args): self.p = args[0].copy() self.v = args[1].copy() else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) elif len(args) == 1: if isinstance(args[0], Line2): self.p = args[0].p.copy() self.v = args[0].v.copy() else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) if not self.v: - raise AttributeError, 'Line has zero-length vector' + raise AttributeError('Line has zero-length vector') def __copy__(self): return self.__class__(self.p, self.v) @@ -1806,7 +1832,9 @@ def _connect_line2(self, other): def _connect_circle(self, other): return _connect_circle_line2(other, self) + class Ray2(Line2): + def __repr__(self): return 'Ray2(<%.2f, %.2f> + u<%.2f, %.2f>)' % \ (self.p.x, self.p.y, self.v.x, self.v.y) @@ -1814,7 +1842,9 @@ def __repr__(self): def _u_in(self, u): return u >= 0.0 + class LineSegment2(Line2): + def __repr__(self): return 'LineSegment2(<%.2f, %.2f> to <%.2f, %.2f>)' % \ (self.p.x, self.p.y, self.p.x + self.v.x, self.p.y + self.v.y) @@ -1836,6 +1866,7 @@ def _swap(self): length = property(lambda self: abs(self.v)) + class Circle(Geometry): __slots__ = ['c', 'r'] @@ -1882,11 +1913,12 @@ def _connect_circle(self, other): # 3D Geometry # ------------------------------------------------------------------------- + def _connect_point3_line3(P, L): d = L.v.magnitude_squared() assert d != 0 - u = ((P.x - L.p.x) * L.v.x + \ - (P.y - L.p.y) * L.v.y + \ + u = ((P.x - L.p.x) * L.v.x + + (P.y - L.p.y) * L.v.y + (P.z - L.p.z) * L.v.z) / d if not L._u_in(u): u = max(min(u, 1.0), 0.0) @@ -1894,17 +1926,20 @@ def _connect_point3_line3(P, L): L.p.y + u * L.v.y, L.p.z + u * L.v.z)) + def _connect_point3_sphere(P, S): v = P - S.c v.normalize() v *= S.r return LineSegment3(P, Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z)) + def _connect_point3_plane(p, plane): n = plane.n.normalized() d = p.dot(plane.n) - plane.k return LineSegment3(p, Point3(p.x - n.x * d, p.y - n.y * d, p.z - n.z * d)) + def _connect_line3_line3(A, B): assert A.v and B.v p13 = A.p - B.p @@ -1934,6 +1969,7 @@ def _connect_line3_line3(A, B): B.p.y + ub * B.v.y, B.p.z + ub * B.v.z)) + def _connect_line3_plane(L, P): d = P.n.dot(L.v) if not d: @@ -1949,11 +1985,12 @@ def _connect_line3_plane(L, P): # Intersection return None + def _connect_sphere_line3(S, L): d = L.v.magnitude_squared() assert d != 0 - u = ((S.c.x - L.p.x) * L.v.x + \ - (S.c.y - L.p.y) * L.v.y + \ + u = ((S.c.x - L.p.x) * L.v.x + + (S.c.y - L.p.y) * L.v.y + (S.c.z - L.p.z) * L.v.z) / d if not L._u_in(u): u = max(min(u, 1.0), 0.0) @@ -1964,25 +2001,27 @@ def _connect_sphere_line3(S, L): return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), point) + def _connect_sphere_sphere(A, B): v = B.c - A.c d = v.magnitude() if A.r >= B.r and d < A.r: - #centre B inside A - s1,s2 = +1, +1 + # centre B inside A + s1, s2 = +1, +1 elif B.r > A.r and d < B.r: - #centre A inside B - s1,s2 = -1, -1 + # centre A inside B + s1, s2 = -1, -1 elif d >= A.r and d >= B.r: - s1,s2 = +1, -1 + s1, s2 = +1, -1 v.normalize() - return LineSegment3(Point3(A.c.x + s1* v.x * A.r, - A.c.y + s1* v.y * A.r, - A.c.z + s1* v.z * A.r), - Point3(B.c.x + s2* v.x * B.r, - B.c.y + s2* v.y * B.r, - B.c.z + s2* v.z * B.r)) + return LineSegment3(Point3(A.c.x + s1 * v.x * A.r, + A.c.y + s1 * v.y * A.r, + A.c.z + s1 * v.z * A.r), + Point3(B.c.x + s2 * v.x * B.r, + B.c.y + s2 * v.y * B.r, + B.c.z + s2 * v.z * B.r)) + def _connect_sphere_plane(S, P): c = _connect_point3_plane(S.c, P) @@ -1995,6 +2034,7 @@ def _connect_sphere_plane(S, P): return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), p2) + def _connect_plane_plane(A, B): if A.n.cross(B.n): # Planes intersect @@ -2003,13 +2043,15 @@ def _connect_plane_plane(A, B): # Planes are parallel, connect to arbitrary point return _connect_point3_plane(A._get_point(), B) + def _intersect_point3_sphere(P, S): return abs(P - S.c) <= S.r + def _intersect_line3_sphere(L, S): a = L.v.magnitude_squared() - b = 2 * (L.v.x * (L.p.x - S.c.x) + \ - L.v.y * (L.p.y - S.c.y) + \ + b = 2 * (L.v.x * (L.p.x - S.c.x) + + L.v.y * (L.p.y - S.c.y) + L.v.z * (L.p.z - S.c.z)) c = S.c.magnitude_squared() + \ L.p.magnitude_squared() - \ @@ -2032,6 +2074,7 @@ def _intersect_line3_sphere(L, S): L.p.y + u2 * L.v.y, L.p.z + u2 * L.v.z)) + def _intersect_line3_plane(L, P): d = P.n.dot(L.v) if not d: @@ -2044,6 +2087,7 @@ def _intersect_line3_plane(L, P): L.p.y + u * L.v.y, L.p.z + u * L.v.z) + def _intersect_plane_plane(A, B): n1_m = A.n.magnitude_squared() n2_m = B.n.magnitude_squared() @@ -2059,7 +2103,9 @@ def _intersect_plane_plane(A, B): c1 * A.n.z + c2 * B.n.z), A.n.cross(B.n)) + class Point3(Vector3, Geometry): + def __repr__(self): return 'Point3(%.2f, %.2f, %.2f)' % (self.x, self.y, self.z) @@ -2092,14 +2138,15 @@ def _connect_plane(self, other): if c: return c._swap() + class Line3: __slots__ = ['p', 'v'] def __init__(self, *args): if len(args) == 3: assert isinstance(args[0], Point3) and \ - isinstance(args[1], Vector3) and \ - type(args[2]) == float + isinstance(args[1], Vector3) and \ + type(args[2]) == float self.p = args[0].copy() self.v = args[1] * args[2] / abs(args[1]) elif len(args) == 2: @@ -2110,18 +2157,18 @@ def __init__(self, *args): self.p = args[0].copy() self.v = args[1].copy() else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) elif len(args) == 1: if isinstance(args[0], Line3): self.p = args[0].p.copy() self.v = args[0].v.copy() else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) # XXX This is annoying. - #if not self.v: + # if not self.v: # raise AttributeError, 'Line has zero-length vector' def __copy__(self): @@ -2171,7 +2218,9 @@ def _connect_plane(self, other): if c: return c + class Ray3(Line3): + def __repr__(self): return 'Ray3(<%.2f, %.2f, %.2f> + u<%.2f, %.2f, %.2f>)' % \ (self.p.x, self.p.y, self.p.z, self.v.x, self.v.y, self.v.z) @@ -2179,7 +2228,9 @@ def __repr__(self): def _u_in(self, u): return u >= 0.0 + class LineSegment3(Line3): + def __repr__(self): return 'LineSegment3(<%.2f, %.2f, %.2f> to <%.2f, %.2f, %.2f>)' % \ (self.p.x, self.p.y, self.p.z, @@ -2202,6 +2253,7 @@ def _swap(self): length = property(lambda self: abs(self.v)) + class Sphere: __slots__ = ['c', 'r'] @@ -2250,6 +2302,7 @@ def _connect_plane(self, other): if c: return c + class Plane: # n.p = k, where n is normal, p is point on plane, k is constant scalar __slots__ = ['n', 'k'] @@ -2257,8 +2310,8 @@ class Plane: def __init__(self, *args): if len(args) == 3: assert isinstance(args[0], Point3) and \ - isinstance(args[1], Point3) and \ - isinstance(args[2], Point3) + isinstance(args[1], Point3) and \ + isinstance(args[2], Point3) self.n = (args[1] - args[0]).cross(args[2] - args[0]) self.n.normalize() self.k = self.n.dot(args[0]) @@ -2270,13 +2323,13 @@ def __init__(self, *args): self.n = args[0].normalized() self.k = args[1] else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) else: - raise AttributeError, '%r' % (args,) + raise AttributeError('%r' % (args,)) if not self.n: - raise AttributeError, 'Points on plane are colinear' + raise AttributeError('Points on plane are colinear') def __copy__(self): return self.__class__(self.n, self.k) diff --git a/src/mainwindow.py b/src/mainwindow.py index 342dacf..fa835a3 100644 --- a/src/mainwindow.py +++ b/src/mainwindow.py @@ -29,6 +29,7 @@ import sys from constants import ZOXEL_TAG + class MainWindow(QtGui.QMainWindow): def __init__(self, parent=None): @@ -50,7 +51,7 @@ def __init__(self, parent=None): # Our animation timer self._timer = QtCore.QTimer(self) self.connect(self._timer, QtCore.SIGNAL("timeout()"), - self.on_animation_tick) + self.on_animation_tick) self._anim_speed = 200 # Load our state if possible self.load_state() @@ -61,7 +62,7 @@ def __init__(self, parent=None): self.display = voxels except Exception as E: QtGui.QMessageBox.warning(self, "Initialisation Failed", - str(E)) + str(E)) exit(1) # Load default model dimensions width = self.get_setting("default_model_width") @@ -114,9 +115,9 @@ def __init__(self, parent=None): latest_tag = urllib.urlopen("https://github.com/chrmoritz/zoxel/releases/latest").geturl() if not latest_tag.endswith(ZOXEL_TAG): responce = QtGui.QMessageBox.question(self, "Outdated Zoxel version", - "A new version of Zoxel is available! Do you want to update now?", - buttons = (QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), - defaultButton = QtGui.QMessageBox.Yes) + "A new version of Zoxel is available! Do you want to update now?", + buttons=(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No), + defaultButton=QtGui.QMessageBox.Yes) if responce == QtGui.QMessageBox.Yes: webbrowser.open(latest_tag, 2) sys.exit(0) @@ -212,7 +213,7 @@ def resize_voxels(self, width, height, depth): new_height_scale = float(height) / self.display.voxels.height new_depth_scale = float(depth) / self.display.voxels.depth self.display.voxels.resize(width, height, depth) - self.display.grids.scale_offsets( new_width_scale, new_height_scale, new_depth_scale ) + self.display.grids.scale_offsets(new_width_scale, new_height_scale, new_depth_scale) self.display.refresh() # Remember these dimensions self.set_setting("default_model_width", width) @@ -240,7 +241,8 @@ def on_action_background_triggered(self): @QtCore.Slot() def on_action_anim_add_triggered(self): - value, res = QtGui.QInputDialog.getInt(self, "Add frame", "Add new frame after:", self.display.voxels.get_frame_number()+1, 1, self.display.voxels.get_frame_count()) + value, res = QtGui.QInputDialog.getInt(self, "Add frame", "Add new frame after:", self.display.voxels.get_frame_number( + ) + 1, 1, self.display.voxels.get_frame_count()) if res: self.display.voxels.add_frame(value, True) self.display.refresh() @@ -248,7 +250,8 @@ def on_action_anim_add_triggered(self): @QtCore.Slot() def on_action_anim_add_empty_triggered(self): - value, res = QtGui.QInputDialog.getInt(self, "Add frame", "Add new frame after:", self.display.voxels.get_frame_number()+1, 1, self.display.voxels.get_frame_count()) + value, res = QtGui.QInputDialog.getInt(self, "Add frame", "Add new frame after:", self.display.voxels.get_frame_number( + ) + 1, 1, self.display.voxels.get_frame_count()) if res: self.display.voxels.add_frame(value, False) self.display.refresh() @@ -256,7 +259,8 @@ def on_action_anim_add_empty_triggered(self): @QtCore.Slot() def on_action_anim_copy_triggered(self): - value, res = QtGui.QInputDialog.getInt(self, "Copy frame", "Replace current frame with:", 1, 1, self.display.voxels.get_frame_count()) + value, res = QtGui.QInputDialog.getInt( + self, "Copy frame", "Replace current frame with:", 1, 1, self.display.voxels.get_frame_count()) if res: self.display.voxels.copy_to_current(value) self.display.refresh() @@ -264,7 +268,8 @@ def on_action_anim_copy_triggered(self): @QtCore.Slot() def on_action_anim_delete_triggered(self): - ret = QtGui.QMessageBox.question(self, "Zoxel", "Do you really want to delete this frame?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) + ret = QtGui.QMessageBox.question( + self, "Zoxel", "Do you really want to delete this frame?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if (ret == QtGui.QMessageBox.Yes): self.display.voxels.delete_frame() self.display.refresh() @@ -349,9 +354,9 @@ def on_action_export_image_triggered(self): directory = self.get_setting("default_directory") # grab a filename filename, filetype = QtGui.QFileDialog.getSaveFileName(self, - caption = "Export Image As", - filter = choices, - dir = directory) + caption="Export Image As", + filter=choices, + dir=directory) if not filename: return @@ -360,7 +365,7 @@ def on_action_export_image_triggered(self): self.set_setting("default_directory", directory) # Save the PNG - png.save(filename,filetype.split()[0]) + png.save(filename, filetype.split()[0]) @QtCore.Slot() def on_action_export_troxel_triggered(self): @@ -399,10 +404,10 @@ def on_tool_drag_end(self): # Confirm if user wants to save before doing something drastic. # returns True if we should continue def confirm_save(self): - responce = QtGui.QMessageBox.question(self,"Save changes?", - "Save changes before discarding?", - buttons = (QtGui.QMessageBox.Save | QtGui.QMessageBox.Cancel - | QtGui.QMessageBox.No)) + responce = QtGui.QMessageBox.question(self, "Save changes?", + "Save changes before discarding?", + buttons=(QtGui.QMessageBox.Save | QtGui.QMessageBox.Cancel + | QtGui.QMessageBox.No)) if responce == QtGui.QMessageBox.StandardButton.Save: if not self.save(): return False @@ -469,7 +474,7 @@ def update_caption(self): if self.display and self.display.voxels.changed: caption += " *" numframes = self.display.voxels.get_frame_count() - frame = self.display.voxels.get_frame_number()+1 + frame = self.display.voxels.get_frame_number() + 1 if numframes > 1: caption += " - Frame {0} of {1}".format(frame, numframes) if caption != self._caption: @@ -477,7 +482,7 @@ def update_caption(self): self._caption = caption # Save the current data - def save(self, newfile = False): + def save(self, newfile=False): # Find the handlers that support saving handlers = [x for x in self._file_handlers if hasattr(x, 'save')] @@ -491,7 +496,7 @@ def save(self, newfile = False): # Build list of available types choices = [] for exporter in handlers: - choices.append( "%s (%s)" % (exporter.description, exporter.filetype)) + choices.append("%s (%s)" % (exporter.description, exporter.filetype)) choices = ";;".join(choices) # Grab our default location @@ -500,10 +505,10 @@ def save(self, newfile = False): # Get a filename if we need one if newfile or not filename: filename, filetype = QtGui.QFileDialog.getSaveFileName(self, - caption = "Save As", - filter = choices, - dir = directory, - selectedFilter="Zoxel Files (*.zox)") + caption="Save As", + filter=choices, + dir=directory, + selectedFilter="Zoxel Files (*.zox)") if not filename: return handler = None @@ -517,7 +522,7 @@ def save(self, newfile = False): for exporter in handlers: ourtype = "%s (%s)" % (exporter.description, exporter.filetype) if filetype == ourtype: - handler = exporter + handler = exporter # Call the save handler try: @@ -525,7 +530,7 @@ def save(self, newfile = False): saved = True except Exception as Ex: QtGui.QMessageBox.warning(self, "Save Failed", - str(Ex)) + str(Ex)) # If we saved, clear edited state if saved: @@ -554,7 +559,7 @@ def load(self): # Build list of types we can load choices = ["All Files (*)"] for importer in handlers: - choices.append( "%s (%s)" % (importer.description, importer.filetype)) + choices.append("%s (%s)" % (importer.description, importer.filetype)) choices = ";;".join(choices) # Grab our default location @@ -562,10 +567,10 @@ def load(self): # Get a filename filename, filetype = QtGui.QFileDialog.getOpenFileName(self, - caption="Open file", - filter=choices, - dir = directory, - selectedFilter="All Files (*)") + caption="Open file", + filter=choices, + dir=directory, + selectedFilter="All Files (*)") if not filename: return if filetype == "All Files (*)": @@ -574,7 +579,7 @@ def load(self): if filename.endswith(importer.filetype[1:]): filetype = "%s (%s)" % (importer.description, importer.filetype) break - if filetype == None: + if filetype is None: return # Remember the location @@ -585,7 +590,7 @@ def load(self): for importer in handlers: ourtype = "%s (%s)" % (importer.description, importer.filetype) if filetype == ourtype: - handler = importer + handler = importer self._last_file_handler = handler # Load the file @@ -598,10 +603,10 @@ def load(self): except Exception as Ex: self.display.voxels.enable_undo() QtGui.QMessageBox.warning(self, "Could not load file", - str(Ex)) + str(Ex)) self.display.build_grids() - #self.display.voxels.resize() + # self.display.voxels.resize() self.display.voxels.saved() self.display.reset_camera() self.update_caption() @@ -610,7 +615,7 @@ def load(self): self.display.refresh() # Registers a tool in the drawing toolbar - def register_tool(self, tool, activate = False): + def register_tool(self, tool, activate=False): self._tools.append(tool) self._tool_group.addAction(tool.get_action()) self.ui.toolbar_drawing.addAction(tool.get_action()) @@ -639,6 +644,6 @@ def refresh_actions(self): self.ui.action_anim_previous.setEnabled(num_frames > 1) self.ui.action_anim_next.setEnabled(num_frames > 1) self.ui.action_anim_play.setEnabled(num_frames > 1 - and not self._timer.isActive()) + and not self._timer.isActive()) self.ui.action_anim_stop.setEnabled(self._timer.isActive()) self.update_caption() diff --git a/src/palette_widget.py b/src/palette_widget.py index d3f94b6..e2206a8 100644 --- a/src/palette_widget.py +++ b/src/palette_widget.py @@ -17,6 +17,7 @@ from PySide import QtCore, QtGui from PySide.QtCore import QRect, QPoint + class PaletteWidget(QtGui.QWidget): # Colour changed signal @@ -25,6 +26,7 @@ class PaletteWidget(QtGui.QWidget): @property def colour(self): return QtGui.QColor.fromHsvF(self._hue, self._saturation, self._value) + @colour.setter def colour(self, value): # If this is an integer, assume is RGBA @@ -32,11 +34,11 @@ def colour(self, value): r = (value & 0xff000000) >> 24 g = (value & 0xff0000) >> 16 b = (value & 0xff00) >> 8 - value = QtGui.QColor.fromRgb(r,g,b) + value = QtGui.QColor.fromRgb(r, g, b) self._set_colour(value) self.RGBvalue.setText(value.name()) - def __init__(self, parent = None, RGBvalue = None): + def __init__(self, parent=None, RGBvalue=None): super(PaletteWidget, self).__init__(parent) self._hue = 1.0 self._saturation = 1.0 @@ -54,17 +56,17 @@ def _calculate_bounds(self): height = self.height() # Hue palette self._hue_rect = QRect( - width-self._hue_width, 0, self._hue_width, height) + width - self._hue_width, 0, self._hue_width, height) # Shades palette self._shades_rect = QRect( - 0, 0, width-(self._hue_width+self._gap), height) + 0, 0, width - (self._hue_width + self._gap), height) # Render our palette to an image def _draw_palette(self): # Create an image with a white background self._image = QtGui.QImage(QtCore.QSize(self.width(), self.height()), - QtGui.QImage.Format.Format_RGB32) + QtGui.QImage.Format.Format_RGB32) self._image.fill(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) # Draw on our image with no pen @@ -74,9 +76,9 @@ def _draw_palette(self): # Render hues rect = self._hue_rect - for x in xrange(rect.x(), rect.x()+rect.width()): - for y in xrange(rect.y(), rect.y()+rect.height(), 8): - h = float(y)/rect.height() + for x in xrange(rect.x(), rect.x() + rect.width()): + for y in xrange(rect.y(), rect.y() + rect.height(), 8): + h = float(y) / rect.height() s = 1.0 v = 1.0 c = QtGui.QColor.fromHsvF(h, s, v) @@ -86,7 +88,7 @@ def _draw_palette(self): # Render hue selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) qp.drawRect(rect.x(), self._hue * rect.height(), - rect.width(), 2) + rect.width(), 2) # Render shades rect = self._shades_rect @@ -94,12 +96,12 @@ def _draw_palette(self): steps = int(round(width / 8.0)) step_size = width / steps x = rect.x() - while x < rect.width()+rect.x(): + while x < rect.width() + rect.x(): w = int(round(step_size)) - for y in xrange(rect.y(), rect.y()+rect.height(), 8): + for y in xrange(rect.y(), rect.y() + rect.height(), 8): h = self._hue - s = 1-float(y)/rect.height() - v = float(x)/rect.width() + s = 1 - float(y) / rect.height() + v = float(x) / rect.width() c = QtGui.QColor.fromHsvF(h, s, v) qp.setBrush(c) qp.drawRect(x, y, w, 8) @@ -111,8 +113,8 @@ def _draw_palette(self): # Render colour selection marker qp.setBrush(QtGui.QColor.fromRgb(0xff, 0xff, 0xff)) - qp.drawRect(rect.x(), (1-self._saturation)*rect.height(), rect.width(), 1) - qp.drawRect(self._value*rect.width(), rect.y(), 1, rect.height()) + qp.drawRect(rect.x(), (1 - self._saturation) * rect.height(), rect.width(), 1) + qp.drawRect(self._value * rect.width(), rect.y(), 1, rect.height()) qp.end() @@ -120,7 +122,7 @@ def paintEvent(self, event): # Render our palette image to the screen qp = QtGui.QPainter() qp.begin(self) - qp.drawImage(QPoint(0,0), self._image) + qp.drawImage(QPoint(0, 0), self._image) qp.end() def mousePressEvent(self, event): @@ -130,7 +132,7 @@ def mousePressEvent(self, event): if self._hue_rect.contains(mouse.x(), mouse.y()): y = mouse.y() c = QtGui.QColor.fromHsvF( - float(y)/self.height(), self._saturation, self._value) + float(y) / self.height(), self._saturation, self._value) self.colour = c # Click on colours? elif self._shades_rect.contains(mouse.x(), mouse.y()): @@ -138,8 +140,8 @@ def mousePressEvent(self, event): x = mouse.x() y = mouse.y() c = QtGui.QColor.fromHsvF( - self._hue, 1-float(y)/self._shades_rect.height(), - float(x)/self._shades_rect.width()) + self._hue, 1 - float(y) / self._shades_rect.height(), + float(x) / self._shades_rect.width()) self.colour = c def mouseMoveEvent(self, event): diff --git a/src/plugin_api.py b/src/plugin_api.py index a0753c6..258d34f 100644 --- a/src/plugin_api.py +++ b/src/plugin_api.py @@ -16,9 +16,11 @@ # along with this program. If not, see . from PySide import QtGui + class PluginManager(object): plugins = [] + class PluginAPI(object): def __init__(self): @@ -28,7 +30,7 @@ def __init__(self): self.mainwindow = self.application.mainwindow # Register a drawing tool with the system - def register_tool(self, tool, activate = False): + def register_tool(self, tool, activate=False): # Create an instance self.mainwindow.register_tool(tool, activate) @@ -59,6 +61,7 @@ def get_voxel_mesh(self): # name should be a hashable type, like a simple string. def set_config(self, name, value): self.api.mainwindow.set_setting(name, value) + def get_config(self, name): return self.api.mainwindow.get_setting(name) @@ -70,6 +73,8 @@ def warning(self, message): # Plugins call this function to register with the system. A plugin # should pass the class which will be instaniated by the application, # this constructor is passed an instance of the system plugin API. + + def register_plugin(plugin_class, name, version): # Create an instance of the API to send to the plugin # Plugins access the main app via this API instance diff --git a/src/plugins/io_magica.py b/src/plugins/io_magica.py index f80d74a..7ee22dd 100644 --- a/src/plugins/io_magica.py +++ b/src/plugins/io_magica.py @@ -16,6 +16,8 @@ from plugin_api import register_plugin # http://voxel.codeplex.com/wikipage?title=VOX%20Format&referringTitle=MagicaVoxel%20Editor + + class MagicaFile(object): # Description of file type @@ -28,38 +30,59 @@ def __init__(self, api): self.api = api # Register our exporter self.api.register_file_handler(self) - self.default_palette = [0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, 0xff9999ff, - 0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, 0xff3333ff, 0xff0033ff, 0xffff00ff, - 0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc, - 0xff00cccc, 0xffff99cc, 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc, - 0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, 0xff00ff99, 0xffffcc99, - 0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, 0xffcc6699, 0xff996699, 0xff666699, 0xff336699, - 0xff006699, 0xffff3399, 0xffcc3399, 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66, - 0xff66ff66, 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, 0xffff6666, - 0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, 0xff990066, 0xff660066, 0xff330066, - 0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933, - 0xff669933, 0xff339933, 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033, - 0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, 0xff66cc00, 0xff33cc00, - 0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300, - 0xff663300, 0xff333300, 0xff003300, 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044, - 0xff000022, 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, 0xffaa0000, - 0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, 0xff444444, 0xff222222, 0xff111111] + self.default_palette = [0xffffffff, 0xffccffff, 0xff99ffff, 0xff66ffff, 0xff33ffff, 0xff00ffff, 0xffffccff, + 0xffccccff, 0xff99ccff, 0xff66ccff, 0xff33ccff, 0xff00ccff, 0xffff99ff, 0xffcc99ff, + 0xff9999ff, 0xff6699ff, 0xff3399ff, 0xff0099ff, 0xffff66ff, 0xffcc66ff, 0xff9966ff, + 0xff6666ff, 0xff3366ff, 0xff0066ff, 0xffff33ff, 0xffcc33ff, 0xff9933ff, 0xff6633ff, + 0xff3333ff, 0xff0033ff, 0xffff00ff, 0xffcc00ff, 0xff9900ff, 0xff6600ff, 0xff3300ff, + 0xff0000ff, 0xffffffcc, 0xffccffcc, 0xff99ffcc, 0xff66ffcc, 0xff33ffcc, 0xff00ffcc, + 0xffffcccc, 0xffcccccc, 0xff99cccc, 0xff66cccc, 0xff33cccc, 0xff00cccc, 0xffff99cc, + 0xffcc99cc, 0xff9999cc, 0xff6699cc, 0xff3399cc, 0xff0099cc, 0xffff66cc, 0xffcc66cc, + 0xff9966cc, 0xff6666cc, 0xff3366cc, 0xff0066cc, 0xffff33cc, 0xffcc33cc, 0xff9933cc, + 0xff6633cc, 0xff3333cc, 0xff0033cc, 0xffff00cc, 0xffcc00cc, 0xff9900cc, 0xff6600cc, + 0xff3300cc, 0xff0000cc, 0xffffff99, 0xffccff99, 0xff99ff99, 0xff66ff99, 0xff33ff99, + 0xff00ff99, 0xffffcc99, 0xffcccc99, 0xff99cc99, 0xff66cc99, 0xff33cc99, 0xff00cc99, + 0xffff9999, 0xffcc9999, 0xff999999, 0xff669999, 0xff339999, 0xff009999, 0xffff6699, + 0xffcc6699, 0xff996699, 0xff666699, 0xff336699, 0xff006699, 0xffff3399, 0xffcc3399, + 0xff993399, 0xff663399, 0xff333399, 0xff003399, 0xffff0099, 0xffcc0099, 0xff990099, + 0xff660099, 0xff330099, 0xff000099, 0xffffff66, 0xffccff66, 0xff99ff66, 0xff66ff66, + 0xff33ff66, 0xff00ff66, 0xffffcc66, 0xffcccc66, 0xff99cc66, 0xff66cc66, 0xff33cc66, + 0xff00cc66, 0xffff9966, 0xffcc9966, 0xff999966, 0xff669966, 0xff339966, 0xff009966, + 0xffff6666, 0xffcc6666, 0xff996666, 0xff666666, 0xff336666, 0xff006666, 0xffff3366, + 0xffcc3366, 0xff993366, 0xff663366, 0xff333366, 0xff003366, 0xffff0066, 0xffcc0066, + 0xff990066, 0xff660066, 0xff330066, 0xff000066, 0xffffff33, 0xffccff33, 0xff99ff33, + 0xff66ff33, 0xff33ff33, 0xff00ff33, 0xffffcc33, 0xffcccc33, 0xff99cc33, 0xff66cc33, + 0xff33cc33, 0xff00cc33, 0xffff9933, 0xffcc9933, 0xff999933, 0xff669933, 0xff339933, + 0xff009933, 0xffff6633, 0xffcc6633, 0xff996633, 0xff666633, 0xff336633, 0xff006633, + 0xffff3333, 0xffcc3333, 0xff993333, 0xff663333, 0xff333333, 0xff003333, 0xffff0033, + 0xffcc0033, 0xff990033, 0xff660033, 0xff330033, 0xff000033, 0xffffff00, 0xffccff00, + 0xff99ff00, 0xff66ff00, 0xff33ff00, 0xff00ff00, 0xffffcc00, 0xffcccc00, 0xff99cc00, + 0xff66cc00, 0xff33cc00, 0xff00cc00, 0xffff9900, 0xffcc9900, 0xff999900, 0xff669900, + 0xff339900, 0xff009900, 0xffff6600, 0xffcc6600, 0xff996600, 0xff666600, 0xff336600, + 0xff006600, 0xffff3300, 0xffcc3300, 0xff993300, 0xff663300, 0xff333300, 0xff003300, + 0xffff0000, 0xffcc0000, 0xff990000, 0xff660000, 0xff330000, 0xff0000ee, 0xff0000dd, + 0xff0000bb, 0xff0000aa, 0xff000088, 0xff000077, 0xff000055, 0xff000044, 0xff000022, + 0xff000011, 0xff00ee00, 0xff00dd00, 0xff00bb00, 0xff00aa00, 0xff008800, 0xff007700, + 0xff005500, 0xff004400, 0xff002200, 0xff001100, 0xffee0000, 0xffdd0000, 0xffbb0000, + 0xffaa0000, 0xff880000, 0xff770000, 0xff550000, 0xff440000, 0xff220000, 0xff110000, + 0xffeeeeee, 0xffdddddd, 0xffbbbbbb, 0xffaaaaaa, 0xff888888, 0xff777777, 0xff555555, + 0xff444444, 0xff222222, 0xff111111] # Helper function to read/write uint32 - def uint32(self, f, value = None): + def uint32(self, f, value=None): if value is not None: # Write data = bytearray() - data.append((value & 0xff)); - data.append((value & 0xff00)>>8); - data.append((value & 0xff0000)>>16); - data.append((value & 0xff000000)>>24); + data.append((value & 0xff)) + data.append((value & 0xff00) >> 8) + data.append((value & 0xff0000) >> 16) + data.append((value & 0xff000000) >> 24) f.write(data) else: # Read x = bytearray(f.read(4)) if len(x) == 4: - return x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24 + return x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24 return 0 def save(self, filename): @@ -77,13 +100,13 @@ def save(self, filename): for x in xrange(voxels.width): vox = voxels.get(x, z, y) if vox != 0: - if not vox in helpPalette: - r = (vox & 0xff000000)>>24 - g = (vox & 0x00ff0000)>>16 - b = (vox & 0x0000ff00)>>8 - paletteChunk.append(r | g<<8 | b <<16 | 0xff<<24) + if vox not in helpPalette: + r = (vox & 0xff000000) >> 24 + g = (vox & 0x00ff0000) >> 16 + b = (vox & 0x0000ff00) >> 8 + paletteChunk.append(r | g << 8 | b << 16 | 0xff << 24) helpPalette[vox] = len(paletteChunk) - 3 - voxelChunk.append(((helpPalette[vox])<<24) | (z<<16) | (y<<8) | x) + voxelChunk.append(((helpPalette[vox]) << 24) | (z << 16) | (y << 8) | x) while len(paletteChunk) < 259: paletteChunk.append(0xffffffff) data.append(1076 + 4 * len(voxelChunk)) @@ -93,7 +116,7 @@ def save(self, filename): data += paletteChunk # Open our file - f = open(filename,"wb") + f = open(filename, "wb") for i in data: self.uint32(f, i) @@ -105,7 +128,7 @@ def load(self, filename): voxels = self.api.get_voxel_data() # Open our file - f = open(filename,"rb") + f = open(filename, "rb") meta = f.read(4) if meta != "VOX ": @@ -152,7 +175,7 @@ def load(self, filename): palette = self.default_palette else: f.seek(paletteBegin) - for i in xrange(0, paletteLength/4): + for i in xrange(0, paletteLength / 4): palette.append(self.uint32(f)) # read voxel chunk @@ -160,14 +183,14 @@ def load(self, filename): voxelCount = self.uint32(f) for i in xrange(0, voxelCount): vox = self.uint32(f) - ix = (vox & 0x000000ff)>>0 - iy = (vox & 0x0000ff00)>>8 - iz = (vox & 0x00ff0000)>>16 - ip = (vox & 0xff000000)>>24 - b = (palette[ip-1] & 0x00ff0000)>>16 - g = (palette[ip-1] & 0x0000ff00)>>8 - r = (palette[ip-1] & 0x000000ff)>>0 - voxels.set(ix, iz, iy, (r<<24) | (g<<16) | (b<<8) | 0xff) + ix = (vox & 0x000000ff) >> 0 + iy = (vox & 0x0000ff00) >> 8 + iz = (vox & 0x00ff0000) >> 16 + ip = (vox & 0xff000000) >> 24 + b = (palette[ip - 1] & 0x00ff0000) >> 16 + g = (palette[ip - 1] & 0x0000ff00) >> 8 + r = (palette[ip - 1] & 0x000000ff) >> 0 + voxels.set(ix, iz, iy, (r << 24) | (g << 16) | (b << 8) | 0xff) f.close() register_plugin(MagicaFile, "Magica Voxel (.vox) file format IO", "1.0") diff --git a/src/plugins/io_obj.py b/src/plugins/io_obj.py index 6a9a77f..f4b2f3d 100644 --- a/src/plugins/io_obj.py +++ b/src/plugins/io_obj.py @@ -17,6 +17,7 @@ import os from plugin_api import register_plugin + class ObjFile(object): # Description of file type @@ -37,21 +38,21 @@ def save(self, filename): vertices, colours, _ = self.api.get_voxel_mesh() # Open our file - f = open(filename,"wt") + f = open(filename, "wt") # Use materials mat_pathname, mat_filename = os.path.split(filename) name, ext = os.path.splitext(mat_filename) if not ext: - filename = filename+'.obj' - mat_filename = os.path.join(mat_pathname, name)+".mtl" + filename = filename + '.obj' + mat_filename = os.path.join(mat_pathname, name) + ".mtl" f.write("mtllib %s\r\n" % mat_filename) # Export vertices i = 0 while i < len(vertices): f.write("v %f %f %f\r\n" % - (vertices[i], vertices[i+1], vertices[i+2])) + (vertices[i], vertices[i + 1], vertices[i + 2])) i += 3 # Build a list of unique colours we use so we can assign materials @@ -59,30 +60,30 @@ def save(self, filename): i = 0 while i < len(colours): r = colours[i] - g = colours[i+1] - b = colours[i+2] - colour = r<<24 | g<<16 | b<<8 + g = colours[i + 1] + b = colours[i + 2] + colour = r << 24 | g << 16 | b << 8 if colour not in mats: mats[colour] = "material_%i" % len(mats) i += 3 # Export faces - faces = (len(vertices)//(3*3))//2 + faces = (len(vertices) // (3 * 3)) // 2 for i in xrange(faces): - n = 1+(i * 6) - r = colours[(i*18)] - g = colours[(i*18)+1] - b = colours[(i*18)+2] - colour = r<<24 | g<<16 | b<<8 + n = 1 + (i * 6) + r = colours[(i * 18)] + g = colours[(i * 18) + 1] + b = colours[(i * 18) + 2] + colour = r << 24 | g << 16 | b << 8 f.write("usemtl %s\r\n" % mats[colour]) - f.write("f %i %i %i\r\n" % (n, n+2, n+1)) - f.write("f %i %i %i\r\n" % (n+5, n+4, n+3)) + f.write("f %i %i %i\r\n" % (n, n + 2, n + 1)) + f.write("f %i %i %i\r\n" % (n + 5, n + 4, n + 3)) # Tidy up f.close() # Create our material file - f = open(mat_filename,"wt") + f = open(mat_filename, "wt") for colour, material in mats.items(): f.write("newmtl %s\r\n" % material) r = (colour & 0xff000000) >> 24 diff --git a/src/plugins/io_png.py b/src/plugins/io_png.py index e61903e..d50ae50 100644 --- a/src/plugins/io_png.py +++ b/src/plugins/io_png.py @@ -17,6 +17,7 @@ from plugin_api import register_plugin from PySide import QtGui + class PngFile(object): # Description of file type @@ -32,7 +33,6 @@ def __init__(self, api): # File version format we support self._file_version = 1 - # Called when we need to load a file. Should raise an exception if there # is a problem. def load(self, filename): @@ -57,10 +57,9 @@ def load(self, filename): for x in range(width): for y in range(height): - color = img.pixel(x,y) + color = img.pixel(x, y) color = color << 8 - voxels.set(x,height-y-1,0, color) - + voxels.set(x, height - y - 1, 0, color) register_plugin(PngFile, "Png file format Importer", "1.0") diff --git a/src/plugins/io_qubicle.py b/src/plugins/io_qubicle.py index cbe9d4d..7a8de09 100644 --- a/src/plugins/io_qubicle.py +++ b/src/plugins/io_qubicle.py @@ -19,6 +19,8 @@ from struct import unpack # http://www.minddesk.com/wiki/index.php?title=Qubicle_Constructor_1:Data_Exchange_With_Qubicle_Binary + + class QubicleFile(object): # Description of file type @@ -33,20 +35,20 @@ def __init__(self, api): self.api.register_file_handler(self) # Helper function to read/write uint32 - def uint32(self, f, value = None): + def uint32(self, f, value=None): if value is not None: # Write data = bytearray() - data.append((value & 0xff)); - data.append((value & 0xff00)>>8); - data.append((value & 0xff0000)>>16); - data.append((value & 0xff000000)>>24); + data.append((value & 0xff)) + data.append((value & 0xff00) >> 8) + data.append((value & 0xff0000) >> 16) + data.append((value & 0xff000000) >> 24) f.write(data) else: # Read x = bytearray(f.read(4)) if len(x) == 4: - return x[0] | x[1]<<8 | x[2]<<16 | x[3]<<24 + return x[0] | x[1] << 8 | x[2] << 16 | x[3] << 24 return 0 def int32(self, f): @@ -59,7 +61,7 @@ def save(self, filename): voxels = self.api.get_voxel_data() # Open our file - f = open(filename,"wb") + f = open(filename, "wb") # Version self.uint32(f, 0x00000101) @@ -98,10 +100,10 @@ def save(self, filename): alpha = 0xff if not vox: alpha = 0x00 - r = (vox & 0xff000000)>>24 - g = (vox & 0xff0000)>>16 - b = (vox & 0xff00)>>8 - vox = r | g<<8 | b<<16 | alpha<<24 + r = (vox & 0xff000000) >> 24 + g = (vox & 0xff0000) >> 16 + b = (vox & 0xff00) >> 8 + vox = r | g << 8 | b << 16 | alpha << 24 self.uint32(f, vox) # Tidy up @@ -109,12 +111,12 @@ def save(self, filename): # sets alpha to ff and converts brga to rgba if format def formatVox(self, vox, format): - r = (vox & 0x000000ff)>>0 - g = (vox & 0x0000ff00)>>8 - b = (vox & 0x00ff0000)>>16 + r = (vox & 0x000000ff) >> 0 + g = (vox & 0x0000ff00) >> 8 + b = (vox & 0x00ff0000) >> 16 if format: - return (b<<24) | (g<<16) | (r<<8) | 0xff - return (r<<24) | (g<<16) | (b<<8) | 0xff + return (b << 24) | (g << 16) | (r << 8) | 0xff + return (r << 24) | (g << 16) | (b << 8) | 0xff # Load a Qubicle Constructor binary file def load(self, filename): @@ -122,7 +124,7 @@ def load(self, filename): voxels = self.api.get_voxel_data() # Open our file - f = open(filename,"rb") + f = open(filename, "rb") # Version version = self.uint32(f) @@ -183,7 +185,7 @@ def load(self, filename): while True: data = self.uint32(f) if (data == 6): - break; + break elif (data == 2): count = self.uint32(f) vox = self.uint32(f) @@ -218,8 +220,10 @@ def load(self, filename): if coords == 1: iz = depth - z - 1 voxels.set((width - x - 1), y, iz, self.formatVox(vox, format)) - if matrix_count == 1 and dx <= 0 and dy <= 0 and dz <= 0 and (dx < 0 or dy < 0 or dz < 0): # restore attachment point - t = "It looks like your are opening a voxel model exported by Trove. Should we try to restore the attachment point out of the .qb's metadata for you?" + # restore attachment point + if matrix_count == 1 and dx <= 0 and dy <= 0 and dz <= 0 and (dx < 0 or dy < 0 or dz < 0): + t = "It looks like your are opening a voxel model exported by Trove.\ + Should we try to restore the attachment point out of the .qb's metadata for you?" r = QMessageBox.question(None, "Restore attachment point?", t, QMessageBox.No, QMessageBox.Yes) if r == QMessageBox.Yes: voxels.set((max_width + dx - 1), -dy, -dz, 0xff00ffff) diff --git a/src/plugins/io_sproxel.py b/src/plugins/io_sproxel.py index bac736d..118f884 100644 --- a/src/plugins/io_sproxel.py +++ b/src/plugins/io_sproxel.py @@ -16,6 +16,7 @@ # along with this program. If not, see . from plugin_api import register_plugin + class SproxelFile(object): # Description of file type @@ -36,14 +37,14 @@ def save(self, filename): voxels = self.api.get_voxel_data() # Open our file - f = open(filename,"wt") + f = open(filename, "wt") # First Sproxel line is model dimenstions f.write("%i,%i,%i\n" % (voxels.width, voxels.height, voxels.depth)) # Then we save from the top of the model - for y in xrange(voxels.height-1, -1, -1): - for z in xrange(voxels.depth-1, -1, -1): + for y in xrange(voxels.height - 1, -1, -1): + for z in xrange(voxels.depth - 1, -1, -1): line = [] for x in xrange(voxels.width): voxel = voxels.get(x, y, z) @@ -52,8 +53,8 @@ def save(self, filename): else: voxel = (voxel & 0xffffff00) | 0xff voxel = "%x" % voxel - line.append("#"+voxel.upper().rjust(8,"0")) - f.write(",".join(line)+"\n") + line.append("#" + voxel.upper().rjust(8, "0")) + f.write(",".join(line) + "\n") f.write("\n") # Tidy up @@ -65,16 +66,16 @@ def load(self, filename): voxels = self.api.get_voxel_data() # Open our file - f = open(filename,"rt") + f = open(filename, "rt") size = f.readline().strip() - x,y,z = size.split(",") + x, y, z = size.split(",") x = int(x) y = int(y) z = int(z) voxels.resize(x, y, z) # Parse the file - for fy in xrange(y-1,-1,-1): - for fz in xrange(z-1,-1,-1): + for fy in xrange(y - 1, -1, -1): + for fz in xrange(z - 1, -1, -1): line = f.readline().strip().split(",") for fx in xrange(0, x): if line[fx] == "#00000000": @@ -87,10 +88,10 @@ def load(self, filename): g = int(g, 16) b = int(b, 16) a = 0xff - v = r<<24 | g<<16 | b<<8 | a + v = r << 24 | g << 16 | b << 8 | a voxels.set(fx, fy, fz, v) - f.readline() # discard empty line + f.readline() # discard empty line f.close() register_plugin(SproxelFile, "Sproxel file format IO", "1.0") diff --git a/src/plugins/io_troxel.py b/src/plugins/io_troxel.py index 6fad581..64a2605 100644 --- a/src/plugins/io_troxel.py +++ b/src/plugins/io_troxel.py @@ -2,6 +2,7 @@ from struct import pack from plugin_api import PluginAPI + class TroxelLink: def __init__(self): @@ -44,7 +45,7 @@ def export(self): if r > 1: data.append(126 + r) if vox[i]: - index = rcolors[ vox[i][2] + 256 * vox[i][1] + 65536 * vox[i][0] ] + index = rcolors[vox[i][2] + 256 * vox[i][1] + 65536 * vox[i][0]] if short: data.append(index) else: diff --git a/src/plugins/io_zoxel.py b/src/plugins/io_zoxel.py index 58c8011..9df069e 100644 --- a/src/plugins/io_zoxel.py +++ b/src/plugins/io_zoxel.py @@ -18,6 +18,7 @@ from plugin_api import register_plugin from constants import ZOXEL_VERSION + class ZoxelFile(object): # Description of file type @@ -44,7 +45,7 @@ def save(self, filename): # Build data structure data = {'version': version, 'frames': voxels.get_frame_count(), - "creator": "Zoxel Version "+ZOXEL_VERSION} + "creator": "Zoxel Version " + ZOXEL_VERSION} for f in xrange(voxels.get_frame_count()): frame = [] @@ -54,16 +55,16 @@ def save(self, filename): for x in range(voxels.width): v = voxels.get(x, y, z) if v: - frame.append((x,y,z,v)) + frame.append((x, y, z, v)) - data['frame{0}'.format(f+1)] = frame + data['frame{0}'.format(f + 1)] = frame data['width'] = voxels.width data['height'] = voxels.height data['depth'] = voxels.depth # Open our file - f = open(filename,"wt") + f = open(filename, "wt") f.write(json.dumps(data)) @@ -111,15 +112,15 @@ def load(self, filename): if z > maxZ: maxZ = z # Resize - voxels.resize(maxX+1, maxY+1, maxZ+1) + voxels.resize(maxX + 1, maxY + 1, maxZ + 1) # Read the voxel data for f in xrange(frames): - frame = data['frame{0}'.format(f+1)] + frame = data['frame{0}'.format(f + 1)] for x, y, z, v in frame: voxels.set(x, y, z, v) # Add another frame if required - if f < frames-1: + if f < frames - 1: voxels.add_frame(False) # Select the first frame by default diff --git a/src/plugins/tool_colourpick.py b/src/plugins/tool_colourpick.py index 1cd06b2..a9dedd9 100644 --- a/src/plugins/tool_colourpick.py +++ b/src/plugins/tool_colourpick.py @@ -18,6 +18,7 @@ from tool import Tool from plugin_api import register_plugin + class ColourPickTool(Tool): def __init__(self, api): diff --git a/src/plugins/tool_drag.py b/src/plugins/tool_drag.py index b4675dd..851679e 100644 --- a/src/plugins/tool_drag.py +++ b/src/plugins/tool_drag.py @@ -18,6 +18,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class DragTool(Tool): def __init__(self, api): diff --git a/src/plugins/tool_draw.py b/src/plugins/tool_draw.py index 970dc71..4be02a6 100644 --- a/src/plugins/tool_draw.py +++ b/src/plugins/tool_draw.py @@ -18,6 +18,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class DrawingTool(Tool): def __init__(self, api): @@ -39,8 +40,8 @@ def __init__(self, api): # returns A Target object indicating the actual place where the voxel were # inserted. Returns None when no insertion was made. def _draw_voxel(self, target, shift_down, erase): - # Works out where exactly the new voxel goes. It can collide with an existing voxel or with the bottom of the 'y' plane, - #in which case, pos will be different than None. + # Works out where exactly the new voxel goes. It can collide with an existing voxel + # or with the bottom of the 'y' plane, in which case, pos will be different than None. color = self.colour if erase: color = 0 @@ -51,9 +52,9 @@ def _draw_voxel(self, target, shift_down, erase): target.world_y = pos[1] target.world_z = pos[2] - if shift_down and self.first_voxel == None: + if shift_down and self.first_voxel is None: self.first_voxel = (target.world_x, target.world_y, target.world_z) - elif self.first_voxel == None: + elif self.first_voxel is None: if target.voxels.set(target.world_x, target.world_y, target.world_z, color): return target else: @@ -73,11 +74,11 @@ def _draw_voxel(self, target, shift_down, erase): return None def _get_valid_sequence_faces(self, face): - if( face in Face.COLLIDABLE_FACES_PLANE_X ): + if(face in Face.COLLIDABLE_FACES_PLANE_X): return Face.COLLIDABLE_FACES_PLANE_Y + Face.COLLIDABLE_FACES_PLANE_Z - elif( face in Face.COLLIDABLE_FACES_PLANE_Y ): + elif(face in Face.COLLIDABLE_FACES_PLANE_Y): return Face.COLLIDABLE_FACES_PLANE_X + Face.COLLIDABLE_FACES_PLANE_Z - elif( face in Face.COLLIDABLE_FACES_PLANE_Z ): + elif(face in Face.COLLIDABLE_FACES_PLANE_Z): return Face.COLLIDABLE_FACES_PLANE_X + Face.COLLIDABLE_FACES_PLANE_Y else: return None @@ -94,10 +95,10 @@ def on_drag_start(self, data): # When dragging, Draw a new voxel next to the targeted face def on_drag(self, data): # In case the first click has missed a valid target. - if( self._first_target is None ): + if(self._first_target is None): return valid_faces = self._get_valid_sequence_faces(self._first_target.face) - if( ( not valid_faces ) or ( data.face not in valid_faces ) ): + if((not valid_faces) or (data.face not in valid_faces)): return self._draw_voxel(data, False, False) diff --git a/src/plugins/tool_erase.py b/src/plugins/tool_erase.py index f7c6bcf..d783ceb 100644 --- a/src/plugins/tool_erase.py +++ b/src/plugins/tool_erase.py @@ -18,6 +18,7 @@ from tool import Tool from plugin_api import register_plugin + class EraseTool(Tool): def __init__(self, api): diff --git a/src/plugins/tool_extrude.py b/src/plugins/tool_extrude.py index 071cea1..c868815 100644 --- a/src/plugins/tool_extrude.py +++ b/src/plugins/tool_extrude.py @@ -18,6 +18,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class ExtrudeTool(Tool): def __init__(self, api): @@ -36,16 +37,16 @@ def __init__(self, api): self.regionend = None def regionvalid(self): - if self.regionstart == None or self.regionend == None: + if self.regionstart is None or self.regionend is None: return 0, False r = [] if self.regionstart[0] == self.regionend[0]: - r.append(0) # Plane is on yz + r.append(0) # Plane is on yz if self.regionstart[1] == self.regionend[1]: - r.append(1) # Plane is on xz + r.append(1) # Plane is on xz if self.regionstart[2] == self.regionend[2]: - r.append(2) # Plane is on xy - if len(r) == 1: # If plane and not line or point + r.append(2) # Plane is on xy + if len(r) == 1: # If plane and not line or point return r.pop(), True return 0, False @@ -64,12 +65,14 @@ def do_extrude(self, target, value, region): if self.regionstart[2] > self.regionend[2]: zdelt = -1 for xidx in range(abs(value)): - for yidx in range(abs(self.regionstart[1] - self.regionend[1])+1): - for zidx in range(abs(self.regionstart[2] - self.regionend[2])+1): - src = target.voxels.get(xpos, ypos+(yidx*ydelt), zpos+(zidx*zdelt)) - tgt = target.voxels.get(xpos+(xidx*xdelt)+xdelt, ypos+(yidx*ydelt), zpos+(zidx*zdelt)) + for yidx in range(abs(self.regionstart[1] - self.regionend[1]) + 1): + for zidx in range(abs(self.regionstart[2] - self.regionend[2]) + 1): + src = target.voxels.get(xpos, ypos + (yidx * ydelt), zpos + (zidx * zdelt)) + tgt = target.voxels.get(xpos + (xidx * xdelt) + xdelt, ypos + + (yidx * ydelt), zpos + (zidx * zdelt)) if tgt == 0: - target.voxels.set(xpos+(xidx*xdelt)+xdelt, ypos+(yidx*ydelt), zpos+(zidx*zdelt), src, True, 1) + target.voxels.set(xpos + (xidx * xdelt) + xdelt, ypos + + (yidx * ydelt), zpos + (zidx * zdelt), src, True, 1) target.voxels.completeUndoFill() return True if region == 1: @@ -80,12 +83,14 @@ def do_extrude(self, target, value, region): if self.regionstart[2] > self.regionend[2]: zdelt = -1 for yidx in range(abs(value)): - for xidx in range(abs(self.regionstart[0] - self.regionend[0])+1): - for zidx in range(abs(self.regionstart[2] - self.regionend[2])+1): - src = target.voxels.get(xpos+(xidx*xdelt), ypos, zpos+(zidx*zdelt)) - tgt = target.voxels.get(xpos+(xidx*xdelt), ypos+(yidx*ydelt)+ydelt, zpos+(zidx*zdelt)) + for xidx in range(abs(self.regionstart[0] - self.regionend[0]) + 1): + for zidx in range(abs(self.regionstart[2] - self.regionend[2]) + 1): + src = target.voxels.get(xpos + (xidx * xdelt), ypos, zpos + (zidx * zdelt)) + tgt = target.voxels.get(xpos + (xidx * xdelt), ypos + + (yidx * ydelt) + ydelt, zpos + (zidx * zdelt)) if tgt == 0: - target.voxels.set(xpos+(xidx*xdelt), ypos+(yidx*ydelt)+ydelt, zpos+(zidx*zdelt), src, True, 1) + target.voxels.set(xpos + (xidx * xdelt), ypos + (yidx * ydelt) + + ydelt, zpos + (zidx * zdelt), src, True, 1) target.voxels.completeUndoFill() return True if region == 2: @@ -96,21 +101,23 @@ def do_extrude(self, target, value, region): if value < 0: zdelt = -1 for zidx in range(abs(value)): - for xidx in range(abs(self.regionstart[0] - self.regionend[0])+1): - for yidx in range(abs(self.regionstart[1] - self.regionend[1])+1): - src = target.voxels.get(xpos+(xidx*xdelt), ypos+(yidx*ydelt), zpos) - tgt = target.voxels.get(xpos+(xidx*xdelt), ypos+(yidx*ydelt), zpos+(zidx*zdelt)+zdelt) + for xidx in range(abs(self.regionstart[0] - self.regionend[0]) + 1): + for yidx in range(abs(self.regionstart[1] - self.regionend[1]) + 1): + src = target.voxels.get(xpos + (xidx * xdelt), ypos + (yidx * ydelt), zpos) + tgt = target.voxels.get(xpos + (xidx * xdelt), ypos + (yidx * ydelt), + zpos + (zidx * zdelt) + zdelt) if tgt == 0: - target.voxels.set(xpos+(xidx*xdelt), ypos+(yidx*ydelt), zpos+(zidx*zdelt)+zdelt, src, True, 1) + target.voxels.set(xpos + (xidx * xdelt), ypos + (yidx * ydelt), + zpos + (zidx * zdelt) + zdelt, src, True, 1) target.voxels.completeUndoFill() return True return False def on_mouse_click(self, data): shift_down = not not (data.key_modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier) - if shift_down and self.regionstart == None: + if shift_down and self.regionstart is None: self.regionstart = [data.world_x, data.world_y, data.world_z] - elif self.regionstart != None: + elif self.regionstart is not None: self.regionend = [data.world_x, data.world_y, data.world_z] region, valid = self.regionvalid() if valid: @@ -126,15 +133,24 @@ def on_mouse_click(self, data): directionHint = "\nPositive values mean to the back, negative mean to the front." min = -1 * data.world_z max = data.voxels.depth - data.world_z - 1 - value, ret = QtGui.QInputDialog.getInt(QtGui.QApplication.instance().mainwindow, "Extrude", "Extrude region: "+str(self.regionstart)+" - "+str(self.regionend)+ directionHint, 0, min, max) + value, ret = QtGui.QInputDialog.getInt(QtGui.QApplication.instance().mainwindow, "Extrude", + "Extrude region: " + str(self.regionstart) + " - " + + str(self.regionend) + directionHint, 0, min, max) if ret: self.do_extrude(data, value, region) else: - QtGui.QMessageBox.warning(QtGui.QApplication.instance().mainwindow, "Extrude", "The region you selected is invalid. Try again.\nMake sure that they are on a plane in any direction.\nExtrusion can only be done in straigt directions. Top, bottom, left, right, back and front.", QtGui.QMessageBox.Ok) + QtGui.QMessageBox.warning(QtGui.QApplication.instance().mainwindow, "Extrude", + ("The region you selected is invalid. Try again.\n" + "Make sure that they are on a plane in any direction.\n" + "Extrusion can only be done in straigt directions. " + "Top, bottom, left, right, back and front."), QtGui.QMessageBox.Ok) self.regionstart = None self.regionend = None else: - QtGui.QMessageBox.warning(QtGui.QApplication.instance().mainwindow, "Extrude", "You have not selected a region yet. Hold shift and click on two voxels that are on a flat plane in any direction.", QtGui.QMessageBox.Ok) + QtGui.QMessageBox.warning(QtGui.QApplication.instance().mainwindow, "Extrude", + ("You have not selected a region yet. " + "Hold shift and click on two voxels that are on a flat plane in any direction."), + QtGui.QMessageBox.Ok) register_plugin(ExtrudeTool, "Extrude Tool", "1.0") diff --git a/src/plugins/tool_fill.py b/src/plugins/tool_fill.py index 023ec2f..4a82df2 100644 --- a/src/plugins/tool_fill.py +++ b/src/plugins/tool_fill.py @@ -16,6 +16,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class FillTool(Tool): def __init__(self, api): @@ -40,7 +41,7 @@ def on_mouse_click(self, target): search_colour = voxel # Don't allow invalid fills c = self.colour.getRgb() - fill_colour = c[0]<<24 | c[1]<<16 | c[2]<<8 | 0xff + fill_colour = c[0] << 24 | c[1] << 16 | c[2] << 8 | 0xff if search_colour == fill_colour: return # Initialise our search list @@ -48,23 +49,23 @@ def on_mouse_click(self, target): search.add((target.world_x, target.world_y, target.world_z)) # Keep iterating over the search list until no more to do while len(search): - x,y,z = search.pop() + x, y, z = search.pop() voxel = target.voxels.get(x, y, z) if not voxel or voxel != search_colour: continue # Add all likely neighbours into our search list - if target.voxels.get(x-1,y,z) == search_colour: - search.add((x-1,y,z)) - if target.voxels.get(x+1,y,z) == search_colour: - search.add((x+1,y,z)) - if target.voxels.get(x,y+1,z) == search_colour: - search.add((x,y+1,z)) - if target.voxels.get(x,y-1,z) == search_colour: - search.add((x,y-1,z)) - if target.voxels.get(x,y,z+1) == search_colour: - search.add((x,y,z+1)) - if target.voxels.get(x,y,z-1) == search_colour: - search.add((x,y,z-1)) + if target.voxels.get(x - 1, y, z) == search_colour: + search.add((x - 1, y, z)) + if target.voxels.get(x + 1, y, z) == search_colour: + search.add((x + 1, y, z)) + if target.voxels.get(x, y + 1, z) == search_colour: + search.add((x, y + 1, z)) + if target.voxels.get(x, y - 1, z) == search_colour: + search.add((x, y - 1, z)) + if target.voxels.get(x, y, z + 1) == search_colour: + search.add((x, y, z + 1)) + if target.voxels.get(x, y, z - 1) == search_colour: + search.add((x, y, z - 1)) # Set the colour of the current voxel target.voxels.set(x, y, z, self.colour, True, len(search) == 0 and 2 or 1) diff --git a/src/plugins/tool_fill_noise.py b/src/plugins/tool_fill_noise.py index 62c7d77..6d03d44 100644 --- a/src/plugins/tool_fill_noise.py +++ b/src/plugins/tool_fill_noise.py @@ -20,6 +20,7 @@ from plugin_api import register_plugin from random import random + class FillNoiseTool(Tool): def __init__(self, api): @@ -44,7 +45,7 @@ def on_mouse_click(self, target): search_colour = voxel # Don't allow invalid fills c = self.colour.getRgb() - fill_colour = c[0]<<24 | c[1]<<16 | c[2]<<8 | 0xff + fill_colour = c[0] << 24 | c[1] << 16 | c[2] << 8 | 0xff # Initialise our search list search = set() search.add((target.world_x, target.world_y, target.world_z)) @@ -53,32 +54,32 @@ def on_mouse_click(self, target): i = QtGui.QInputDialog.getDouble(self.api.mainwindow, "Intensity", "Intensity:", 0.3, 0.0, 1.0, 3.0)[0] # Keep iterating over the search list until no more to do while len(search): - x,y,z = search.pop() + x, y, z = search.pop() voxel = target.voxels.get(x, y, z) if not voxel or voxel != search_colour: continue # Add all likely neighbours into our search list - if target.voxels.get(x-1,y,z) == search_colour and not (x-1,y,z) in searched: - search.add((x-1,y,z)) - if target.voxels.get(x+1,y,z) == search_colour and not (x+1,y,z) in searched: - search.add((x+1,y,z)) - if target.voxels.get(x,y+1,z) == search_colour and not (x,y+1,z) in searched: - search.add((x,y+1,z)) - if target.voxels.get(x,y-1,z) == search_colour and not (x,y-1,z) in searched: - search.add((x,y-1,z)) - if target.voxels.get(x,y,z+1) == search_colour and not (x,y,z+1) in searched: - search.add((x,y,z+1)) - if target.voxels.get(x,y,z-1) == search_colour and not (x,y,z-1) in searched: - search.add((x,y,z-1)) + if target.voxels.get(x - 1, y, z) == search_colour and not (x - 1, y, z) in searched: + search.add((x - 1, y, z)) + if target.voxels.get(x + 1, y, z) == search_colour and not (x + 1, y, z) in searched: + search.add((x + 1, y, z)) + if target.voxels.get(x, y + 1, z) == search_colour and not (x, y + 1, z) in searched: + search.add((x, y + 1, z)) + if target.voxels.get(x, y - 1, z) == search_colour and not (x, y - 1, z) in searched: + search.add((x, y - 1, z)) + if target.voxels.get(x, y, z + 1) == search_colour and not (x, y, z + 1) in searched: + search.add((x, y, z + 1)) + if target.voxels.get(x, y, z - 1) == search_colour and not (x, y, z - 1) in searched: + search.add((x, y, z - 1)) # Set the colour of the current voxel if target.mouse_button == MouseButtons.LEFT: - nc = color.lighter(random()*200*i + 100 - 100*i) + nc = color.lighter(random() * 200 * i + 100 - 100 * i) elif target.mouse_button == MouseButtons.RIGHT: nc = QtGui.QColor(color) - nc.setHsvF((nc.hueF()+(random()*0.2*i-0.1*i))%1, - max(0,min(1,nc.saturationF()+(random()*2*i-i))), - max(0,min(1,nc.valueF()+(random()*2*i-i)))) + nc.setHsvF((nc.hueF() + (random() * 0.2 * i - 0.1 * i)) % 1, + max(0, min(1, nc.saturationF() + (random() * 2 * i - i))), + max(0, min(1, nc.valueF() + (random() * 2 * i - i)))) target.voxels.set(x, y, z, nc, True, len(search) == 0 and 2 or 1) - searched.append((x,y,z)) + searched.append((x, y, z)) register_plugin(FillNoiseTool, "Noisy Fill Tool", "1.0") diff --git a/src/plugins/tool_paint.py b/src/plugins/tool_paint.py index 93c49b3..7519264 100644 --- a/src/plugins/tool_paint.py +++ b/src/plugins/tool_paint.py @@ -18,6 +18,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class PaintingTool(Tool): def __init__(self, api): diff --git a/src/plugins/tool_shade.py b/src/plugins/tool_shade.py index 0144825..d011f76 100644 --- a/src/plugins/tool_shade.py +++ b/src/plugins/tool_shade.py @@ -17,6 +17,7 @@ from tool import Tool, EventData, MouseButtons, KeyModifiers, Face from plugin_api import register_plugin + class ShaderTool(Tool): def __init__(self, api): @@ -38,14 +39,14 @@ def on_mouse_click(self, data): if voxel: self.api.set_palette_colour(voxel) if data.mouse_button == MouseButtons.LEFT: - #Darken the colour. + # Darken the colour. newColour = self.api.get_palette_colour().darker(110) - #set the voxel's new color. + # set the voxel's new color. data.voxels.set(data.world_x, data.world_y, data.world_z, newColour) elif data.mouse_button == MouseButtons.RIGHT: - #Lighten the colour. + # Lighten the colour. newColour = self.api.get_palette_colour().lighter(110) - #set the voxel's new color. + # set the voxel's new color. data.voxels.set(data.world_x, data.world_y, data.world_z, newColour) # Colour when dragging also diff --git a/src/tool.py b/src/tool.py index b185080..2020918 100644 --- a/src/tool.py +++ b/src/tool.py @@ -18,6 +18,8 @@ from PySide import QtGui # Enumeration type + + def enum(*sequential, **named): enums = dict(zip(sequential, range(len(sequential))), **named) return type('Enum', (), enums) @@ -28,6 +30,7 @@ def enum(*sequential, **named): # Keyboard modifiers KeyModifiers = enum('CTRL', 'SHIFT', 'ALT') + class Face(object): FRONT = 0 # z- TOP = 1 # y+ @@ -45,11 +48,13 @@ class Face(object): COLLIDABLE_FACES_PLANE_Y = FACES_PLANE_Y + [None] COLLIDABLE_FACES_PLANE_Z = FACES_PLANE_Z + class EventData(object): @property def face(self): return self._face + @face.setter def face(self, value): self._face = value @@ -57,6 +62,7 @@ def face(self, value): @property def world_x(self): return self._world_x + @world_x.setter def world_x(self, value): self._world_x = value @@ -64,6 +70,7 @@ def world_x(self, value): @property def world_y(self): return self._world_y + @world_y.setter def world_y(self, value): self._world_y = value @@ -71,6 +78,7 @@ def world_y(self, value): @property def world_z(self): return self._world_z + @world_z.setter def world_z(self, value): self._world_z = value @@ -78,6 +86,7 @@ def world_z(self, value): @property def voxels(self): return self._voxels + @voxels.setter def voxels(self, value): self._voxels = value @@ -85,6 +94,7 @@ def voxels(self, value): @property def mouse_x(self): return self._mouse_x + @mouse_x.setter def mouse_x(self, value): self._mouse_x = value @@ -92,6 +102,7 @@ def mouse_x(self, value): @property def mouse_y(self): return self._mouse_y + @mouse_y.setter def mouse_y(self, value): self._mouse_y = value @@ -99,6 +110,7 @@ def mouse_y(self, value): @property def mouse_button(self): return self._mouse_button + @mouse_button.setter def mouse_button(self, value): self._mouse_button = value @@ -106,6 +118,7 @@ def mouse_button(self, value): @property def key_modifiers(self): return self._key_modifiers + @key_modifiers.setter def key_modifiers(self, value): self._key_modifiers = value @@ -113,9 +126,9 @@ def key_modifiers(self, value): def __repr__(self): return ('EventData(face={0},world_x={1},world_y={2},world_z={3},' 'mouse_x={4},mouse_y={5},mouse_button={6},key_modifiers={7})'.format( - self._face, self._world_x, self._world_y, self._world_z, - self._mouse_x, self._mouse_y, self._mouse_button, - self._key_modifiers)) + self._face, self._world_x, self._world_y, self._world_z, + self._mouse_x, self._mouse_y, self._mouse_button, + self._key_modifiers)) def __init__(self): self._face = None @@ -129,11 +142,11 @@ def __init__(self): self._voxels = None def __eq__(self, other): - return ( (self._x == other._x) and - (self._y == other._y ) and - (self._z == other._z ) and - (self._face == other._face ) and - (self._voxels == other._voxels ) ) + return ((self._x == other._x) and + (self._y == other._y) and + (self._z == other._z) and + (self._face == other._face) and + (self._voxels == other._voxels)) # Returns the coordinates of the voxel next to the selected face. # Or None if there is not one. @@ -146,7 +159,7 @@ def get_neighbour(self): if self.face == Face.TOP: y += 1 elif self.face == Face.BOTTOM: - y -=1 + y -= 1 elif self.face == Face.BACK: z += 1 elif self.face == Face.FRONT: @@ -157,6 +170,7 @@ def get_neighbour(self): x += 1 return (x, y, z) + class Tool(object): @property diff --git a/src/undo.py b/src/undo.py index 7006240..4a15bc6 100644 --- a/src/undo.py +++ b/src/undo.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . + class UndoItem(object): @property @@ -34,6 +35,7 @@ def __init__(self, operation, olddata, newdata): self._olddata = olddata self._newdata = newdata + class Undo(object): # Types of operation @@ -44,6 +46,7 @@ class Undo(object): @property def enabled(self): return self._enabled + @enabled.setter def enabled(self, value): self._enabled = value @@ -51,6 +54,7 @@ def enabled(self, value): @property def frame(self): return self._frame + @frame.setter def frame(self, value): self._frame = value @@ -71,10 +75,10 @@ def add(self, item): if not self._enabled: return # Clear future if we're somewhere in the middle of the undo history - if self._ptr[self._frame] < len(self._buffer[self._frame])-1: - self._buffer[self._frame] = self._buffer[self._frame][:self._ptr[self._frame]+1] + if self._ptr[self._frame] < len(self._buffer[self._frame]) - 1: + self._buffer[self._frame] = self._buffer[self._frame][:self._ptr[self._frame] + 1] self._buffer[self._frame].append(item) - self._ptr[self._frame] = len(self._buffer[self._frame])-1 + self._ptr[self._frame] = len(self._buffer[self._frame]) - 1 def _valid_buffer(self): return len(self._buffer[self._frame]) > 0 @@ -93,8 +97,8 @@ def redo(self): return self._ptr[self._frame] += 1 item = None - if self._ptr[self._frame] > len(self._buffer[self._frame])-1: - self._ptr[self._frame] = len(self._buffer[self._frame])-1 + if self._ptr[self._frame] > len(self._buffer[self._frame]) - 1: + self._ptr[self._frame] = len(self._buffer[self._frame]) - 1 else: item = self._buffer[self._frame][self._ptr[self._frame]] return item diff --git a/src/voxel.py b/src/voxel.py index c8a3836..36304d2 100644 --- a/src/voxel.py +++ b/src/voxel.py @@ -43,6 +43,7 @@ # Occlusion factor OCCLUSION = 0.7 + class VoxelData(object): # Constants for referring to axis @@ -54,9 +55,11 @@ class VoxelData(object): @property def width(self): return self._width + @property def height(self): return self._height + @property def depth(self): return self._depth @@ -64,6 +67,7 @@ def depth(self): @property def changed(self): return self._changed + @changed.setter def changed(self, value): if value and not self._changed: @@ -76,6 +80,7 @@ def changed(self, value): @property def occlusion(self): return self._occlusion + @occlusion.setter def occlusion(self, value): self._occlusion = value @@ -114,7 +119,7 @@ def _initialise_data(self): # Return an empty voxel space def blank_data(self): return [[[0 for _ in xrange(self.depth)] - for _ in xrange(self.height)] + for _ in xrange(self.height)] for _ in xrange(self.width)] def is_valid_bounds(self, x, y, z): @@ -143,7 +148,7 @@ def select_frame(self, frame_number): self.changed = True # Add a new frame by copying the current one - def add_frame(self, index, copy_current = True): + def add_frame(self, index, copy_current=True): if copy_current: data = self.get_data() else: @@ -151,14 +156,14 @@ def add_frame(self, index, copy_current = True): # If current frame is at the position of the new frame # We must move out of the way. if self._current_frame == index: - self.select_frame(index-1) + self.select_frame(index - 1) self._frames.insert(index, data) self._undo.add_frame(index) self._frame_count += 1 self.select_frame(index) - def copy_to_current(self, index): - data = self._frames[index-1] + def copy_to_current(self, index): + data = self._frames[index - 1] self.set_data(data) # Delete the current frame @@ -183,16 +188,16 @@ def delete_frame(self): # Change to the next frame (with wrap) def select_next_frame(self): - nextframe = self._current_frame+1 + nextframe = self._current_frame + 1 if nextframe >= self._frame_count: nextframe = 0 self.select_frame(nextframe) # Change to the previous frame (with wrap) def select_previous_frame(self): - prevframe = self._current_frame-1 + prevframe = self._current_frame - 1 if prevframe < 0: - prevframe = self._frame_count-1 + prevframe = self._frame_count - 1 self.select_frame(prevframe) # Get current frame number @@ -200,14 +205,14 @@ def get_frame_number(self): return self._current_frame # Set a voxel to the given state - def set(self, x, y, z, state, undo = True, fill = 0): + def set(self, x, y, z, state, undo=True, fill=0): # If this looks like a QT Color instance, convert it if hasattr(state, "getRgb"): c = state.getRgb() - state = c[0]<<24 | c[1]<<16 | c[2]<<8 | 0xff + state = c[0] << 24 | c[1] << 16 | c[2] << 8 | 0xff # Check bounds - if ( not self.is_valid_bounds(x, y, z ) ): + if (not self.is_valid_bounds(x, y, z)): return False # Add to undo if undo: @@ -218,15 +223,15 @@ def set(self, x, y, z, state, undo = True, fill = 0): self.completeUndoFill() else: self._undo.add(UndoItem(Undo.SET_VOXEL, - (x, y, z, self._data[x][y][z]), (x, y, z, state))) + (x, y, z, self._data[x][y][z]), (x, y, z, state))) # Set the voxel self._data[x][y][z] = state if state != EMPTY: - if (x,y,z) not in self._cache: - self._cache.append((x,y,z)) + if (x, y, z) not in self._cache: + self._cache.append((x, y, z)) else: - if (x,y,z) in self._cache: - self._cache.remove((x,y,z)) + if (x, y, z) in self._cache: + self._cache.remove((x, y, z)) self.changed = True return True @@ -237,7 +242,7 @@ def completeUndoFill(self): # Get the state of the given voxel def get(self, x, y, z): - if ( not self.is_valid_bounds(x, y, z ) ): + if (not self.is_valid_bounds(x, y, z)): return EMPTY return self._data[x][y][z] @@ -262,7 +267,7 @@ def get_vertices(self): colour_ids = [] normals = [] uvs = [] - for x,y,z in self._cache: + for x, y, z in self._cache: v, c, n, cid, uv = self._get_voxel_vertices(x, y, z) vertices += v colours += c @@ -279,7 +284,7 @@ def saved(self): # Count the number of non-empty voxels from the list of coordinates def _count_voxels(self, coordinates): count = 0 - for x,y,z in coordinates: + for x, y, z in coordinates: if self.get(x, y, z) != EMPTY: count += 1 return count @@ -293,28 +298,28 @@ def _get_voxel_vertices(self, x, y, z): uvs = [] # Remember voxel coordinates - vx, vy, vz = x,y,z + vx, vy, vz = x, y, z # Determine if we have filled voxels around us - front = self.get(x, y, z-1) == EMPTY - left = self.get(x-1, y, z) == EMPTY - right = self.get(x+1, y, z) == EMPTY - top = self.get(x, y+1, z) == EMPTY - back = self.get(x, y, z+1) == EMPTY - bottom = self.get(x, y-1, z) == EMPTY + front = self.get(x, y, z - 1) == EMPTY + left = self.get(x - 1, y, z) == EMPTY + right = self.get(x + 1, y, z) == EMPTY + top = self.get(x, y + 1, z) == EMPTY + back = self.get(x, y, z + 1) == EMPTY + bottom = self.get(x, y - 1, z) == EMPTY # Get our colour c = self.get(x, y, z) - r = (c & 0xff000000)>>24 - g = (c & 0xff0000)>>16 - b = (c & 0xff00)>>8 + r = (c & 0xff000000) >> 24 + g = (c & 0xff0000) >> 16 + b = (c & 0xff00) >> 8 # Calculate shades for our 4 occlusion levels shades = [] for c in range(5): shades.append(( - int(r*math.pow(OCCLUSION,c)), - int(g*math.pow(OCCLUSION,c)), - int(b*math.pow(OCCLUSION,c)))) + int(r * math.pow(OCCLUSION, c)), + int(g * math.pow(OCCLUSION, c)), + int(b * math.pow(OCCLUSION, c)))) # Encode our voxel space coordinates as colours, used for face selection # We use 7 bits per coordinate and the bottom 3 bits for face: @@ -324,9 +329,9 @@ def _get_voxel_vertices(self, x, y, z): # 3 - right # 4 - back # 5 - bottom - voxel_id = (x & 0x7f)<<17 | (y & 0x7f)<<10 | (z & 0x7f)<<3 - id_r = (voxel_id & 0xff0000)>>16 - id_g = (voxel_id & 0xff00)>>8 + voxel_id = (x & 0x7f) << 17 | (y & 0x7f) << 10 | (z & 0x7f) << 3 + id_r = (voxel_id & 0xff0000) >> 16 + id_g = (voxel_id & 0xff00) >> 8 id_b = (voxel_id & 0xff) # Adjust coordinates to the origin @@ -339,39 +344,39 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx,vy+1,vz-1) != EMPTY: + if self.get(vx, vy + 1, vz - 1) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx-1,vy,vz-1) != EMPTY: + if self.get(vx - 1, vy, vz - 1) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx+1,vy,vz-1) != EMPTY: + if self.get(vx + 1, vy, vz - 1) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx,vy-1,vz-1) != EMPTY: + if self.get(vx, vy - 1, vz - 1) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx-1,vy-1,vz-1) != EMPTY: + if self.get(vx - 1, vy - 1, vz - 1) != EMPTY: occ1 += 1 - if self.get(vx-1,vy+1,vz-1) != EMPTY: + if self.get(vx - 1, vy + 1, vz - 1) != EMPTY: occ2 += 1 - if self.get(vx+1,vy-1,vz-1) != EMPTY: + if self.get(vx + 1, vy - 1, vz - 1) != EMPTY: occ3 += 1 - if self.get(vx+1,vy+1,vz-1) != EMPTY: + if self.get(vx + 1, vy + 1, vz - 1) != EMPTY: occ4 += 1 - vertices += (x, y, z) + vertices += (x, y, z) colours += shades[occ1] - vertices += (x, y+1, z) + vertices += (x, y + 1, z) colours += shades[occ2] - vertices += (x+1, y, z) + vertices += (x + 1, y, z) colours += shades[occ3] - vertices += (x+1, y, z) + vertices += (x + 1, y, z) colours += shades[occ3] - vertices += (x, y+1, z) + vertices += (x, y + 1, z) colours += shades[occ2] - vertices += (x+1, y+1, z) + vertices += (x + 1, y + 1, z) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (0, 0, 1) * 6 colour_ids += (id_r, id_g, id_b) * 6 # Top face @@ -381,39 +386,39 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx,vy+1,vz+1) != EMPTY: + if self.get(vx, vy + 1, vz + 1) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx-1,vy+1,vz) != EMPTY: + if self.get(vx - 1, vy + 1, vz) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx+1,vy+1,vz) != EMPTY: + if self.get(vx + 1, vy + 1, vz) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx,vy+1,vz-1) != EMPTY: + if self.get(vx, vy + 1, vz - 1) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx-1,vy+1,vz-1) != EMPTY: + if self.get(vx - 1, vy + 1, vz - 1) != EMPTY: occ1 += 1 - if self.get(vx+1,vy+1,vz-1) != EMPTY: + if self.get(vx + 1, vy + 1, vz - 1) != EMPTY: occ3 += 1 - if self.get(vx+1,vy+1,vz+1) != EMPTY: + if self.get(vx + 1, vy + 1, vz + 1) != EMPTY: occ4 += 1 - if self.get(vx-1,vy+1,vz+1) != EMPTY: + if self.get(vx - 1, vy + 1, vz + 1) != EMPTY: occ2 += 1 - vertices += (x, y+1, z) + vertices += (x, y + 1, z) colours += shades[occ1] - vertices += (x, y+1, z-1) + vertices += (x, y + 1, z - 1) colours += shades[occ2] - vertices += (x+1, y+1, z) + vertices += (x + 1, y + 1, z) colours += shades[occ3] - vertices += (x+1, y+1, z) + vertices += (x + 1, y + 1, z) colours += shades[occ3] - vertices += (x, y+1, z-1) + vertices += (x, y + 1, z - 1) colours += shades[occ2] - vertices += (x+1, y+1, z-1) + vertices += (x + 1, y + 1, z - 1) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (0, 1, 0) * 6 colour_ids += (id_r, id_g, id_b | 1) * 6 # Right face @@ -423,39 +428,39 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx+1,vy+1,vz) != EMPTY: + if self.get(vx + 1, vy + 1, vz) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx+1,vy,vz-1) != EMPTY: + if self.get(vx + 1, vy, vz - 1) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx+1,vy,vz+1) != EMPTY: + if self.get(vx + 1, vy, vz + 1) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx+1,vy-1,vz) != EMPTY: + if self.get(vx + 1, vy - 1, vz) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx+1,vy-1,vz-1) != EMPTY: + if self.get(vx + 1, vy - 1, vz - 1) != EMPTY: occ1 += 1 - if self.get(vx+1,vy+1,vz-1) != EMPTY: + if self.get(vx + 1, vy + 1, vz - 1) != EMPTY: occ2 += 1 - if self.get(vx+1,vy-1,vz+1) != EMPTY: + if self.get(vx + 1, vy - 1, vz + 1) != EMPTY: occ3 += 1 - if self.get(vx+1,vy+1,vz+1) != EMPTY: + if self.get(vx + 1, vy + 1, vz + 1) != EMPTY: occ4 += 1 - vertices += (x+1, y, z) + vertices += (x + 1, y, z) colours += shades[occ1] - vertices += (x+1, y+1, z) + vertices += (x + 1, y + 1, z) colours += shades[occ2] - vertices += (x+1, y, z-1) + vertices += (x + 1, y, z - 1) colours += shades[occ3] - vertices += (x+1, y, z-1) + vertices += (x + 1, y, z - 1) colours += shades[occ3] - vertices += (x+1, y+1, z) + vertices += (x + 1, y + 1, z) colours += shades[occ2] - vertices += (x+1, y+1, z-1) + vertices += (x + 1, y + 1, z - 1) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (1, 0, 0) * 6 colour_ids += (id_r, id_g, id_b | 3) * 6 # Left face @@ -465,39 +470,39 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx-1,vy+1,vz) != EMPTY: + if self.get(vx - 1, vy + 1, vz) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx-1,vy,vz+1) != EMPTY: + if self.get(vx - 1, vy, vz + 1) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx-1,vy,vz-1) != EMPTY: + if self.get(vx - 1, vy, vz - 1) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx-1,vy-1,vz) != EMPTY: + if self.get(vx - 1, vy - 1, vz) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx-1,vy-1,vz+1) != EMPTY: + if self.get(vx - 1, vy - 1, vz + 1) != EMPTY: occ1 += 1 - if self.get(vx-1,vy+1,vz+1) != EMPTY: + if self.get(vx - 1, vy + 1, vz + 1) != EMPTY: occ2 += 1 - if self.get(vx-1,vy-1,vz-1) != EMPTY: + if self.get(vx - 1, vy - 1, vz - 1) != EMPTY: occ3 += 1 - if self.get(vx-1,vy+1,vz-1) != EMPTY: + if self.get(vx - 1, vy + 1, vz - 1) != EMPTY: occ4 += 1 - vertices += (x, y, z-1) + vertices += (x, y, z - 1) colours += shades[occ1] - vertices += (x, y+1, z-1) + vertices += (x, y + 1, z - 1) colours += shades[occ2] vertices += (x, y, z) colours += shades[occ3] vertices += (x, y, z) colours += shades[occ3] - vertices += (x, y+1, z-1) + vertices += (x, y + 1, z - 1) colours += shades[occ2] - vertices += (x, y+1, z) + vertices += (x, y + 1, z) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (-1, 0, 0) * 6 colour_ids += (id_r, id_g, id_b | 2) * 6 # Back face @@ -507,39 +512,39 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx,vy+1,vz+1) != EMPTY: + if self.get(vx, vy + 1, vz + 1) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx+1,vy,vz+1) != EMPTY: + if self.get(vx + 1, vy, vz + 1) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx-1,vy,vz+1) != EMPTY: + if self.get(vx - 1, vy, vz + 1) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx,vy-1,vz+1) != EMPTY: + if self.get(vx, vy - 1, vz + 1) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx+1,vy-1,vz+1) != EMPTY: + if self.get(vx + 1, vy - 1, vz + 1) != EMPTY: occ1 += 1 - if self.get(vx+1,vy+1,vz+1) != EMPTY: + if self.get(vx + 1, vy + 1, vz + 1) != EMPTY: occ2 += 1 - if self.get(vx-1,vy-1,vz+1) != EMPTY: + if self.get(vx - 1, vy - 1, vz + 1) != EMPTY: occ3 += 1 - if self.get(vx-1,vy+1,vz+1) != EMPTY: + if self.get(vx - 1, vy + 1, vz + 1) != EMPTY: occ4 += 1 - vertices += (x+1, y, z-1) + vertices += (x + 1, y, z - 1) colours += shades[occ1] - vertices += (x+1, y+1, z-1) + vertices += (x + 1, y + 1, z - 1) colours += shades[occ2] - vertices += (x, y, z-1) + vertices += (x, y, z - 1) colours += shades[occ3] - vertices += (x, y, z-1) + vertices += (x, y, z - 1) colours += shades[occ3] - vertices += (x+1, y+1, z-1) + vertices += (x + 1, y + 1, z - 1) colours += shades[occ2] - vertices += (x, y+1, z-1) + vertices += (x, y + 1, z - 1) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (0, 0, -1) * 6 colour_ids += (id_r, id_g, id_b | 4) * 6 # Bottom face @@ -549,77 +554,76 @@ def _get_voxel_vertices(self, x, y, z): occ3 = 0 occ4 = 0 if self._occlusion: - if self.get(vx,vy-1,vz-1) != EMPTY: + if self.get(vx, vy - 1, vz - 1) != EMPTY: occ2 += 1 occ4 += 1 - if self.get(vx-1,vy-1,vz) != EMPTY: + if self.get(vx - 1, vy - 1, vz) != EMPTY: occ1 += 1 occ2 += 1 - if self.get(vx+1,vy-1,vz) != EMPTY: + if self.get(vx + 1, vy - 1, vz) != EMPTY: occ3 += 1 occ4 += 1 - if self.get(vx,vy-1,vz+1) != EMPTY: + if self.get(vx, vy - 1, vz + 1) != EMPTY: occ1 += 1 occ3 += 1 - if self.get(vx-1,vy-1,vz+1) != EMPTY: + if self.get(vx - 1, vy - 1, vz + 1) != EMPTY: occ1 += 1 - if self.get(vx-1,vy-1,vz-1) != EMPTY: + if self.get(vx - 1, vy - 1, vz - 1) != EMPTY: occ2 += 1 - if self.get(vx+1,vy-1,vz+1) != EMPTY: + if self.get(vx + 1, vy - 1, vz + 1) != EMPTY: occ3 += 1 - if self.get(vx+1,vy-1,vz-1) != EMPTY: + if self.get(vx + 1, vy - 1, vz - 1) != EMPTY: occ4 += 1 - vertices += (x, y, z-1) + vertices += (x, y, z - 1) colours += shades[occ1] vertices += (x, y, z) colours += shades[occ2] - vertices += (x+1, y, z-1) + vertices += (x + 1, y, z - 1) colours += shades[occ3] - vertices += (x+1, y, z-1) + vertices += (x + 1, y, z - 1) colours += shades[occ3] vertices += (x, y, z) colours += shades[occ2] - vertices += (x+1, y, z) + vertices += (x + 1, y, z) colours += shades[occ4] - uvs += (0,0,0,1,1,0,1,0,0,1,1,1) + uvs += (0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1) normals += (0, -1, 0) * 6 colour_ids += (id_r, id_g, id_b | 5) * 6 return (vertices, colours, normals, colour_ids, uvs) - # Return vertices for a floor grid def get_grid_vertices(self): grid = [] - #builds the Y_plane - for z in xrange(self.depth+1): + # builds the Y_plane + for z in xrange(self.depth + 1): gx, gy, gz = self.voxel_to_world(0, 0, z) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(self.width, 0, z) grid += (gx, gy, gz) - for x in xrange(self.width+1): + for x in xrange(self.width + 1): gx, gy, gz = self.voxel_to_world(x, 0, 0) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(x, 0, self.depth) grid += (gx, gy, gz) - #builds the Z_plane - for x in xrange(self.width+1): + # builds the Z_plane + for x in xrange(self.width + 1): gx, gy, gz = self.voxel_to_world(x, 0, self.depth) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(x, self.height, self.depth) grid += (gx, gy, gz) - for y in xrange(self.height+1): + for y in xrange(self.height + 1): gx, gy, gz = self.voxel_to_world(0, y, self.depth) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(self.width, y, self.depth) grid += (gx, gy, gz) - #builds the X_plane - for y in xrange(self.height+1): + # builds the X_plane + for y in xrange(self.height + 1): gx, gy, gz = self.voxel_to_world(0, y, 0) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(0, y, self.depth) grid += (gx, gy, gz) - for z in xrange(self.depth+1): + for z in xrange(self.depth + 1): gx, gy, gz = self.voxel_to_world(0, 0, z) grid += (gx, gy, gz) gx, gy, gz = self.voxel_to_world(0, self.height, z) @@ -628,17 +632,17 @@ def get_grid_vertices(self): # Convert voxel space coordinates to world space def voxel_to_world(self, x, y, z): - x = (x - self.width//2)-0.5 - y = (y - self.height//2)-0.5 - z = (z - self.depth//2)-0.5 + x = (x - self.width // 2) - 0.5 + y = (y - self.height // 2) - 0.5 + z = (z - self.depth // 2) - 0.5 z = -z return x, y, z # Convert world space coordinates to voxel space def world_to_voxel(self, x, y, z): - x = (x + self.width//2)+0.5 - y = (y + self.height//2)+0.5 - z = (z - self.depth//2)-0.5 + x = (x + self.width // 2) + 0.5 + y = (y + self.height // 2) + 0.5 + z = (z - self.depth // 2) - 0.5 z = -z return x, y, z @@ -677,15 +681,15 @@ def get_bounding_box(self): minz = z if z > maxz: maxz = z - width = (maxx-minx)+1 - height = (maxy-miny)+1 - depth = (maxz-minz)+1 + width = (maxx - minx) + 1 + height = (maxy - miny) + 1 + depth = (maxz - minz) + 1 return minx, miny, minz, width, height, depth # Resize the voxel space. If no dimensions given, adjust to bounding box. # We offset all voxels on all axis by the given amount. # Resize all animation frames - def resize(self, width = None, height = None, depth = None, shift = 0): + def resize(self, width=None, height=None, depth=None, shift=0): # Reset undo buffer self._undo.clear() # No dimensions, use bounding box @@ -695,21 +699,21 @@ def resize(self, width = None, height = None, depth = None, shift = 0): for i, frame in enumerate(self._frames): # Create new data structure of the required size data = [[[0 for _ in xrange(depth)] - for _ in xrange(height)] + for _ in xrange(height)] for _ in xrange(width)] # Adjust ranges movewidth = min(width, cwidth) moveheight = min(height, cheight) movedepth = min(depth, cdepth) # Calculate translation - dx = (0-mx)+shift - dy = (0-my)+shift - dz = (0-mz)+shift + dx = (0 - mx) + shift + dy = (0 - my) + shift + dz = (0 - mz) + shift # Copy data over at new location - for x in xrange(mx, mx+movewidth): - for y in xrange(my, my+moveheight): - for z in xrange(mz, mz+movedepth): - data[x+dx][y+dy][z+dz] = frame[x][y][z] + for x in xrange(mx, mx + movewidth): + for y in xrange(my, my + moveheight): + for z in xrange(mz, mz + movedepth): + data[x + dx][y + dy][z + dz] = frame[x][y][z] self._frames[i] = data self._data = self._frames[self._current_frame] # Set new dimensions @@ -726,7 +730,7 @@ def rotate_about_axis(self, axis): self._undo.clear() if axis == self.Y_AXIS: - width = self.depth # note swap + width = self.depth # note swap height = self.height depth = self.width elif axis == self.X_AXIS: @@ -742,7 +746,7 @@ def rotate_about_axis(self, axis): # Create new temporary data structure data = [[[0 for _ in xrange(depth)] - for _ in xrange(height)] + for _ in xrange(height)] for _ in xrange(width)] # Copy data over at new location @@ -750,16 +754,16 @@ def rotate_about_axis(self, axis): for ty in xrange(0, self.height): for tz in xrange(0, self.depth): if axis == self.Y_AXIS: - dx = (-tz)-1 + dx = (-tz) - 1 dy = ty dz = tx elif axis == self.X_AXIS: dx = tx - dy = (-tz)-1 + dy = (-tz) - 1 dz = ty elif axis == self.Z_AXIS: dx = ty - dy = (-tx)-1 + dy = (-tx) - 1 dz = tz data[dx][dy][dz] = frame[tx][ty][tz] self._frames[i] = data @@ -782,7 +786,7 @@ def mirror_in_axis(self, axis): # Create new temporary data structure data = [[[0 for _ in xrange(self.depth)] - for _ in xrange(self.height)] + for _ in xrange(self.height)] for _ in xrange(self.width)] # Copy data over at new location @@ -791,16 +795,16 @@ def mirror_in_axis(self, axis): for tz in xrange(0, self.depth): if axis == self.Y_AXIS: dx = tx - dy = (-ty)-1 + dy = (-ty) - 1 dz = tz elif axis == self.X_AXIS: - dx = (-tx)-1 + dx = (-tx) - 1 dy = ty dz = tz elif axis == self.Z_AXIS: dx = tx dy = ty - dz = (-tz)-1 + dz = (-tz) - 1 data[dx][dy][dz] = frame[tx][ty][tz] self._frames[i] = data @@ -810,7 +814,7 @@ def mirror_in_axis(self, axis): self.changed = True # Translate the voxel data. - def translate(self, x, y, z, undo = True): + def translate(self, x, y, z, undo=True): # Sanity if x == 0 and y == 0 and z == 0: return @@ -818,19 +822,19 @@ def translate(self, x, y, z, undo = True): # Add to undo if undo: self._undo.add(UndoItem(Undo.TRANSLATE, - (-x, -y, -z), (x, y, z))) + (-x, -y, -z), (x, y, z))) # Create new temporary data structure data = [[[0 for _ in xrange(self.depth)] - for _ in xrange(self.height)] + for _ in xrange(self.height)] for _ in xrange(self.width)] # Copy data over at new location for tx in xrange(0, self.width): for ty in xrange(0, self.height): for tz in xrange(0, self.depth): - dx = (tx+x) % self.width - dy = (ty+y) % self.height - dz = (tz+z) % self.depth + dx = (tx + x) % self.width + dy = (ty + y) % self.height + dz = (tz + z) % self.depth data[dx][dy][dz] = self._data[tx][ty][tz] self._data = data self._frames[self._current_frame] = self._data @@ -873,5 +877,6 @@ def redo(self): # Enable/Disable undo buffer def disable_undo(self): self._undo.enabled = False + def enable_undo(self): self._undo.enabled = True diff --git a/src/voxel_grid.py b/src/voxel_grid.py index c53e3f2..f4fe59a 100644 --- a/src/voxel_grid.py +++ b/src/voxel_grid.py @@ -22,6 +22,8 @@ ## # Constants for the planes, to be used with a dictionary. + + class GridPlanes(object): X = "x" Y = "y" @@ -29,13 +31,16 @@ class GridPlanes(object): ## # Represents a plane to be used in the grid + + class GridPlane(object): ## # @param voxels Reference to the VoxelData # @param plane The plane where this grid belongs # @param offset The offset of the plane, relative to their negative end ( X = Left, Y = Bottom, Z = Front ) # @param visible Indicates if the plane is visible or not - def __init__( self, voxels, plane, offset, visible, color = QtGui.QColor("white") ): + + def __init__(self, voxels, plane, offset, visible, color=QtGui.QColor("white")): self._voxels = voxels self._plane = plane self._offset = 0 @@ -57,6 +62,7 @@ def __init__( self, voxels, plane, offset, visible, color = QtGui.QColor("white" @property def voxels(self): return self._voxels + @voxels.setter def voxels(self, voxels): self._voxels = voxels @@ -64,29 +70,32 @@ def voxels(self, voxels): @property def plane(self): return self._plane + @plane.setter def plane(self, value): - assert value in ( GridPlanes.X, GridPlanes.Y, GridPlanes.Z ) - if( value != self._plane ): + assert value in (GridPlanes.X, GridPlanes.Y, GridPlanes.Z) + if(value != self._plane): self._plane = value self.update_vertices() @property def offset(self): return self._offset + @offset.setter def offset(self, value): assert isinstance(value, int) offset_limit = self._offset_plane_limit[self.plane]() - if( value > offset_limit ): + if(value > offset_limit): value = offset_limit - if( value != self._offset ): + if(value != self._offset): self._offset = value self.update_vertices() @property def visible(self): return self._visible + @visible.setter def visible(self, value): assert isinstance(value, bool) @@ -95,9 +104,10 @@ def visible(self, value): @property def color(self): return self._color + @color.setter def color(self, value): - assert isinstance( value, QtGui.QColor ) + assert isinstance(value, QtGui.QColor) self._color = value @property @@ -107,67 +117,68 @@ def vertices(self): def update_vertices(self): self._vertices = self._methods_get_plane_vertices[self._plane]() self._vertices_array = array.array("f", self._vertices).tostring() - self._num_vertices = len(self._vertices)//3 + self._num_vertices = len(self._vertices) // 3 def _get_grid_vertices_x_plane(self): vertices = [] height = self._voxels.height depth = self._voxels.depth - for y in xrange(height+1): - vertices += self._voxels.voxel_to_world( self.offset, y, 0 ) - vertices += self._voxels.voxel_to_world( self.offset, y, depth ) - for z in xrange(depth+1): - vertices += self._voxels.voxel_to_world( self.offset, 0, z ) - vertices += self._voxels.voxel_to_world( self.offset, height, z ) + for y in xrange(height + 1): + vertices += self._voxels.voxel_to_world(self.offset, y, 0) + vertices += self._voxels.voxel_to_world(self.offset, y, depth) + for z in xrange(depth + 1): + vertices += self._voxels.voxel_to_world(self.offset, 0, z) + vertices += self._voxels.voxel_to_world(self.offset, height, z) return vertices def _get_grid_vertices_y_plane(self): vertices = [] width = self._voxels.width depth = self._voxels.depth - for z in xrange(depth+1): - vertices += self._voxels.voxel_to_world( 0, self.offset, z ) - vertices += self._voxels.voxel_to_world( width, self.offset, z ) - for x in xrange(width+1): - vertices += self._voxels.voxel_to_world( x, self.offset, 0 ) - vertices += self._voxels.voxel_to_world( x, self.offset, depth ) + for z in xrange(depth + 1): + vertices += self._voxels.voxel_to_world(0, self.offset, z) + vertices += self._voxels.voxel_to_world(width, self.offset, z) + for x in xrange(width + 1): + vertices += self._voxels.voxel_to_world(x, self.offset, 0) + vertices += self._voxels.voxel_to_world(x, self.offset, depth) return vertices def _get_grid_vertices_z_plane(self): vertices = [] width = self._voxels.width height = self._voxels.height - for x in xrange(width+1): - vertices += self._voxels.voxel_to_world( x, 0, self.offset ) - vertices += self._voxels.voxel_to_world( x, height, self.offset ) - for y in xrange(height+1): - vertices += self._voxels.voxel_to_world( 0, y, self.offset ) - vertices += self._voxels.voxel_to_world( width, y, self.offset ) + for x in xrange(width + 1): + vertices += self._voxels.voxel_to_world(x, 0, self.offset) + vertices += self._voxels.voxel_to_world(x, height, self.offset) + for y in xrange(height + 1): + vertices += self._voxels.voxel_to_world(0, y, self.offset) + vertices += self._voxels.voxel_to_world(width, y, self.offset) return vertices + class VoxelGrid(object): - def __init__(self, widget ): + def __init__(self, widget): self._voxels = widget self._planes = {} - def add_grid_plane(self, plane, offset, visible, color = QtGui.QColor("white") ): + def add_grid_plane(self, plane, offset, visible, color=QtGui.QColor("white")): key = (plane, offset) - if( key in self._planes.keys() ): + if(key in self._planes.keys()): self._planes[key].visible = visible else: - grid_plane = GridPlane( self._voxels, plane, offset, visible, color ) + grid_plane = GridPlane(self._voxels, plane, offset, visible, color) self._planes[key] = grid_plane def remove_grid_plane(self, plane, offset): key = (plane, offset) - if( key in self._planes.keys() ): + if(key in self._planes.keys()): del self._planes[key] # Return vertices for a floor grid def get_grid_plane(self, plane, offset): - key = ( plane, offset ) - if( key in self._planes.keys() ): + key = (plane, offset) + if(key in self._planes.keys()): return self._planes[key] else: return None @@ -186,14 +197,14 @@ def paint(self): glDisable(GL_TEXTURE_2D) for grid in self._planes.itervalues(): - if( not grid.visible ): + if(not grid.visible): continue red = grid.color.redF() green = grid.color.greenF() blue = grid.color.blueF() # Grid colour - glColor3f(red,green,blue) + glColor3f(red, green, blue) # Enable vertex buffers glEnableClientState(GL_VERTEX_ARRAY) @@ -211,11 +222,11 @@ def paint(self): glEnable(GL_LIGHTING) glEnable(GL_TEXTURE_2D) - def scale_offsets(self, width_scale = None, height_scale = None, depth_scale = None ): + def scale_offsets(self, width_scale=None, height_scale=None, depth_scale=None): for grid in self._planes.itervalues(): - if( grid.plane == GridPlanes.X and width_scale ): + if(grid.plane == GridPlanes.X and width_scale): grid.offset = int(round(grid.offset * width_scale)) - elif( grid.plane == GridPlanes.Y and height_scale ): + elif(grid.plane == GridPlanes.Y and height_scale): grid.offset = int(round(grid.offset * height_scale)) - elif( grid.plane == GridPlanes.Z and depth_scale ): + elif(grid.plane == GridPlanes.Z and depth_scale): grid.offset = int(round(grid.offset * depth_scale)) diff --git a/src/voxel_widget.py b/src/voxel_widget.py index 5daf286..d1b2ec3 100644 --- a/src/voxel_widget.py +++ b/src/voxel_widget.py @@ -28,6 +28,7 @@ from voxel_grid import VoxelGrid import time + class GLWidget(QtOpenGL.QGLWidget): # Constants for referring to axis @@ -43,6 +44,7 @@ class GLWidget(QtOpenGL.QGLWidget): @property def axis_grids(self): return self._display_axis_grids + @axis_grids.setter def axis_grids(self, value): self._display_axis_grids = value @@ -51,6 +53,7 @@ def axis_grids(self, value): @property def wireframe(self): return self._display_wireframe + @wireframe.setter def wireframe(self, value): self._display_wireframe = value @@ -59,6 +62,7 @@ def wireframe(self, value): @property def voxel_colour(self): return self._voxel_colour + @voxel_colour.setter def voxel_colour(self, value): self._voxel_colour = value @@ -66,6 +70,7 @@ def voxel_colour(self, value): @property def background(self): return self._background_colour + @background.setter def background(self, value): self._background_colour = value @@ -74,6 +79,7 @@ def background(self, value): @property def voxel_edges(self): return self._voxeledges + @voxel_edges.setter def voxel_edges(self, value): self._voxeledges = value @@ -89,7 +95,7 @@ def grids(self): drag_event = QtCore.Signal() end_drag_event = QtCore.Signal() - def __init__(self, parent = None): + def __init__(self, parent=None): glformat = QtOpenGL.QGLFormat() glformat.setVersion(1, 1) glformat.setProfile(QtOpenGL.QGLFormat.CoreProfile) @@ -106,9 +112,9 @@ def __init__(self, parent = None): # Mouse position self._mouse = QtCore.QPoint() self._mouse_absolute = QtCore.QPoint() - self.mouse_delta_relative = (0,0) - self.mouse_delta_absolute = (0,0) - self.mouse_position = (0,0) + self.mouse_delta_relative = (0, 0) + self.mouse_delta_absolute = (0, 0) + self.mouse_position = (0, 0) # Default camera self.reset_camera(False) # zoom @@ -124,12 +130,12 @@ def __init__(self, parent = None): # Grid manager self._grids = VoxelGrid(self.voxels) # create the default _grids - self.grids.add_grid_plane(GridPlanes.X, offset = 0, visible = True, - color = QtGui.QColor(0x6c, 0x7d, 0x67)) - self.grids.add_grid_plane(GridPlanes.Y, offset = 0, visible = True, - color = QtGui.QColor(0x65, 0x65, 0x7b)) - self.grids.add_grid_plane(GridPlanes.Z, offset = self.voxels.depth, - visible = True, color = QtGui.QColor(0x7b, 0x65, 0x68)) + self.grids.add_grid_plane(GridPlanes.X, offset=0, visible=True, + color=QtGui.QColor(0x6c, 0x7d, 0x67)) + self.grids.add_grid_plane(GridPlanes.Y, offset=0, visible=True, + color=QtGui.QColor(0x65, 0x65, 0x7b)) + self.grids.add_grid_plane(GridPlanes.Z, offset=self.voxels.depth, + visible=True, color=QtGui.QColor(0x7b, 0x65, 0x68)) # Used to track the z component of various mouse activity self._depth_focus = 1 # Keep track how long mouse buttons are down for @@ -151,7 +157,7 @@ def refresh(self): self.updateGL() # Reset camera position to defaults - def reset_camera(self, update = True): + def reset_camera(self, update=True): self._translate_x = 0 self._translate_y = 0 self._translate_z = -30 @@ -319,7 +325,7 @@ def mousePressEvent(self, event): self._mouse = QtCore.QPoint(event.pos()) self._mouse_absolute = QtCore.QPoint(event.pos()) - self.mouse_position = (self._mouse.x(),self._mouse.y()) + self.mouse_position = (self._mouse.x(), self._mouse.y()) self._button_down = None if event.buttons() & QtCore.Qt.LeftButton: @@ -353,14 +359,14 @@ def mousePressEvent(self, event): def mouseMoveEvent(self, event): - self.mouse_position = (self._mouse.x(),self._mouse.y()) + self.mouse_position = (self._mouse.x(), self._mouse.y()) ctrl = (self._key_modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) != 0 shift = (self._key_modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier) != 0 alt = (self._key_modifiers - & QtCore.Qt.KeyboardModifier.AltModifier) != 0 + & QtCore.Qt.KeyboardModifier.AltModifier) != 0 # Screen units delta dx = event.x() - self._mouse.x() @@ -369,7 +375,7 @@ def mouseMoveEvent(self, event): # Right mouse button held down with CTRL/cmd key - rotate # Or middle mouse button held if ((event.buttons() & QtCore.Qt.RightButton and ctrl) - or ((event.buttons() & QtCore.Qt.MiddleButton) and not ctrl)): + or ((event.buttons() & QtCore.Qt.MiddleButton) and not ctrl)): self._rotating = True self._rotate_x = self._rotate_x + dy self._rotate_y = self._rotate_y + dx @@ -378,7 +384,7 @@ def mouseMoveEvent(self, event): # Middle mouse button held down with CTRL - translate # or right mouse button with alt elif ((event.buttons() & QtCore.Qt.MiddleButton and ctrl) - or (event.buttons() & QtCore.Qt.RightButton and alt)): + or (event.buttons() & QtCore.Qt.RightButton and alt)): self._rotating = True # Work out the translation in 3d space self._translate_x = self._translate_x + dx * self._htranslate @@ -389,7 +395,7 @@ def mouseMoveEvent(self, event): # Remember the mouse deltas self.mouse_delta_relative = (dx, dy) self.mouse_delta_absolute = (event.x() - self._mouse_absolute.x(), - event.y() - self._mouse_absolute.y()) + event.y() - self._mouse_absolute.y()) # Maybe we are dragging if time.time() - self._mousedown_time > 0.3 and not self._dragging: @@ -483,7 +489,7 @@ def plane_intersection(self, x, y): planes = ( Plane(Vector3(1, 0, 0), origin[0]), Plane(Vector3(0, 1, 0), origin[1]), - Plane(Vector3(0, 0, 1), origin[2]+0.001)) + Plane(Vector3(0, 0, 1), origin[2] + 0.001)) intersection = None, None, None distance = sys.maxint for plane in planes: @@ -492,7 +498,7 @@ def plane_intersection(self, x, y): if intersect: # Adjust to voxel space coordinates x, y, z = self.voxels.world_to_voxel(intersect.x, - intersect.y, intersect.z) + intersect.y, intersect.z) x = int(x) y = int(y) z = int(z) @@ -522,7 +528,7 @@ def view_axis(self): # Convert window x,y coordinates into x,y,z world coordinates, also return # the depth - def window_to_world(self, x, y, z = None): + def window_to_world(self, x, y, z=None): # Find depth y = self._height - y if z is None: diff --git a/src/zoxel.py b/src/zoxel.py index 8869c6d..f7a33b7 100644 --- a/src/zoxel.py +++ b/src/zoxel.py @@ -19,6 +19,7 @@ from PySide import QtGui from mainwindow import MainWindow + def main(): # create application app = QtGui.QApplication(sys.argv)