diff options
Diffstat (limited to 'face.py')
-rw-r--r-- | face.py | 438 |
1 files changed, 438 insertions, 0 deletions
@@ -0,0 +1,438 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import math + +import gi +from gi.repository import Gtk +from gi.repository import Gdk + +class Face(Gtk.EventBox): + """La cara.""" + + def __init__(self): + + Gtk.EventBox.__init__(self) + + self.eyes_box = None + self.mouth = None + + self.type_eyes = 'Round' + self.type_mouth = 'Simple' + + self.eyes_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + mouthbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + + box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) + box.pack_start(self.eyes_box, True, True, 0) + box.pack_start(mouthbox, True, True, 0) + + self.mouth = Mouth() + mouthbox.pack_start(self.mouth, True, True, 0) + + self.add(box) + + self.set_number_eyes(2) + + self.show_all() + + def set_type_mouth(self, tipo): + """Actualizar el tipo de boca. + tipo puede ser 'Simple' o 'Waveform' o 'Frequency'""" + + self.type_mouth = tipo + self.mouth.tipo = self.type_mouth + + def set_type_eyes(self, tipo): + """Actualizar el tipo de ojos. + tipo puede ser 'Round' o 'Glasses'""" + + self.type_eyes = tipo + for child in self.eyes_box.get_children(): + child.set_type(self.type_eyes) + + def set_number_eyes(self, number): + """Actualizar la cantidad de ojos.""" + + while len(self.eyes_box.get_children()) < number: + self.add_eye() + + while len(self.eyes_box.get_children()) > number: + self.del_eye() + + self.set_type_eyes(self.type_eyes) + + def add_eye(self): + """Agrega un ojo.""" + + self.eyes_box.pack_start(Eye(), True, True, 0) + + def del_eye(self): + """Quita un ojo.""" + + childs = self.eyes_box.get_children() + if childs: + eye = childs[-1] + self.eyes_box.remove(eye) + eye.destroy() + + def look_at(self, pos): + """Mirar hacia pos""" + + x, y = pos + map(lambda e, x=x, y=y: e.look_at(x, y), self.eyes_box.get_children()) + + +class Eye(Gtk.DrawingArea): + """El ojo. + self.tipo puede ser 'Round' o 'Glasses'.""" + + def __init__(self): + + Gtk.DrawingArea.__init__(self) + + self.x, self.y = 0, 0 + self.fill_color = (49000, 52000, 18000) + self.tipo = 'Round' + + self.show_all() + + def set_type(self, tipo): + """Cambia el tipo de ojo. + tipo puede ser 'Round' o 'Glasses'""" + + self.tipo = tipo + self.queue_draw() + + def look_at(self, x, y): + """ Look. . .""" + + self.x = x + self.y = y + self.queue_draw() + + def computePupil(self): + """La Pupila.""" + + rect = self.get_allocation() + + if self.x is None or self.y is None: + if rect.x + rect.width / 2 < rect.width / 2: + cx = rect.width * 0.6 + else: + cx = rect.width * 0.4 + return cx, rect.height * 0.6 + + EYE_X, EYE_Y = self.translate_coordinates( + self.get_toplevel(), rect.width / 2, rect.height / 2) + EYE_HWIDTH = rect.width + EYE_HHEIGHT = rect.height + BALL_DIST = EYE_HWIDTH / 4 + + dx = self.x - EYE_X + dy = self.y - EYE_Y + + if dx or dy: + angle = math.atan2(dy, dx) + cosa = math.cos(angle) + sina = math.sin(angle) + h = math.hypot(EYE_HHEIGHT * cosa, EYE_HWIDTH * sina) + x = (EYE_HWIDTH * EYE_HHEIGHT) * cosa / h + y = (EYE_HWIDTH * EYE_HHEIGHT) * sina / h + dist = BALL_DIST * math.hypot(x, y) + + if dist < math.hypot(dx, dy): + dx = dist * cosa + dy = dist * sina + + return rect.width / 2 + dx, rect.height / 2 + dy + + def do_draw(self, context): + rect = self.get_allocation() + eyeSize = min(rect.width, rect.height) + + outlineWidth = eyeSize / 20.0 + pupilSize = eyeSize / 10.0 + pupilX, pupilY = self.computePupil() + dX = pupilX - rect.width / 2. + dY = pupilY - rect.height / 2. + distance = math.sqrt(dX * dX + dY * dY) + limit = eyeSize / 2 - outlineWidth * 2 - pupilSize + if distance > limit: + pupilX = rect.width / 2 + dX * limit / distance + pupilY = rect.height / 2 + dY * limit / distance + + r,g,b = self.fill_color + context.set_source_rgba(r,g,b,1) + context.rectangle(0, 0, rect.width, rect.height) + context.fill() + + if self.tipo == 'Round': + # eye ball + context.set_source_rgb(1, 1, 1) + context.arc(rect.width / 2, + rect.height / 2, + eyeSize / 2 - outlineWidth / 2, + 0, 360) + context.fill() + + # outline + context.set_source_rgb(0, 0, 0) + context.set_line_width(outlineWidth) + context.arc(rect.width / 2, + rect.height / 2, + eyeSize / 2 - outlineWidth / 2, + 0, 360) + context.stroke() + + elif self.tipo == 'Glasses': + def roundrect(x1, y1, x2, y2): + context.move_to(x1, (y1 + y2) / 2.) + context.curve_to(x1, y1, x1, y1, (x1 + x2) / 2., y1) + context.curve_to(x2, y1, x2, y1, x2, (y1 + y2) / 2.) + context.curve_to(x2, y2, x2, y2, (x1 + x2) / 2., y2) + context.curve_to(x1, y2, x1, y2, x1, (y1 + y2) / 2.) + + # eye ball + context.set_source_rgb(1, 1, 1) + roundrect(outlineWidth, + outlineWidth, + rect.width - outlineWidth, + rect.height - outlineWidth) + context.fill() + + # outline + context.set_source_rgb(0, 0, 0) + context.set_line_width(outlineWidth) + roundrect(outlineWidth, + outlineWidth, + rect.width - outlineWidth, + rect.height - outlineWidth) + context.stroke() + + # pupil + context.arc(pupilX, pupilY, pupilSize, 0, 360) + context.set_source_rgb(0, 0, 0) + context.fill() + + return True + +class Mouth(Gtk.DrawingArea): + """La boca.""" + + def __init__(self): + + Gtk.DrawingArea.__init__(self) + + self.fill_color = (49000, 52000, 18000) + self.volume = 0 + self.tipo = "Simple" + + self.show_all() + + def do_draw(self, context): + + rect = self.get_allocation() + + #self.processBuffer() + + # background + r,g,b = self.fill_color + context.set_source_rgba(r,g,b,1) + context.paint() + + # Draw the mouth + volume = self.volume / 30000. + mouthH = volume * rect.height + mouthW = volume ** 2 * (rect.width / 2.) + rect.width / 2. + # T + # L R + # B + Lx, Ly = rect.width / 2 - mouthW / 2, rect.height / 2 + Tx, Ty = rect.width / 2, rect.height / 2 - mouthH / 2 + Rx, Ry = rect.width / 2 + mouthW / 2, rect.height / 2 + Bx, By = rect.width / 2, rect.height / 2 + mouthH / 2 + context.set_line_width(min(rect.height / 10.0, 10)) + context.move_to(Lx, Ly) + context.curve_to(Tx, Ty, Tx, Ty, Rx, Ry) + context.curve_to(Bx, By, Bx, By, Lx, Ly) + context.set_source_rgb(0, 0, 0) + context.close_path() + context.stroke() + + return True + + ''' + def _new_buffer(self, obj, buf): + if len(buf) < 28: + self.newest_buffer = [] + else: + self.newest_buffer = list(unpack(str(int(len(buf)) / 2) + 'h', + buf)) + self.main_buffers += self.newest_buffer + if(len(self.main_buffers) > self.buffer_size): + del self.main_buffers[0:(len(self.main_buffers) - \ + self.buffer_size)] + + self.queue_draw() + return True + + def processBuffer(self): + if len(self.main_buffers) == 0 or len(self.newest_buffer) == 0: + self.volume = 0 + else: + self.volume = numpy.core.max(self.main_buffers) # -\ + # numpy.core.min(self.main_buffers) + + + + +class WaveformMouth(Mouth): + def __init__(self, audioSource, fill_color): + + Mouth.__init__(self, audioSource, fill_color) + + self.buffer_size = 100 + self.peaks = [] + + self.stop = False + + self.y_mag_bias_multiplier = 1 + self.y_mag = 0.7 + + self.show_all() + + def draw_wave(self, context): + rect = self.get_allocation() + self.param1 = rect.height / 65536.0 + self.param2 = rect.height / 2.0 + + # background + context.set_source_rgba(*self.fill_color.get_rgba()) + context.paint() + + # Draw the waveform + context.set_line_width(min(rect.height / 10.0, 10)) + count = 0 + buflen = float(len(self.main_buffers)) + for value in self.main_buffers: + peak = float(self.param1 * value * self.y_mag) +\ + self.y_mag_bias_multiplier * self.param2 + + if peak >= rect.height: + peak = rect.height + if peak <= 0: + peak = 0 + + x = count / buflen * rect.width + context.line_to(x, rect.height - peak) + + count += 1 + context.set_source_rgb(0, 0, 0) + context.stroke() + + return True + + +class FFTMouth(Mouth): + def __init__(self, audioSource, fill_color): + + Mouth.__init__(self, audioSource, fill_color) + + self.peaks = [] + + self.y_mag = 1.7 + self.freq_range = 70 + self.draw_interval = 1 + self.num_of_points = 105 + + self.stop = False + + #constant to multiply with self.param2 while scaling values + self.y_mag_bias_multiplier = 1 + + self.fftx = [] + + self.scaleX = "10" + self.scaleY = "10" + + self.show_all() + self.connect('draw', self.draw_fftmouth) + + def newprocessBuffer(self, rect): + self.param1 = rect.height / 65536.0 + self.param2 = rect.height / 2.0 + + if(self.stop == False): + + Fs = 48000 + nfft = 65536 + self.newest_buffer = self.newest_buffer[0:256] + self.fftx = fft(self.newest_buffer, 256, -1) + + self.fftx = self.fftx[0:self.freq_range * 2] + self.draw_interval = rect.width / (self.freq_range * 2.) + + NumUniquePts = ceil((nfft + 1) / 2) + self.buffers = abs(self.fftx) * 0.02 + self.y_mag_bias_multiplier = 0.1 + self.scaleX = "hz" + self.scaleY = "" + + if(len(self.buffers) == 0): + return False + + # Scaling the values + val = [] + for i in self.buffers: + temp_val_float = float(self.param1 * i * self.y_mag) +\ + self.y_mag_bias_multiplier * self.param2 + + if(temp_val_float >= rect.height): + temp_val_float = rect.height - 25 + if(temp_val_float <= 0): + temp_val_float = 25 + val.append(temp_val_float) + + self.peaks = val + + def draw_fftmouth(self, widget, context): + rect = widget.get_allocation() + + self.newprocessBuffer(rect) + + # background + context.set_source_rgba(*self.fill_color.get_rgba()) + context.paint() + + # Draw the waveform + context.set_line_width(min(rect.height / 10.0, 10)) + context.set_source_rgb(0, 0, 0) + count = 0 + for peak in self.peaks: + context.line_to(rect.width / 2 + count, + rect.height / 2 - peak) + count += self.draw_interval + context.stroke() + count = 0 + for peak in self.peaks: + context.line_to(rect.width / 2 - count, + rect.height / 2 - peak) + count += self.draw_interval + context.stroke() + + return True'''
\ No newline at end of file |