#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import numpy
from numpy import array, radians, cos, sin, linspace, sqrt, ceil
from GWL.GWL_parser import GWLobject, calculateNvoxelsAndInterVoxelDistance
#import bpy
#import blender_scripts.blender_utilities as blender_utilities
## different ways of writing tubes:
#-horizontal circles
#-vertical lines
#-spirals
#-constant angular step or constant distance between points...
#-other still unknown way of doing it -> a function should be passed as arg
## example suppressor function
#(a,b) = suppressor(A,B)
#a=b=None if not allowed
#a=A,b=B if both allowed
#a=intersection point or b=intersection point if it's the case
## "boolean operations":
#-linear cutoffs: xmin,xmax,ymin,ymax
#-angular cutoff: thetamin,thetamax
#-other arbitrary supression of points -> a function should be passed as arg
## geometric vs writing parameters:
#-height, radius, centro, etc -> same no matter what writing method
#-steps, distance between points, etc -> change depending on writing method
## fundamental formulas
# Npts = Nsteps + 1
# E2E_size = C2C_size + voxelsize
# overlap = max(1-step/voxelsize, 0)
# C2C_size = end - start = Nsteps*step
## derived formulas
# C2C_size = E2E_size - voxelsize = end - start = Nsteps*step
# E2E_size = (Npts-1)*step + voxelsize
## calculation parameters:
#-voxel size
#-overlap
#-min/max overlap, etc
#class tube -> geometry params
#def setWriter(spiralwriter)
#class spiralwriter -> writing params
## TODO: boolean operations on meshes without faces, only edges (GWL files), either as blender script/modifier or as an external function applicable to any GWLobject (and similar like FIB)
# -> so no point in implementing it inside tube and similar now
# Papa class:
[docs]class TubePapa(GWLobject):
def __init__(self):
GWLobject.__init__(self)
[docs] def computePoints(self):
self.clear()
write_sequence = []
write_sequence.append([0,0,0])
write_sequence.append([1,0,0])
write_sequence.append([2,1,0])
self.GWL_voxels.append(write_sequence)
write_sequence = []
write_sequence.append([0,0,1])
write_sequence.append([1,0,1])
write_sequence.append([2,1,1])
self.GWL_voxels.append(write_sequence)
[docs] def writeGWL(self, filename, writingOffset = [0,0,0,0]):
print('TubePapa.writeGWL')
self.computePoints()
GWLobject.writeGWL(self, filename, writingOffset)
return
[docs] def getMeshData(self, position = [0,0,0]):
self.computePoints()
return(GWLobject.getMeshData(self, position))
[docs]def suppressorFunction_Xmax(A, B, Xmin):
pointList = []
if Xmin<A[0]:
if Xmin<B[0]:
pointList = [A,B]
connectLeft = True
connectRight = True
else:
pointList = [A,B]
connectLeft = True
connectRight = False
else:
if Xmin<B[0]:
pointList = [A,B]
connectLeft = True
connectRight = True
else:
pointList = []
connectLeft = False
connectRight = False
return pointList, connectLeft, connectRight
[docs]class HorizontalCircleTubeWithConstantAngularStep(GWLobject):
def __init__(self):
self.NumberOfSteps_R = 2
self.NumberOfSteps_Theta = 9
self.NumberOfSteps_Z = 2
self.StepSize_R = 50/cos(radians(22.5))
self.StepSize_ThetaDegrees = 45
self.StepSize_Z = 40
self.Start_R = 100/cos(radians(22.5))
self.Start_ThetaDegrees = 22.5
self.Start_Z = -20
self.centro = array([0,0,60,0]) # we are always in a 4D space...
self.powerFunction = lambda x, y, z: -1
self.suppressorFunction = lambda A, B: (A,B)
#self.inner_radius = 1
#self.outer_radius = 2
#self.height = 1
#self.power = -1
#self.PointDistance_r = 0.1
#self.PointDistance_theta = 0.1
#self.PointDistance_z = 0.1
#self.downwardWriting = True
return
[docs] def setCentro(self, vec3or4):
if len(vec3or4)>=4:
self.centro = array([vec3or4[0],vec3or4[1],vec3or4[2],vec3or4[3]])
else:
self.centro = array([vec3or4[0],vec3or4[1],vec3or4[2],0])
return
[docs] def setNumberOfSteps_R(self, NumberOfSteps_R):
self.NumberOfSteps_R = int(NumberOfSteps_R)
return
[docs] def setNumberOfSteps_Theta(self, NumberOfSteps_Theta):
self.NumberOfSteps_Theta = int(NumberOfSteps_Theta)
return
[docs] def setNumberOfSteps_Z(self, NumberOfSteps_Z):
self.NumberOfSteps_Z = int(NumberOfSteps_Z)
return
[docs] def setStepSize_R(self, StepSize_R):
self.StepSize_R = StepSize_R
return
[docs] def setStepSize_ThetaDegrees(self, StepSize_ThetaDegrees):
self.StepSize_ThetaDegrees = StepSize_ThetaDegrees
return
[docs] def setStepSize_Z(self, StepSize_Z):
self.StepSize_Z = StepSize_Z
return
[docs] def setStart_R(self, Start_R):
self.Start_R = Start_R
return
[docs] def setStart_ThetaDegrees(self, Start_ThetaDegrees):
self.Start_ThetaDegrees = Start_ThetaDegrees
return
[docs] def setStart_Z(self, Start_Z):
self.Start_Z = Start_Z
return
[docs] def computePoints(self):
#print("I'm in.")
self.clear()
for Z_idx in range(self.NumberOfSteps_Z):
Z = self.Start_Z + Z_idx * self.StepSize_Z
for R_idx in range(self.NumberOfSteps_R):
write_sequence = []
lastpoint = None
R = self.Start_R + R_idx * self.StepSize_R
for Theta_idx in range(self.NumberOfSteps_Theta):
ThetaDegrees = self.Start_ThetaDegrees + Theta_idx * self.StepSize_ThetaDegrees
ThetaRadians = radians(ThetaDegrees)
P = self.centro + array([R*cos(ThetaRadians), R*sin(ThetaRadians), Z, self.powerFunction(R, ThetaRadians, Z)])
#print(P)
#pointlist = self.suppressorFunction(lastpoint, P)
lastpoint = P
write_sequence.append(P)
self.GWL_voxels.append(write_sequence)
#self.addTube(self.centro, self.inner_radius, self.outer_radius, self.height, -1, self.PointDistance_r, self.PointDistance_theta, self.PointDistance_z, self.downwardWriting)
#def addTube(self, centro, inner_radius, outer_radius, height, power, PointDistance_r, PointDistance_theta, PointDistance_z, downwardWriting=True):
##print('=== addTube ===')
##print((numpy.linspace(inner_radius, outer_radius, float((outer_radius - inner_radius)/PointDistance_r))))
#for radius in numpy.linspace(self.inner_radius, self.outer_radius, float(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)):
#zrange = numpy.linspace(self.centro[2]+0.5*self.height, self.centro[2]-0.5*self.height, float(1+self.height/self.PointDistance_z))
#if not self.downwardWriting:
#zrange = reversed(zrange)
#for z in zrange:
##print((radius,z))
##for i_theta in numpy.linspace(0, 2*numpy.pi, (outer_radius - inner_radius)/PointDistance_r):
#self.addHorizontalCircle([self.centro[0],self.centro[1],z], radius, self.power, self.PointDistance_theta, closed_loop=self.closed_loop)
return
[docs] def getMeshData(self, position = [0,0,0]):
self.computePoints()
return(GWLobject.getMeshData(self, position))
# tube class
[docs]class Tube(GWLobject):
''' This class allows you to create "tubes", i.e. cylinders with an optional inner radius.
There are three writing methods available:
* horizontal disks
* vertical lines
* spirals (with or without additional flat disks on top and bottom)
.. todo:: Finish documenting...
.. todo:: rotated tube... (part of the parent todo: general location+rotation system for GWL objects and BFDTD objects)
'''
def __init__(self):
self.centro = [0,0,0]
self.inner_radius = 1
self.outer_radius = 2
self.height = 3
self.method = 'circles'
self.PointDistance_r = 0.150
self.PointDistance_theta = 0.150
self.PointDistance_z = 2.7*0.150
self.downwardWriting = True
self.zigzag = True
self.rotateSpirals = False
self.add_flat_ends = True
self.closed_loop = False
self.power = -1
GWLobject.__init__(self)
return
[docs] def setCentro(self, vec3):
self.centro = array(vec3)
return
[docs] def setHeight(self, height):
self.height = height
return
[docs] def setInnerRadius(self, inner_radius):
# TODO
return
[docs] def setOuterRadius(self, outer_radius):
self.outer_radius = outer_radius
return
[docs] def setAngularStepDegrees(self,x):
# TODO
return
[docs] def setNumberOfSteps_R(self,x):
# TODO
return
[docs] def setNumberOfSteps_Theta(self,x):
# TODO
return
[docs] def setNumberOfSteps_Z(self,x):
# TODO
return
[docs] def setStepSize_R(self,x):
# TODO
return
[docs] def setStepSize_ThetaDegrees(self,x):
# TODO
return
[docs] def setStepSize_Z(self,x):
# TODO
return
[docs] def setStart_R(self,x):
# TODO
return
[docs] def setStart_ThetaDegrees(self,x):
# TODO
return
[docs] def setStart_Z(self,x):
# TODO
return
#def setNlinesVertical(self):
#Zlist =
#self.NlinesVertical = float(1+self.height/self.PointDistance_z)
#return
[docs] def addTube(self):
for radius in linspace(self.inner_radius, self.outer_radius, float(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)):
zrange = linspace(self.centro[2]+0.5*self.height, self.centro[2]-0.5*self.height, ceil(1+self.height/self.PointDistance_z))
if not self.downwardWriting:
zrange = reversed(zrange)
for z in zrange:
self.addHorizontalCircle([self.centro[0],self.centro[1],z], radius, self.power, self.PointDistance_theta, closed_loop=self.closed_loop)
return
# TODO: Improve/standardize voxelsize/overlap/step system, etc
# TODO: Fix location of non-spiral tubes (they don't seem to get correctly centered on location in Blender addon)
[docs] def computePoints(self):
self.clear()
### spiral method
if self.method == 'spiral':
### number of spirals/circles
N_radius = int(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)
### Add first flat end
if self.add_flat_ends:
if self.downwardWriting:
z = self.centro[2]+0.5*self.height
else:
z = self.centro[2]-0.5*self.height
for radius in linspace(self.inner_radius, self.outer_radius, float(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)):
self.addHorizontalCircle([self.centro[0], self.centro[1], z], radius, self.power, self.PointDistance_theta, closed_loop=self.closed_loop)
### Add spirals
#for radius in numpy.linspace(self.inner_radius, self.outer_radius, float(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)):
for radius_idx in range(N_radius):
# radius of current spiral
radius = self.inner_radius + radius_idx * self.PointDistance_r
# set starting angle of current spiral
if self.rotateSpirals:
theta0 = radius_idx * 2*numpy.pi/N_radius
else:
theta0 = 0
# set theta step
step_theta = self.PointDistance_theta/radius
# total number of points for the current spiral
N_theta = int((2*numpy.pi*self.height)/(self.PointDistance_z*step_theta))
# upwards/downwards writing handling
if not self.downwardWriting:
theta_idx_range = range(N_theta)
else:
theta_idx_range = reversed(range(N_theta))
write_sequence = []
for theta_idx in theta_idx_range:
theta = theta_idx*step_theta
x = radius*numpy.cos(theta0 + theta)
y = radius*numpy.sin(theta0 + theta)
z = -0.5*self.height + self.PointDistance_z*theta/(2*numpy.pi)
write_sequence.append([x,y,z])
self.GWL_voxels.append(write_sequence)
### Add second flat end
if self.add_flat_ends:
if self.downwardWriting:
z = self.centro[2]-0.5*self.height
else:
z = self.centro[2]+0.5*self.height
for radius in linspace(self.inner_radius, self.outer_radius, float(1+(self.outer_radius - self.inner_radius)/self.PointDistance_r)):
self.addHorizontalCircle([self.centro[0], self.centro[1], z], radius, self.power, self.PointDistance_theta, closed_loop=self.closed_loop)
### vertical lines method
elif self.method == 'vertical lines':
self.addTubeWithVerticalLines(self.centro, self.inner_radius, self.outer_radius, self.height, -1, self.PointDistance_r, self.PointDistance_theta, self.downwardWriting, self.zigzag)
### horizontal disk method
else:
self.addTube()
[docs] def writeGWL(self, filename, writingOffset = [0,0,0,0]):
self.computePoints()
GWLobject.writeGWL(self, filename, writingOffset)
return
[docs] def getMeshData(self, position = [0,0,0]):
self.computePoints()
#print(self.GWL_voxels[0][0])
return(GWLobject.getMeshData(self, position))
[docs]class TubeLarge(TubePapa):
def __init__(self):
TubePapa.__init__(self)
self.centro = [0,0,0]
self.radius = 10
#self.fraction = 0.5
#self.fractionLeft = 0.5
#self.fractionRight = 0.5
self.NlinesHorizontal = 10
self.height = 5
self.NlinesVertical = 5
self.Xmin = -7.5
self.Xmax = 5
[docs] def computePoints(self):
self.clear()
#Xlist = linspace(self.radius, self.fraction*self.radius, self.NlinesHorizontal)
Xlist = linspace( max(-self.radius,self.Xmin), min(self.Xmax,self.radius), self.NlinesHorizontal)
Zlist = linspace(0.5*self.height, -0.5*self.height, self.NlinesVertical)
write_sequence = []
Xdir = 0
Ydir = 0
for Z in Zlist:
if Xdir%2 == 0:
XlistLocal = Xlist
else:
XlistLocal = reversed(Xlist)
for X in XlistLocal:
Mpos = array(self.centro) + array([X,sqrt(self.radius**2-X**2),Z])
Mneg = array(self.centro) + array([X,-sqrt(self.radius**2-X**2),Z])
if Ydir%2 == 0:
write_sequence.extend([Mpos,Mneg])
else:
write_sequence.extend([Mneg,Mpos])
self.GWL_voxels.append(write_sequence); write_sequence = []
Ydir += 1
Xdir += 1
self.GWL_voxels.append(write_sequence)
[docs]class HalfTubeLargeWithInnerAndOuter(TubePapa):
def __init__(self):
TubePapa.__init__(self)
self.centro = [0,0,0]
self.inner_radius = 5
self.outer_radius = 10
self.NlinesHorizontal = 10
self.height = 5
self.NlinesVertical = 5
#self.fraction = 0
self.Xmin = -7.5
self.Xmax = 5
[docs] def computePoints(self):
self.clear()
#Xlist = linspace(self.outer_radius, self.fraction*self.outer_radius, self.NlinesHorizontal)
Xlist = linspace( max(-self.outer_radius,self.Xmin), min(self.Xmax,self.outer_radius), self.NlinesHorizontal)
Zlist = linspace(0.5*self.height, -0.5*self.height, self.NlinesVertical)
write_sequence = []
Xdir = 0
Ydir = 0
for Z in Zlist:
if Xdir%2 == 0:
XlistLocal = Xlist
else:
XlistLocal = reversed(Xlist)
for X in XlistLocal:
if abs(X) >= self.inner_radius:
Mpos = array(self.centro) + array([X,sqrt(self.outer_radius**2-X**2),Z])
Mneg = array(self.centro) + array([X,-sqrt(self.outer_radius**2-X**2),Z])
if Ydir%2 == 0:
write_sequence.extend([Mpos,Mneg])
else:
write_sequence.extend([Mneg,Mpos])
else:
Mpos_outer = array(self.centro) + array([X,sqrt(self.outer_radius**2-X**2),Z])
Mpos_inner = array(self.centro) + array([X,sqrt(self.inner_radius**2-X**2),Z])
Mneg_inner = array(self.centro) + array([X,-sqrt(self.inner_radius**2-X**2),Z])
Mneg_outer = array(self.centro) + array([X,-sqrt(self.outer_radius**2-X**2),Z])
if Ydir%2 == 0:
write_sequence.extend([Mpos_outer,Mpos_inner])
self.GWL_voxels.append(write_sequence); write_sequence = []
write_sequence.extend([Mneg_inner,Mneg_outer])
else:
write_sequence.extend([Mneg_outer,Mneg_inner])
self.GWL_voxels.append(write_sequence); write_sequence = []
write_sequence.extend([Mpos_inner,Mpos_outer])
self.GWL_voxels.append(write_sequence); write_sequence = []
Ydir += 1
Xdir += 1
self.GWL_voxels.append(write_sequence)
[docs]class TruncatedTube(GWLobject):
def __init__(self):
GWLobject.__init__(self)
self.centro = [0,0,0]
self.inner_radius = 0.5
self.outer_radius = 1
self.height = 1
self.z_step = 0.1
self.theta_step_deg = 1
self.r_step = 1
self.truncationDistanceFromCentro = 0.7
self.startAngle_deg = 0
self.endAngle_deg = 360
self.voxel_width = 0.100
self.voxel_height = 0.200
self.closed_loop = False
return
[docs] def writeGWL(self, filename, writingOffset = [0,0,0,0]):
self.computePoints()
GWLobject.writeGWL(self, filename, writingOffset)
return
[docs] def getMeshData(self, position = [0,0,0]):
self.computePoints()
#print(self.GWL_voxels[0][0])
return(GWLobject.getMeshData(self, position))
[docs] def computePoints(self):
self.clear()
log_width = self.outer_radius - self.inner_radius
(LineNumber_R, LineDistance_R) = calculateNvoxelsAndInterVoxelDistance(Length=log_width, Voxelsize=self.voxel_width, Overlap=0)
(LineNumber_theta, LineDistance_theta) = calculateNvoxelsAndInterVoxelDistance(Length=2*numpy.pi*self.outer_radius, Voxelsize=self.voxel_width, Overlap=0)
(LineNumber_Z, LineDistance_Z) = calculateNvoxelsAndInterVoxelDistance(Length=self.height, Voxelsize=self.voxel_height, Overlap=6./7.)
#print('LineNumber_Z='+str(LineNumber_Z))
#print('LineDistance_Z='+str(LineDistance_Z))
power = -1
PointDistance_R = LineDistance_R
PointDistance_theta = LineDistance_theta
PointDistance_Z = LineDistance_Z
for index_R in range(LineNumber_R):
# calculate radius of current circle
radius = 0.5*(self.inner_radius+self.outer_radius) - 0.5*((LineNumber_R-1)*PointDistance_R) + index_R*PointDistance_R
if self.truncationDistanceFromCentro < radius:
if abs(radius) > abs(self.truncationDistanceFromCentro):
#startAngle = numpy.arccos(self.truncationDistanceFromCentro/radius)
#endAngle = 2*numpy.pi-numpy.arccos(self.truncationDistanceFromCentro/radius)
startAngle = -numpy.arccos(self.truncationDistanceFromCentro/radius)
endAngle = numpy.arccos(self.truncationDistanceFromCentro/radius)
else:
startAngle = 0
endAngle = 2*numpy.pi
for index_Z in range(LineNumber_Z):
zloc = self.centro[2]+0.5*((LineNumber_Z-1)*PointDistance_Z) - index_Z*PointDistance_Z
self.addHorizontalCircle([self.centro[0],self.centro[1],zloc], radius, power, PointDistance_theta, startAngle, endAngle, closed_loop=self.closed_loop)
[docs]def testTruncatedTube():
foo = TruncatedTube()
foo.inner_radius = 0.5
foo.outer_radius = 1.5
foo.height = 2
foo.truncationDistanceFromCentro = 1
foo.centro = [0,0,0]
v,e,f = foo.getMeshData()
print(v[0])
foo.centro = [1,0,0]
v,e,f = foo.getMeshData()
print(v[0])
foo.writeGWL(sys.argv[1])
[docs]def testTube():
foo = Tube()
foo.inner_radius = 0.5
foo.outer_radius = 0.5
foo.height = 0
foo.centro = [0,0,0]
v,e,f = foo.getMeshData()
print(v[0])
foo.centro = [1,0,0]
v,e,f = foo.getMeshData()
print(v[0])
foo.writeGWL(sys.argv[1])
[docs]def usage_example():
#obj = Tube()
#blender_utilities.addSimpleObject(obj.getMeshData, object_name='object', mesh_name='mesh', context=bpy.context)
obj = Tube()
#obj.centro = self.location # not passed, since blender already takes care of the relative mesh location
obj.inner_radius = 0.5
obj.outer_radius = 1
obj.height = 1
obj.method = 'spiral'
obj.PointDistance_r = 0.1
obj.PointDistance_theta = 0.1
obj.PointDistance_z = 0.1
obj.downwardWriting = True
obj.zigzag = True
obj.rotateSpirals = False
obj.add_flat_ends = True
obj.closed_loop = True
obj.writeGWL(sys.argv[1]+os.sep+'tmp.gwl')
print('DONE')
if __name__ == "__main__":
usage_example()