summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 36f4e3c)
raw | patch | inline | side by side (parent: 36f4e3c)
author | Alvin Penner <penner@vaxxine.com> | |
Wed, 23 Dec 2009 12:09:12 +0000 (07:09 -0500) | ||
committer | Alvin Penner <penner@vaxxine.com> | |
Wed, 23 Dec 2009 12:09:12 +0000 (07:09 -0500) |
share/extensions/Makefile.am | patch | blob | history | |
share/extensions/wireframe_sphere.inx | [new file with mode: 0644] | patch | blob |
share/extensions/wireframe_sphere.py | [new file with mode: 0644] | patch | blob |
index 14238ad31f61c838e7969f55d76edd98cb7f7747..2dbb71a543f1339c4748421042c8ce8266fee44b 100644 (file)
web-set-att.py \
web-transmit-att.py \
whirl.py \
+ wireframe_sphere.py \
wmf_output.py \
yocto_css.py
web-set-att.inx \
web-transmit-att.inx \
whirl.inx \
+ wireframe_sphere.inx \
wmf_input.inx \
wmf_output.inx \
xaml2svg.inx
diff --git a/share/extensions/wireframe_sphere.inx b/share/extensions/wireframe_sphere.inx
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
+ <_name>Wireframe Sphere</_name>
+ <id>il.wireframesphere</id>
+ <dependency type="executable" location="extensions">wireframe_sphere.py</dependency>
+ <dependency type="executable" location="extensions">inkex.py</dependency>
+ <dependency type="executable" location="extensions">simplestyle.py</dependency>
+ <dependency type="executable" location="extensions">simpletransform.py</dependency>
+ <param name="num_lat" type="int" min="0" max="1000" _gui-text="Lines of latitude">19</param>
+ <param name="num_long" type="int" min="0" max="1000" _gui-text="Lines of longitude">24</param>
+ <param name="tilt" type="float" min="-90" max="90" _gui-text="Tilt [deg]">35</param>
+ <param name="rotation" type="float" min="0" max="360" _gui-text="Rotation [deg]">4</param>
+ <param name="radius" type="float" min="1" max="1000" _gui-text="Radius [px]">100.0</param>
+ <param name="hide_back" type="boolean" _gui-text="Hide lines behind the sphere">false</param>
+ <effect>
+ <object-type>all</object-type>
+ <effects-menu>
+ <submenu _name="Render"/>
+ </effects-menu>
+ </effect>
+ <script>
+ <command reldir="extensions" interpreter="python">wireframe_sphere.py</command>
+ </script>
+</inkscape-extension>
diff --git a/share/extensions/wireframe_sphere.py b/share/extensions/wireframe_sphere.py
--- /dev/null
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+# -*- coding: UTF-8 -*-
+'''
+Copyright (C) 2009 John Beard john.j.beard@gmail.com
+
+######DESCRIPTION######
+
+This extension renders a wireframe sphere constructed from lines of latitude
+and lines of longitude.
+
+The number of lines of latitude and longitude is independently variable. Lines
+of latitude and longtude are in separate subgroups. The whole figure is also in
+its own group.
+
+The whole sphere can be tilted towards or away from the veiwer by a given
+number of degrees. If the whole sphere is then rotated normally in Inkscape,
+any position can be acheived.
+
+There is an option to hide the lines at the back of the sphere, as if the
+sphere were opaque.
+ #FIXME: Lines of latitude only have an approximation of the function needed
+ to hide the back portion. If you can derive the proper equation,
+ please add it in.
+ Line of longitude have the exact method already.
+ Workaround: Use the Inkscape ellipse tool to edit the start and end
+ points of the lines of latitude to end at the horizon circle.
+
+
+#TODO: Add support for odd numbers of lines of longitude. This means breaking
+ the line at the poles, and having two half ellipses for each line.
+ The angles at which the ellipse arcs pass the poles are not constant and
+ need to be derived before this can be implemented.
+#TODO: Add support for prolate and oblate spheroids
+
+######LICENCE#######
+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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+######VERSION HISTORY#####
+ Ver. Date Notes
+
+ 0.10 2009-10-25 First version. Basic spheres supported.
+ Hidden lines of latitude still not properly calculated.
+ Prolate and oblate spheroids not considered.
+'''
+
+import inkex, simplestyle
+
+import gettext
+_ = gettext.gettext
+
+from math import *
+
+#SVG OUTPUT FUNCTIONS ================================================
+def draw_SVG_ellipse((rx, ry), (cx, cy), parent, start_end=(0,2*pi),transform='' ):
+
+ style = { 'stroke' : '#000000',
+ 'width' : '1',
+ 'fill' : 'none' }
+ circ_attribs = {'style':simplestyle.formatStyle(style),
+ inkex.addNS('cx','sodipodi') :str(cx),
+ inkex.addNS('cy','sodipodi') :str(cy),
+ inkex.addNS('rx','sodipodi') :str(rx),
+ inkex.addNS('ry','sodipodi') :str(ry),
+ inkex.addNS('start','sodipodi') :str(start_end[0]),
+ inkex.addNS('end','sodipodi') :str(start_end[1]),
+ inkex.addNS('open','sodipodi') :'true', #all ellipse sectors we will draw are open
+ inkex.addNS('type','sodipodi') :'arc',
+ 'transform' :transform
+
+ }
+ circ = inkex.etree.SubElement(parent, inkex.addNS('path','svg'), circ_attribs )
+
+class Wireframe_Sphere(inkex.Effect):
+ def __init__(self):
+ inkex.Effect.__init__(self)
+
+ #PARSE OPTIONS
+ self.OptionParser.add_option("--num_lat",
+ action="store", type="int",
+ dest="NUM_LAT", default=19)
+ self.OptionParser.add_option("--num_long",
+ action="store", type="int",
+ dest="NUM_LONG", default=24)
+ self.OptionParser.add_option("--radius",
+ action="store", type="float",
+ dest="RADIUS", default=100.0)
+ self.OptionParser.add_option("--tilt",
+ action="store", type="float",
+ dest="TILT", default=35.0)
+ self.OptionParser.add_option("--rotation",
+ action="store", type="float",
+ dest="ROT_OFFSET", default=4)
+ self.OptionParser.add_option("--hide_back",
+ action="store", type="inkbool",
+ dest="HIDE_BACK", default=False)
+
+ def effect(self):
+
+ so = self.options
+
+ #PARAMETER PROCESSING
+
+ if so.NUM_LONG % 2 != 0: #lines of longitude are odd : abort
+ inkex.errormsg(_('Please enter an even number of lines of longitude.'))
+ else:
+ if so.TILT < 0: # if the tilt is backwards
+ flip = ' scale(1, -1)' # apply a vertical flip to the whole sphere
+ else:
+ flip = '' #no flip
+
+ so.TILT = abs(so.TILT)*(pi/180) #Convert to radians
+ so.ROT_OFFSET = so.ROT_OFFSET*(pi/180) #Convert to radians
+
+ 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
+
+ #INKSCAPE GROUP TO CONTAIN EVERYTHING
+
+ centre = self.view_center #Put in in the centre of the current view
+ grp_transform = 'translate' + str( centre ) + flip
+ grp_name = 'WireframeSphere'
+ grp_attribs = {inkex.addNS('label','inkscape'):grp_name,
+ 'transform':grp_transform }
+ grp = inkex.etree.SubElement(self.current_layer, 'g', grp_attribs)#the group to put everything in
+
+ #LINES OF LONGITUDE
+
+ if so.NUM_LONG > 0: #only process longitudes if we actually want some
+
+ #GROUP FOR THE LINES OF LONGITUDE
+ grp_name = 'Lines of Longitude'
+ grp_attribs = {inkex.addNS('label','inkscape'):grp_name}
+ grp_long = inkex.etree.SubElement(grp, 'g', grp_attribs)
+
+ delta_long = 360.0/so.NUM_LONG #angle between neighbouring lines of longitude in degrees
+
+ for i in range(0,so.NUM_LONG/2):
+ long_angle = so.ROT_OFFSET + (i*delta_long)*(pi/180.0); #The longitude of this particular line in radians
+ width = so.RADIUS * cos(long_angle)
+ height = so.RADIUS * sin(long_angle) * sin(so.TILT) #the rise is scaled by the sine of the tilt
+ length = sqrt(width*width+height*height) #by pythagorean theorem
+ inverse = sin(acos(length/so.RADIUS))
+
+ minorRad = so.RADIUS * inverse
+ minorRad=minorRad + EPSILON
+
+ #calculate the rotation of the ellipse to get it to pass through the pole (in degrees)
+ rotation = atan(height/width)*(180.0/pi)
+ transform = "rotate("+str(rotation)+')' #generate the transform string
+ #the rotation will be applied about the group centre (the centre of the sphere)
+
+ # remove the hidden side of the ellipses if required
+ # this is always exactly half the ellipse, but we need to find out which half
+ start_end = (0, 2*pi) #Default start and end angles -> full ellipse
+ if so.HIDE_BACK:
+ if long_angle <= pi/2: #cut out the half ellispse that is hidden
+ start_end = (pi/2, 3*pi/2)
+ else:
+ start_end = (3*pi/2, pi/2)
+
+ #finally, draw the line of longitude
+ #the centre is always at the centre of the sphere
+ draw_SVG_ellipse( ( minorRad, so.RADIUS ), (0,0), grp_long , start_end,transform)
+
+ # LINES OF LATITUDE
+ if so.NUM_LAT > 0:
+
+ #GROUP FOR THE LINES OF LATITUDE
+ grp_name = 'Lines of Latitude'
+ grp_attribs = {inkex.addNS('label','inkscape'):grp_name}
+ grp_lat = inkex.etree.SubElement(grp, 'g', grp_attribs)
+
+
+ so.NUM_LAT = so.NUM_LAT + 1 #Account for the fact that we loop over N-1 elements
+ delta_lat = 180.0/so.NUM_LAT #Angle between the line of latitude (subtended at the centre)
+
+ for i in range(1,so.NUM_LAT):
+ lat_angle=((delta_lat*i)*(pi/180)) #The angle of this line of latitude (from a pole)
+
+ majorRad=so.RADIUS*sin(lat_angle) #The width of the LoLat (no change due to projection)
+ minorRad=so.RADIUS*sin(lat_angle) * sin(so.TILT) #The projected height of the line of latitude
+ minorRad=minorRad + EPSILON
+
+ cy=so.RADIUS*cos(lat_angle) * cos(so.TILT) #The projected y position of the LoLat
+ cx=0 #The x position is just the center of the sphere
+
+ if so.HIDE_BACK:
+ if lat_angle > so.TILT: #this LoLat is partially or fully visible
+ if lat_angle > pi-so.TILT: #this LoLat is fully visible
+ draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat)
+ else: #this LoLat is partially visible
+
+ proportion = -(acos( (lat_angle - pi/2)/(pi/2 - so.TILT)) )/pi + 1 #this is a dirty hacky approximation
+ #FIXME: if you can work out the right way to do this, please do it
+ start_end = ( pi/2 - proportion*pi, pi/2 + proportion*pi ) #make the start and end angles (mirror image around pi/2)
+ draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat, start_end)
+
+ else: #just draw the full lines of latitude
+ draw_SVG_ellipse((majorRad, minorRad), (cx,cy), grp_lat)
+
+
+ #THE HORIZON CIRCLE
+ draw_SVG_ellipse((so.RADIUS, so.RADIUS), (0,0), grp) #circle, centred on the sphere centre
+
+if __name__ == '__main__':
+ e = Wireframe_Sphere()
+ e.affect()
+
+# vim: expandtab shiftwidth=4 tabstop=8 softtabstop=4 encoding=utf-8 textwidth=99