Source code for geometry3d

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-

""" 
**Project Name:**      MakeHuman

**Product Home Page:** http://www.makehuman.org/

**Code Home Page:**    https://bitbucket.org/MakeHuman/makehuman/

**Authors:**           Marc Flerackers

**Copyright(c):**      MakeHuman Team 2001-2015

**Licensing:**         AGPL3 (http://www.makehuman.org/doc/node/the_makehuman_application.html)

    This file is part of MakeHuman (www.makehuman.org).

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**Coding Standards:**  See http://www.makehuman.org/node/165

Abstract
--------

This module contains classes for commonly used geometry
"""

import module3d
import numpy as np
import transformations as tm

[docs]class RectangleMesh(module3d.Object3D): """ A filled rectangle. :param width: The width. :type width: int or float :param height: The height. :type height: int or float :param centered True to center the mesh around its local origin. :type centered bool :param texture: The texture. :type texture: str """ def __init__(self, width, height, centered = False, texture=None, rotation=None): module3d.Object3D.__init__(self, 'rectangle_%s' % texture) self.centered = centered self.coord_rotation = rotation if rotation is None: self.inv_coord_rotation = None else: self.inv_coord_rotation = tm.inverse_matrix(rotation) # create group fg = self.createFaceGroup('rectangle') # The 4 vertices v = self._getVerts(width, height) # The 4 uv values uv = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) # The face fv = [(0,1,2,3)] fuv = [(0,1,2,3)] self.setCoords(v) self.setUVs(uv) self.setFaces(fv, fuv, fg.idx) self.setCameraProjection(1) self.updateIndexBuffer() def _getVerts(self, width, height, no_rotation=False): if self.centered: v = [ (-width/2, -height/2, 0.0), (width/2, -height/2, 0.0), (width/2, height/2, 0.0), (-width/2, height/2, 0.0) ] else: v = [ (0.0, 0.0, 0.0), (width, 0.0, 0.0), (width, height, 0.0), (0.0, height, 0.0) ] v = np.asarray(v, dtype=np.float32) if no_rotation: return v else: return self._rotatedVerts(v) def _originalVerts(self, verts): if self.inv_coord_rotation is not None: v = np.zeros((4,4), dtype=np.float32) v[:,:3] = verts[:,:] for idx, vert in enumerate(v): v[idx,:] = self.inv_coord_rotation.dot( vert )[:] return v[:,:3] else: return verts.copy() def _rotatedVerts(self, verts): if self.coord_rotation is not None: v = np.zeros((4,4), dtype=np.float32) v[:,:3] = verts[:,:] for idx, vert in enumerate(v): v[idx,:] = self.coord_rotation.dot( vert )[:] return v[:,:3] else: return verts.copy() def move(self, dx, dy): coords = self._originalVerts(self.coord) + np.asarray((dx, dy, 0), dtype=np.float32) self.coord = self._rotatedVerts(coords) self.markCoords(coor=True) self.update() def setPosition(self, x, y): width, height = self.getSize() v = self._getVerts(width, height, True) + np.asarray((x, y, 0), dtype=np.float32) self.changeCoords(self._rotatedVerts(v)) self.update() def resetPosition(self): width, height = self.getSize() v = self._getVerts(width, height) self.changeCoords(v) self.update() def resize(self, width, height): dx, dy = self.getOffset() v = self._getVerts(width, height, True) v[:, 0] += dx v[:, 1] += dy self.changeCoords(self._rotatedVerts(v)) self.update() def setColors(self, bottomLeft, bottomRight, topRight, topLeft): def _toNpArray(arr): if len(arr) == 3: return np.asarray(arr+[1.0], dtype=np.float32) else: return np.asarray(arr, dtype=np.float32) color = np.asarray([255 * _toNpArray(bottomLeft), 255 * _toNpArray(bottomRight), 255 * _toNpArray(topRight), 255 * _toNpArray(topLeft) ], dtype=np.uint8) self.setColor(color) def _bbox(self, ignore_rotation=True): if ignore_rotation: coord = self._originalVerts(self.coord) if len(coord) == 0: return np.zeros((2,3), dtype = np.float32) v0 = np.amin(coord, axis=0) v1 = np.amax(coord, axis=0) return np.vstack((v0, v1)) else: return self.calcBBox() def getSize(self): ((x0,y0,z0),(x1,y1,z1)) = self._bbox(True) return (x1 - x0, y0 - y1) def getOffset(self): ((x0,y0,z0),(x1,y1,z1)) = self._bbox(True) if self.centered: w, h = (x1 - x0, y0 - y1) dx = x0+w/2 dy = y1+h/2 else: dx = x0 dy = y1 return dx, dy
class AxisMesh(module3d.Object3D): def __init__(self, scale=1.0): import files3d import getpath module3d.Object3D.__init__(self, 'axis', 4) files3d.loadMesh(getpath.getSysDataPath('3dobjs/axis.obj'), maxFaces=4, obj=self) for fg_name in self.getFaceGroups(): if 'red' in fg_name.lower(): self.color[self.getVerticesForGroups([fg_name])] = [255, 0, 0, 255] elif 'green' in fg_name.lower(): self.color[self.getVerticesForGroups([fg_name])] = [0, 255, 0, 255] elif 'blue' in fg_name.lower(): self.color[self.getVerticesForGroups([fg_name])] = [0, 0, 255, 255] self.markCoords(colr=True) self.sync_color() if scale != 1.0: self.coord[:] *= float(scale) self.markCoords(coor=True) self.sync_coord() # These are recommended, but cannot be assigned until this mesh is attached to an Object #self.material.ambientColor=[0.2,0.2,0.2] #self.material.configureShading(vertexColors=True)
[docs]class FrameMesh(module3d.Object3D): """ A wire rectangle. :param width: The width. :type width: int or float :param height: The height. :type height: int or float """ def __init__(self, width, height): module3d.Object3D.__init__(self, 'frame', 2) # create group fg = self.createFaceGroup('frame') # The 4 vertices v = [ (0.0, 0.0, 0.0), (width, 0.0, 0.0), (width, height, 0.0), (0.0, height, 0.0) ] # The 4 uv values uv = ((0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)) # The faces f = [(0,3),(1,0),(2,1),(3,2)] self.setCoords(v) self.setUVs(uv) self.setFaces(f, f, fg.idx) self.setCameraProjection(1) self.updateIndexBuffer() def move(self, dx, dy): self.coord += (dx, dy, 0) self.markCoords(coor=True) self.update() def resize(self, width, height): v = [ (0.0, 0.0, 0.0), (width, 0.0, 0.0), (width, height, 0.0), (0.0, height, 0.0) ] self.changeCoords(v) self.update()
[docs]class Cube(module3d.Object3D): """ A cube. :param width: The width. :type width: int or float :param height: The height, if 0 it will be equal to width. :type height: int or float :param depth: The depth, if 0 it will be equal to width. :type depth: int or float :param texture: The texture. :type texture: str """ def __init__(self, width, height=0, depth=0, texture=None): module3d.Object3D.__init__(self, 'cube_%s' % texture) self.width = width self.height = height or width self.depth = depth or width # create group fg = self.createFaceGroup('cube') # The 8 vertices v = [(x,y,z) for z in [0,self.depth] for y in [0,self.height] for x in [0,self.width]] # /0-----1\ # / | | \ # |4---------5| # | | | | # | 3-----2 | # | / \ | # |/ \| # |7---------6| # The 4 uv values uv = ([0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]) # The 6 faces f = [ (4, 5, 6, 7), # front (1, 0, 3, 2), # back (0, 4, 7, 3), # left (5, 1, 2, 6), # right (0, 1, 5, 4), # top (7, 6, 2, 3), # bottom ] self.setCoords(v) self.setUVs(uv) self.setFaces(f, fg.idx) self.setCameraProjection(0) self.updateIndexBuffer() def resize(self, width, height, depth): v = [(x,y,z) for z in [0,depth] for y in [0,height] for x in [0,width]] self.changeCoords(v) self.update()
class GridMesh(module3d.Object3D): def __init__(self, rows, columns, spacing=1, offset=0, plane=0, subgrids = 0, static = False): """ Plane: 0 for back plane, 1 for ground plane """ typeName = "ground" if plane == 1 else "back" module3d.Object3D.__init__(self, '%s_grid' % typeName, vertsPerPrimitive = 2) self.plane = plane # create group fg = self.createFaceGroup('grid') hBoxes = rows vBoxes = columns # Nb of lines rows += 1 columns += 1 # Vertices size = (columns+rows) self.subgrids = subgrids if self.subgrids > 0: size = size + vBoxes * (subgrids - 1) + hBoxes * (subgrids - 1) v = np.zeros((2 * size, 3), dtype = np.float32) f = np.zeros((size, 2), dtype = np.float32) hBegin = (-(rows/2)) * spacing hEnd = hBegin + (rows * spacing) vBegin = (-(columns/2)) * spacing vEnd = vBegin + (columns * spacing) # Horizontal lines for i in xrange(rows): pos = hBegin + (i * spacing) if plane == 1: v[2*i] = [pos, offset, vBegin ] v[2*i +1] = [pos, offset, vEnd-spacing] else: v[2*i] = [vBegin, pos, offset] v[2*i +1] = [vEnd-spacing, pos, offset] f[i] = [2*i, 2*i +1] # Vertical lines for i in xrange(columns): pos = vBegin + (i * spacing) if plane == 1: v[2* (rows+i) ] = [hBegin, offset, pos] v[2* (rows+i) +1] = [hEnd-spacing, offset, pos] else: v[2* (rows+i) ] = [pos, hBegin, offset] v[2* (rows+i) +1] = [pos, hEnd-spacing, offset] f[rows+ i] = [2* (rows+i), 2* (rows+i) +1] self.mainGridEnd = 2*(columns+rows) # Subgrid if self.subgrids > 0: boxspacing = spacing spacing = float(spacing) / self.subgrids # Horizontal lines sub = self.mainGridEnd/2 for i in xrange(hBoxes*(subgrids-1)): boxOffset = (spacing * (i // (subgrids-1))) pos = spacing + hBegin + (i * spacing) + boxOffset if plane == 1: v[2* (sub+i)] = [pos, offset, vBegin ] v[2* (sub+i) +1] = [pos, offset, vEnd-boxspacing] else: v[2* (sub+i)] = [vBegin, pos, offset] v[2* (sub+i) +1] = [vEnd-boxspacing, pos, offset] f[sub+ i] = [2*(sub+i), 2*(sub+i) +1] # Vertical lines sub += hBoxes*(subgrids-1) for i in xrange(vBoxes*(subgrids-1)): boxOffset = (spacing * (i // (subgrids-1))) pos = spacing + vBegin + (i * spacing) + boxOffset if plane == 1: v[2* (sub+i) ] = [hBegin, offset, pos] v[2* (sub+i) +1] = [hEnd-boxspacing, offset, pos] else: v[2* (sub+i) ] = [pos, hBegin, offset] v[2* (sub+i) +1] = [pos, hEnd-boxspacing, offset] f[sub+ i] = [2* (sub+i), 2* (sub+i) +1] self.setCoords(v) self.setFaces(f, None, fg.idx) self.updateIndexBuffer() self.setCameraProjection(1 if static else 0) self.restrictVisibleToCamera = False # Set to True to only show the grid when the camera is set to a defined parallel view (front, left, top, ...) self.restrictVisibleAboveGround = False # Set to true to make the grid invisible when camera inclination is below 0 self.minSubgridZoom = 1.0 # Minimum zoom factor of the camera in which the subgrid will be shown self._subgridVisible = True def hasSubGrid(self): #return self.mainGridEnd < (len(self.coord) - 2) return self.subgrids > 0 def setMainColor(self, color): """ Set the color of the main grid. """ self._setVertColors(color, 0, self.mainGridEnd) def setSubColor(self, color): """ Set the color of the sub grid. """ if not self.hasSubGrid(): return self._setVertColors(color, self.mainGridEnd, len(self.coord)) def _setVertColors(self, color, beginIdx, endIdx): color = list(color) if len(color) == 3: color = color + [1.0] size = endIdx - beginIdx color = np.asarray([255*c for c in color], dtype=np.uint8) col = np.tile(color, size).reshape((size, 4)) clr = self.color.copy() clr[beginIdx:endIdx] = col[:] self.setColor(clr) @module3d.Object3D.visibility.getter def visibility(self): if not self._visibility: return False from core import G camera = G.cameras[self.cameraMode] if self.hasSubGrid(): subgridVisible = (camera.zoomFactor/camera.radius*10) >= self.minSubgridZoom if subgridVisible != self._subgridVisible: # Update subgrid visibility self._subgridVisible = subgridVisible mask = self.face_mask #mask = np.ones(self.getFaceCount(), dtype=np.bool) if subgridVisible: mask[self.mainGridEnd/2:] = True else: mask[self.mainGridEnd/2:] = False self.changeFaceMask(mask) self.updateIndexBufferFaces() if self.restrictVisibleAboveGround: if camera.getVerticalInclination() > 180 or camera.getVerticalInclination() < 0: return False if self.restrictVisibleToCamera: #return camera.isInParallelView() # Hide the grid in top and bottom view: return camera.isInFrontView() or camera.isInBackView() or camera.isInSideView() elif self.plane == 1 and (camera.isInFrontView() or camera.isInBackView() or camera.isInSideView()): # Hide if this is a horizontal grid and camera is looking at it horizontally return False elif self.plane == 0 and (camera.isInTopView() and camera.isInBottomView()): # Hide if this is a vertical grid and camera is looking at it horizontally return False else: return super(GridMesh, self).visibility