home *** CD-ROM | disk | FTP | other *** search
/ Chip 2011 November / CHIP_2011_11.iso / Programy / Narzedzia / Inkscape / Inkscape-0.48.2-1-win32.exe / share / extensions / wireframe_sphere.py < prev    next >
Text File  |  2011-07-08  |  11KB  |  222 lines

  1. #!/usr/bin/env python 
  2. # -*- coding: UTF-8 -*-
  3. '''
  4. Copyright (C) 2009 John Beard john.j.beard@gmail.com
  5.  
  6. ######DESCRIPTION######
  7.  
  8. This extension renders a wireframe sphere constructed from lines of latitude
  9. and lines of longitude.
  10.  
  11. The number of lines of latitude and longitude is independently variable. Lines 
  12. of latitude and longtude are in separate subgroups. The whole figure is also in
  13. its own group.
  14.  
  15. The whole sphere can be tilted towards or away from the veiwer by a given 
  16. number of degrees. If the whole sphere is then rotated normally in Inkscape,
  17. any position can be acheived.
  18.  
  19. There is an option to hide the lines at the back of the sphere, as if the 
  20. sphere were opaque.
  21.     #FIXME: Lines of latitude only have an approximation of the function needed
  22.             to hide the back portion. If you can derive the proper equation,
  23.             please add it in.
  24.             Line of longitude have the exact method already.
  25.             Workaround: Use the Inkscape ellipse tool to edit the start and end
  26.             points of the lines of latitude to end at the horizon circle.
  27.             
  28.            
  29. #TODO:  Add support for odd numbers of lines of longitude. This means breaking
  30.         the line at the poles, and having two half ellipses for each line.
  31.         The angles at which the ellipse arcs pass the poles are not constant and
  32.         need to be derived before this can be implemented.
  33. #TODO:  Add support for prolate and oblate spheroids
  34.  
  35. ######LICENCE#######
  36. This program is free software; you can redistribute it and/or modify
  37. it under the terms of the GNU General Public License as published by
  38. the Free Software Foundation; either version 2 of the License, or
  39. (at your option) any later version.
  40.  
  41. This program is distributed in the hope that it will be useful,
  42. but WITHOUT ANY WARRANTY; without even the implied warranty of
  43. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  44. GNU General Public License for more details.
  45.  
  46. You should have received a copy of the GNU General Public License
  47. along with this program; if not, write to the Free Software
  48. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  49.  
  50. ######VERSION HISTORY#####
  51.     Ver.       Date                       Notes
  52.     
  53.     0.10    2009-10-25  First version. Basic spheres supported.
  54.                         Hidden lines of latitude still not properly calculated.
  55.                         Prolate and oblate spheroids not considered.
  56. '''
  57.  
  58. import inkex, simplestyle
  59.  
  60. import gettext
  61. _ = gettext.gettext
  62.  
  63. from math import *
  64.  
  65. #SVG OUTPUT FUNCTIONS ================================================
  66. def draw_SVG_ellipse((rx, ry), (cx, cy), parent, start_end=(0,2*pi),transform='' ):
  67.  
  68.     style = {   'stroke'        : '#000000',
  69.                 'stroke-width'  : '1',
  70.                 'fill'          : 'none'            }
  71.     circ_attribs = {'style':simplestyle.formatStyle(style),
  72.         inkex.addNS('cx','sodipodi')        :str(cx),
  73.         inkex.addNS('cy','sodipodi')        :str(cy),
  74.         inkex.addNS('rx','sodipodi')        :str(rx),
  75.         inkex.addNS('ry','sodipodi')        :str(ry),
  76.         inkex.addNS('start','sodipodi')     :str(start_end[0]),
  77.         inkex.addNS('end','sodipodi')       :str(start_end[1]),
  78.         inkex.addNS('open','sodipodi')      :'true',    #all ellipse sectors we will draw are open
  79.         inkex.addNS('type','sodipodi')      :'arc',
  80.         'transform'                         :transform
  81.         
  82.             }
  83.     circ = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), circ_attribs )
  84.     
  85. class Wireframe_Sphere(inkex.Effect):
  86.     def __init__(self):
  87.         inkex.Effect.__init__(self)
  88.         
  89.         #PARSE OPTIONS
  90.         self.OptionParser.add_option("--num_lat",
  91.             action="store", type="int",
  92.             dest="NUM_LAT", default=19)
  93.         self.OptionParser.add_option("--num_long",
  94.             action="store", type="int",
  95.             dest="NUM_LONG", default=24)
  96.         self.OptionParser.add_option("--radius",
  97.             action="store", type="float", 
  98.             dest="RADIUS", default=100.0)
  99.         self.OptionParser.add_option("--tilt",
  100.             action="store", type="float",
  101.             dest="TILT", default=35.0)
  102.         self.OptionParser.add_option("--rotation",
  103.             action="store", type="float",
  104.             dest="ROT_OFFSET", default=4)
  105.         self.OptionParser.add_option("--hide_back",
  106.             action="store", type="inkbool", 
  107.             dest="HIDE_BACK", default=False)
  108.             
  109.     def effect(self):
  110.         
  111.         so = self.options
  112.         
  113.         #PARAMETER PROCESSING
  114.         
  115.         if so.NUM_LONG % 2 != 0: #lines of longitude are odd : abort
  116.             inkex.errormsg(_('Please enter an even number of lines of longitude.'))
  117.         else:
  118.             if so.TILT < 0:            # if the tilt is backwards
  119.                 flip = ' scale(1, -1)' # apply a vertical flip to the whole sphere
  120.             else:
  121.                 flip = '' #no flip
  122.  
  123.             so.TILT       =  abs(so.TILT)*(pi/180)  #Convert to radians
  124.             so.ROT_OFFSET = so.ROT_OFFSET*(pi/180)  #Convert to radians
  125.             
  126.             EPSILON = 0.001 #add a tiny value to the ellipse radii, so that if we get a zero radius, the ellipse still shows up as a line
  127.  
  128.             #INKSCAPE GROUP TO CONTAIN EVERYTHING
  129.             
  130.             centre = self.view_center   #Put in in the centre of the current view
  131.             grp_transform = 'translate' + str( centre ) + flip
  132.             grp_name = 'WireframeSphere'
  133.             grp_attribs = {inkex.addNS('label','inkscape'):grp_name,
  134.                            'transform':grp_transform }
  135.             grp = inkex.etree.SubElement(self.current_layer, 'g', grp_attribs)#the group to put everything in
  136.             
  137.             #LINES OF LONGITUDE
  138.             
  139.             if so.NUM_LONG > 0:      #only process longitudes if we actually want some
  140.                 
  141.                 #GROUP FOR THE LINES OF LONGITUDE
  142.                 grp_name = 'Lines of Longitude'
  143.                 grp_attribs = {inkex.addNS('label','inkscape'):grp_name}
  144.                 grp_long = inkex.etree.SubElement(grp, 'g', grp_attribs)
  145.                 
  146.                 delta_long = 360.0/so.NUM_LONG      #angle between neighbouring lines of longitude in degrees
  147.                 
  148.                 for i in range(0,so.NUM_LONG/2):
  149.                     long_angle = so.ROT_OFFSET + (i*delta_long)*(pi/180.0); #The longitude of this particular line in radians
  150.                     if long_angle > pi:
  151.                         long_angle -= 2*pi
  152.                     width      = so.RADIUS * cos(long_angle)
  153.                     height     = so.RADIUS * sin(long_angle) * sin(so.TILT)       #the rise is scaled by the sine of the tilt
  154.                     # length     = sqrt(width*width+height*height)  #by pythagorean theorem
  155.                     # inverse    = sin(acos(length/so.RADIUS))
  156.                     inverse    = abs(sin(long_angle)) * cos(so.TILT)
  157.                     
  158.                     minorRad   = so.RADIUS * inverse
  159.                     minorRad=minorRad + EPSILON
  160.                     
  161.                     #calculate the rotation of the ellipse to get it to pass through the pole (in degrees)
  162.                     rotation = atan(height/width)*(180.0/pi)
  163.                     transform = "rotate("+str(rotation)+')' #generate the transform string
  164.                     #the rotation will be applied about the group centre (the centre of the sphere)
  165.                     
  166.                     # remove the hidden side of the ellipses if required
  167.                     # this is always exactly half the ellipse, but we need to find out which half
  168.                     start_end = (0, 2*pi)   #Default start and end angles -> full ellipse
  169.                     if so.HIDE_BACK:
  170.                         if long_angle <= pi/2:           #cut out the half ellispse that is hidden
  171.                             start_end = (pi/2, 3*pi/2)
  172.                         else:
  173.                             start_end = (3*pi/2, pi/2)
  174.                     
  175.                     #finally, draw the line of longitude
  176.                     #the centre is always at the centre of the sphere
  177.                     draw_SVG_ellipse( ( minorRad, so.RADIUS ), (0,0), grp_long , start_end,transform)
  178.                 
  179.             # LINES OF LATITUDE
  180.             if so.NUM_LAT > 0:
  181.             
  182.                 #GROUP FOR THE LINES OF LATITUDE
  183.                 grp_name = 'Lines of Latitude'
  184.                 grp_attribs = {inkex.addNS('label','inkscape'):grp_name}
  185.                 grp_lat = inkex.etree.SubElement(grp, 'g', grp_attribs)
  186.                 
  187.                 
  188.                 so.NUM_LAT = so.NUM_LAT + 1     #Account for the fact that we loop over N-1 elements
  189.                 delta_lat = 180.0/so.NUM_LAT    #Angle between the line of latitude (subtended at the centre)
  190.                 
  191.                 for i in range(1,so.NUM_LAT):
  192.                     lat_angle=((delta_lat*i)*(pi/180))            #The angle of this line of latitude (from a pole)
  193.                     
  194.                     majorRad=so.RADIUS*sin(lat_angle)                 #The width of the LoLat (no change due to projection)
  195.                     minorRad=so.RADIUS*sin(lat_angle) * sin(so.TILT)     #The projected height of the line of latitude
  196.                     minorRad=minorRad + EPSILON
  197.                     
  198.                     cy=so.RADIUS*cos(lat_angle) * cos(so.TILT) #The projected y position of the LoLat
  199.                     cx=0                                    #The x position is just the center of the sphere
  200.                     
  201.                     if so.HIDE_BACK:
  202.                         if lat_angle > so.TILT:                     #this LoLat is partially or fully visible
  203.                             if lat_angle > pi-so.TILT:               #this LoLat is fully visible
  204.                                 draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat)
  205.                             else: #this LoLat is partially visible
  206.                                 proportion = -(acos( tan(lat_angle - pi/2)/tan(pi/2 - so.TILT)) )/pi + 1
  207.                                 start_end = ( pi/2 - proportion*pi, pi/2 + proportion*pi ) #make the start and end angles (mirror image around pi/2)
  208.                                 draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat, start_end)
  209.                             
  210.                     else: #just draw the full lines of latitude
  211.                         draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat)
  212.             
  213.         
  214.             #THE HORIZON CIRCLE
  215.             draw_SVG_ellipse((so.RADIUS, so.RADIUS), (0,0), grp) #circle, centred on the sphere centre
  216.             
  217. if __name__ == '__main__':
  218.     e = Wireframe_Sphere()
  219.     e.affect()
  220.  
  221. # vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99
  222.