# ----------------------------------------------------------------------------- # Copyright (c) 2007, 2008 by Severn Clay # # 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 Blender from Blender import Mesh, Object, Scene, Material from Blender.Mathutils import CrossVecs, Vector, Matrix, AngleBetweenVecs,\ MidpointVecs, DotVecs from pantographConfig import * try: from scipy import weave WEAVE = True except: print('Weave library not found - you will experience somewhat slower rendering times, sorry.') WEAVE = False try: import ming MING = True except: print('Ming library not found - you will not be able to render animations') MING = False try: import cairo except: print('Cairo library not found - you will not be able export to SVG, PDF or PNG formats') CAIRO = False from copy import deepcopy, copy from bisect import insort from math import tan,atan,pi,degrees,sqrt,radians from decimal import Decimal from time import clock import cPickle import sys, os, gc, types try: from Polygon import * except: print "Make sure Polygon is installed and properly compiled" print "and linked for your installation of Python" print "http://download.dezentral.de/soft/Python/Polygon/" #---GLOBAL VARIABLES---#AA00AA#FFFFFF------------------------------------------- CLASSIFY_CALLS = 0 SPLIT_CALLS = 0 MAX_DEPTH = 0 PROCESSED_FACES = 0 TOTAL_FACES = 0 CLIP_COUNTER = 0 #Precision############# setTolerance(EPSILON2D) #this controls the tolerance of the Polygon package RECURSION_LIMIT = 100 #this is set to prevent the BSP algorithm from over-recursing #Spacial Classification############## NOT_SET = None IN_FRONT = 0001 ON = 0100 BEHIND = 0010 INTERSECTING = 0011 #Face Types########################## GEOMETRY_TYPE = 1004 CUTTING_TYPE = 1005 PARTITION_TYPE = 1006 FRUSTUM_TYPE = 1007 VIGNETTE_TYPE = 1008 FRONT_FACING = 1009 BACK_FACING = 1010 ###Move to pantographConfig.py### #User Variables###################### #These set the default settings when the script is run: CULL_BACKFACES = False USE_ALPHA = True RENDER_ANIMATION = False AUTO_CREASE = False LOAD_PREVIOUS_SETTINGS = True EPSILON3D = .0001 EPSILON2D = .0001 ### #FACE BIT CODES: FRONT_FACING = 00000001 #Face normal faces towards the camera = 1 BACK_FACING = 00000010 #Face normal faces away from camera = 2 #EDGE BIT CODES: CREASE = 00000001 #Determines whether the edge will become a crease or not = 1 MESH = 00000010 #The edge is an original poly edge, before trinagulation = 2 HIDDEN = 00000100 #The edge has been occluded = 4 SILHOUETTE = 00001000 #Silhouette edge, between front-facing and back-facing polygons = 8 DRAWN = 00010000 #Flag that indicates that the edge has been drawn = 16 CLIPPED = 00100000 #Indicates the edge has been visited by the 2d clipping algorithm = 32 CUT = 01000000 #An edge that has been clipped by a Cutaway or Vignette = 64 CUTISVISIBLE = 10000000 #determines whether a cut edge should be shown as a Cut or Silhouette = 128 #SCENEGRAPH DICTS: EdgeDict = dict() #a dict of {(mesh, edge):edgeBitCodes} pairs FaceDict = dict() #a dict of {(mesh, face):faceBitCodes} pairs #Installation settings############### LIB_VERSION = "0.5" #---3D GEOMETRY FUNCTIONS---#FF0000#FFFFFF----------------------------- def splitFaceWithPlane(face, scene, mesh, pointOnPlane, planeNormal, cMap=False): """ Splits a face with an infinite plane, resulting in a tuple of: ([front faces], [behind faces]) In cases where there is no split, returns (None, [behind faces]) or ([front faces], None) In cases where the face is parallel to and on the plane, returns (False, False) Attributes of split faces will match the original face """ if not cMap: cMap = [classifyPointPlane(vert.co, pointOnPlane, planeNormal) for vert in face.verts] #First, check to see whether the face falls entirely on one side or the other r = classifyFaceFace(face, pointOnPlane, planeNormal, cMap) if r is IN_FRONT: return (face, None) elif r is BEHIND: return (None, face) #We know that we have a split resulting in two new faces global SPLIT_CALLS SPLIT_CALLS += 1 classifyDict = dict(zip([vert.co for vert in face.verts],cMap)) frontVerts = list() frontEdges = list() backVerts = list() backEdges = list() intersectEdge = None intersectVerts = list() #iterate through vert pairs to find the intersection points vertSeq = face.verts for (vert, nextVert) in zip(vertSeq, (vertSeq[1:] + vertSeq[0])): myEdge = EdgeDict[(mesh,(min(vert.index,nextVert.index), max(vert.index,nextVert.index)))] if classifyDict[vert] is IN_FRONT: frontVerts.append(vert) if classifyDict[nextVert] is IN_FRONT: frontEdges.append(myEdge) elif classifyDict[vert] is ON: intersectVerts.append(vert) if classifyDict[nextVert] is IN_FRONT: frontEdges.append(myEdge) frontVerts.append(vert) elif classifyDict[nextVert] is BEHIND: backEdges.append(myEdge) backVerts.append(vert) elif classifyDict[vert] is BEHIND: backVerts.append(vert) if nextVert is BEHIND: backEdges.append(myEdge) elif (classifyDict[vert] + classifyDict[nextVert]) is INTERSECTING: splitVerts = splitEdge(vert, nextVert, pointOnPlane, planeNormal) frontVerts.extend(splitVerts[0:1]) intersectVerts.append(splitVerts[1]) backVerts.extend(splitVerts[0:1]) #make new front edge, append it #append dummy intersectEdge to both #make new back edge, append it if len(intersectVerts) == 2: #intersectEdge = make new edge elif len(intersectVerts) > 2: raise "Too many intersection points!"