diff --git a/modules/gfx/pymod/CMakeLists.txt b/modules/gfx/pymod/CMakeLists.txt
index f77be27fc82cce5dd22cba2dcc8cc4d520c55688..0fcc9a8ed304044934f7c7e2b67a51ff873c3f5c 100644
--- a/modules/gfx/pymod/CMakeLists.txt
+++ b/modules/gfx/pymod/CMakeLists.txt
@@ -7,6 +7,7 @@ set(OST_GFX_PYMOD_SOURCES
   export_entity.cc
   export_surface.cc
   export_primitives.cc
+  export_primlist.cc
   export_scene_observer.cc
   export_render_options.cc
   export_color_ops.cc
diff --git a/modules/gfx/pymod/__init__.py b/modules/gfx/pymod/__init__.py
index e9276ead1f891aaf801571c8084756bb4a818ade..8fbbedeaff78baf5424bdeebe80bba9db094f3b2 100644
--- a/modules/gfx/pymod/__init__.py
+++ b/modules/gfx/pymod/__init__.py
@@ -59,11 +59,11 @@ def Stereo(mode,flip=None,alg=None):
   :type param: int
   """
   if(flip):
-    _gfx.Scene().SetStereoFlip(flip)
+    Scene().SetStereoFlip(flip)
   if(alg):
-    _gfx.Scene().SetStereoAlg(alg)
+    Scene().SetStereoAlg(alg)
 
-  _gfx.Scene().SetStereoMode(mode)
+  Scene().SetStereoMode(mode)
 
 def FitToScreen(gfx_ent, width=None, height=None, margin=0.05):
   """
@@ -184,3 +184,59 @@ def _Match(scene, pattern="*"):
   return GfxNodeListProxy(_Recurse("", Scene().root_node, pattern))
 
 SceneSingleton.Match=_Match
+
+def _to_vec3(p):
+  import ost.geom
+  if isinstance(p,ost.geom.Vec3):
+    return p
+  else:
+    try:
+      return ost.geom.Vec3(p[0],p[1],p[2])
+    except:
+      raise TypeError("expected either a sequence or a geom.Vec3 object")
+  
+
+def _primlist_add_point(self,pos,col=None):
+  pos=_to_vec3(pos)
+  if not col:
+    col=WHITE
+  self._add_point(pos,col)
+  
+def _primlist_add_line(self,pos1,pos2,col=None,col1=None,col2=None):
+  pos1=_to_vec3(pos1)
+  pos2=_to_vec3(pos2)
+  if not col:
+    col=WHITE
+  if not col1:
+    col1=col
+  if not col2:
+    col2=col
+  self._add_line(pos1,pos2,col1,col2)
+
+def _primlist_add_sphere(self,cen,rad=1.0,col=None):
+  pos=_to_vec3(cen)
+  if not col:
+    col=WHITE
+  self._add_sphere(pos,rad,col)
+  
+def _primlist_add_cyl(self,pos1,pos2,rad=None,rad1=None,rad2=None,col=None,col1=None,col2=None):
+  pos1=_to_vec3(pos1)
+  pos2=_to_vec3(pos2)
+  if rad is None:
+    rad=1.0
+  if rad1 is None:
+    rad1=rad
+  if rad2 is None:
+    rad2=rad
+  if not col:
+    col=WHITE
+  if not col1:
+    col1=col
+  if not col2:
+    col2=col
+  self._add_cyl(pos1,pos2,rad1,rad2,col1,col2)
+
+PrimList.AddPoint=_primlist_add_point
+PrimList.AddLine=_primlist_add_line
+PrimList.AddSphere=_primlist_add_sphere
+PrimList.AddCyl=_primlist_add_cyl
diff --git a/modules/gfx/pymod/export_primlist.cc b/modules/gfx/pymod/export_primlist.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1456d157e595fc5cfe2467ecbfe048e099a8282e
--- /dev/null
+++ b/modules/gfx/pymod/export_primlist.cc
@@ -0,0 +1,39 @@
+//------------------------------------------------------------------------------
+// This file is part of the OpenStructure project <www.openstructure.org>
+//
+// Copyright (C) 2008-2011 by the OpenStructure authors
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License as published by the Free
+// Software Foundation; either version 3.0 of the License, or (at your option)
+// any later version.
+// This library 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 Lesser General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+//------------------------------------------------------------------------------
+#include <boost/python.hpp>
+using namespace boost::python;
+
+#include <ost/gfx/prim_list.hh>
+using namespace ost;
+using namespace ost::gfx;
+
+void export_primlist()
+{
+  class_<PrimList, bases<GfxObj>, PrimListP, boost::noncopyable>("PrimList", init<const String& >())
+    .def("Clear",&PrimList::Clear)
+    .def("_add_line",&PrimList::AddLine)
+    .def("_add_point",&PrimList::AddPoint)
+    .def("_add_sphere",&PrimList::AddSphere)
+    .def("_add_cyl",&PrimList::AddCyl)
+    .def("SetColor",&PrimList::SetColor)
+    .def("SetDiameter",&PrimList::SetDiameter)
+    .def("SetRadius",&PrimList::SetRadius)
+  ;
+
+}
diff --git a/modules/gfx/pymod/wrap_gfx.cc b/modules/gfx/pymod/wrap_gfx.cc
index c6b37a25baee7e27adbd42eeb54df4024e818785..4be1d63f23896a87f29549a6b8a287c0d0f6463d 100644
--- a/modules/gfx/pymod/wrap_gfx.cc
+++ b/modules/gfx/pymod/wrap_gfx.cc
@@ -34,6 +34,7 @@ extern void export_GfxNode();
 extern void export_GfxObj();
 extern void export_Entity();
 extern void export_Surface();
+extern void export_primlist();
 extern void export_primitives();
 #if OST_IMG_ENABLED
   extern void export_Map();
@@ -134,17 +135,8 @@ BOOST_PYTHON_MODULE(_ost_gfx)
   class_<InputEvent>("InputEvent", init<InputDevice, InputCommand, float>())
    .def(init<InputDevice,InputCommand,int,int,float>())
   ;
-  class_<PrimList, bases<GfxObj>, PrimListP, boost::noncopyable>("PrimList", init<const String& >())
-    .def("Clear",&PrimList::Clear)
-    .def("AddLine",&PrimList::AddLine)
-    .def("AddPoint",&PrimList::AddPoint)
-    .def("SetColor",&PrimList::SetColor)
-    .def("SetDiameter",&PrimList::SetDiameter)
-    .def("SetRadius",&PrimList::SetRadius)
-  ;
 
   class_<GfxTestObj, bases<GfxObj>, boost::noncopyable>("GfxTestObj", init<>());
-
   
   class_<Color>("Color",init<>())
     .def(init<float, float, float, optional<float> >())
@@ -189,6 +181,7 @@ BOOST_PYTHON_MODULE(_ost_gfx)
 #endif
 
   export_primitives();
+  export_primlist();
 }
 
 
diff --git a/modules/gfx/src/gfx_prim.hh b/modules/gfx/src/gfx_prim.hh
index c88a92368de0b9bc15ae5319e0e535e9569a96f7..d3b97cec8bb5bf6c7b9c249d0ddaf23507151305 100644
--- a/modules/gfx/src/gfx_prim.hh
+++ b/modules/gfx/src/gfx_prim.hh
@@ -48,19 +48,46 @@ typedef std::vector<SpherePrim> SpherePrimList;
 
 struct CylinderPrim {
   CylinderPrim():
-    start(),end(),radius(1.0),color1(),color2(),length(1.0),rotmat(),rotmat_t()
+    start(), end(),
+    radius1(1.0), radius2(1.0),
+    color1(), color2(),
+    length(1.0), rotmat(), rotmat_t()
   {
     calc_rotmat();
   }
 
   CylinderPrim(const geom::Vec3& st, const geom::Vec3& en, float rad, const Color& col):
-    start(st),end(en),radius(rad),color1(col),color2(col),length(geom::Length(end-start)),rotmat(),rotmat_t() 
+    start(st), end(en),
+    radius1(rad), radius2(rad),
+    color1(col), color2(col),
+    length(geom::Length(end-start)), rotmat(), rotmat_t() 
   {
     calc_rotmat();
   }
 
   CylinderPrim(const geom::Vec3& st, const geom::Vec3& en, float rad, const Color& col1, const Color& col2):
-    start(st),end(en),radius(rad),color1(col1),color2(col2),length(geom::Length(end-start)),rotmat(),rotmat_t() 
+    start(st), end(en),
+    radius1(rad), radius2(rad),
+    color1(col1), color2(col2),
+    length(geom::Length(end-start)), rotmat(), rotmat_t() 
+  {
+    calc_rotmat();
+  }
+
+  CylinderPrim(const geom::Vec3& st, const geom::Vec3& en, float rad1, float rad2, const Color& col):
+    start(st), end(en),
+    radius1(rad1), radius2(rad2),
+    color1(col), color2(col),
+    length(geom::Length(end-start)), rotmat(), rotmat_t() 
+  {
+    calc_rotmat();
+  }
+
+  CylinderPrim(const geom::Vec3& st, const geom::Vec3& en, float rad1, float rad2, const Color& col1, const Color& col2):
+    start(st), end(en),
+    radius1(rad1), radius2(rad2),
+    color1(col1), color2(col2),
+    length(geom::Length(end-start)), rotmat(), rotmat_t() 
   {
     calc_rotmat();
   }
@@ -68,7 +95,7 @@ struct CylinderPrim {
   void calc_rotmat();
 
   geom::Vec3 start,end;
-  float radius;
+  float radius1,radius2;
   Color color1, color2;
   float length;
   geom::Mat3 rotmat;
@@ -77,7 +104,6 @@ struct CylinderPrim {
 
 typedef std::vector<CylinderPrim> CylinderPrimList;
 
-
 struct TextPrim {
   TextPrim(): str(""), position(),color(),points(1.0) {}
   TextPrim(const String& s, const geom::Vec3& p, const Color& c, float ps):
diff --git a/modules/gfx/src/prim_list.cc b/modules/gfx/src/prim_list.cc
index 5486ccaed82dcecbce81d9d3b56fcc4a00abe314..95ed64cfb640e5390e3e2871dff1af381797a4bb 100644
--- a/modules/gfx/src/prim_list.cc
+++ b/modules/gfx/src/prim_list.cc
@@ -33,19 +33,36 @@ PrimList::PrimList(const String& name):
   GfxObj(name),
   points_(),
   lines_(),
-  radius_(0.5),
+  spheres_(),
+  cyls_(),
   sphere_detail_(4),
-  arc_detail_(4)
+  arc_detail_(4),
+  simple_va_()
 {}
 
 void PrimList::Clear()
 {
   points_.clear();
   lines_.clear();
+  spheres_.clear();
+  cyls_.clear();
   Scene::Instance().RequestRedraw();
   this->FlagRebuild();
 }
 
+
+geom::AlignedCuboid PrimList::GetBoundingBox() const
+{
+  geom::Vec3 minc(std::numeric_limits<float>::max(),
+                  std::numeric_limits<float>::max(),
+                  std::numeric_limits<float>::max());
+  geom::Vec3 maxc(-std::numeric_limits<float>::max(),
+                  -std::numeric_limits<float>::max(),
+                  -std::numeric_limits<float>::max());
+  ProcessLimits(minc,maxc,mol::Transform());
+  return geom::AlignedCuboid(minc,maxc);
+}
+
 void PrimList::ProcessLimits(geom::Vec3& minc, geom::Vec3& maxc, 
                              const mol::Transform& tf) const
 {
@@ -62,6 +79,19 @@ void PrimList::ProcessLimits(geom::Vec3& minc, geom::Vec3& maxc,
     minc=geom::Min(minc,tpos);
     maxc=geom::Max(maxc,tpos);
   }
+  for(PointEntryList::const_iterator it=spheres_.begin();it!=spheres_.end();++it) {
+    geom::Vec3 tpos = tf.Apply(it->pos);
+    minc=geom::Min(minc,tpos);
+    maxc=geom::Max(maxc,tpos);
+  }
+  for(LineEntryList::const_iterator it=cyls_.begin();it!=cyls_.end();++it) {
+    geom::Vec3 tpos = tf.Apply(it->pos1);
+    minc=geom::Min(minc,tpos);
+    maxc=geom::Max(maxc,tpos);
+    tpos = tf.Apply(it->pos2);
+    minc=geom::Min(minc,tpos);
+    maxc=geom::Max(maxc,tpos);
+  }
   minc-=1.0;
   maxc+=1.0;
 }
@@ -69,71 +99,45 @@ void PrimList::ProcessLimits(geom::Vec3& minc, geom::Vec3& maxc,
 geom::Vec3 PrimList::GetCenter() const
 {
   geom::Vec3 cen;
+  size_t sum=0;
   for(PointEntryList::const_iterator it=points_.begin();it!=points_.end();++it) {
     cen+=it->pos;
   }
+  sum+=points_.size();
   for(LineEntryList::const_iterator it=lines_.begin();it!=lines_.end();++it) {
     cen+=0.5*(it->pos1+it->pos2);
   }
-  if(!lines_.empty() || !points_.empty()) {
-    cen/=static_cast<float>(points_.size()+lines_.size());
+  sum+=lines_.size();
+  for(PointEntryList::const_iterator it=spheres_.begin();it!=spheres_.end();++it) {
+    cen+=it->pos;
+  }
+  sum+=spheres_.size();
+  for(LineEntryList::const_iterator it=cyls_.begin();it!=cyls_.end();++it) {
+    cen+=0.5*(it->pos1+it->pos2);
+  }
+  sum+=cyls_.size();
+  if(sum>0) {
+    cen/=static_cast<float>(sum);
   }
   return cen;
 }
 
 void PrimList::OnRenderModeChange()
 {
-  if(GetRenderMode()==RenderMode::CUSTOM) {
-    render_custom();
-  } else {
-    render_simple();
-  }
-  // this does not work
-  //GfxObj::OnRenderModeChange();
+  // noop
 }
 
 void PrimList::CustomPreRenderGL(bool flag)
 {
-  if(flag) {
-    if(GetRenderMode()==RenderMode::CUSTOM) {
-      render_custom();
-    } else {
-      render_simple();
-    }
-  }
-}
-
-namespace {
-
-struct AALineEntry {
-  float p0[3],p1[3];
-  float edge0[3],edge1[3],edge2[3],edge3[3];
-  float color[4];
-  float z;
-};
-
-geom::Vec3 make_edge(const geom::Vec2& c1, const geom::Vec2& c0, float s)
-{
-  geom::Vec3 nrvo(c1[1]-c0[1],c0[0]-c1[0],c1[0]*c0[1]-c0[0]*c1[1]);
-  nrvo*=1.0/(s*Length(c1-c0));
-  return nrvo;
-}
-
-struct AALineEntryLess
-{
-  bool operator()(const AALineEntry& e1, const AALineEntry& e2)
-  {
-    // provides back-to-front sorting
-    return e1.z<e2.z;
-  }
-};
-
+  prep_va();
+  prep_simple_va();
 }
 
 void PrimList::CustomRenderGL(RenderPass pass)
 {
   if(pass==STANDARD_RENDER_PASS || pass==TRANSPARENT_RENDER_PASS) {
     va_.RenderGL();
+    simple_va_.RenderGL();
   }
 }
 
@@ -143,44 +147,62 @@ void PrimList::CustomRenderPov(PovState& pov)
   pov.write_merge_or_union(GetName());
 
   for(PointEntryList::const_iterator it=points_.begin();it!=points_.end();++it) {
-    pov.write_sphere(it->pos,radius_,it->color,GetName());
+    pov.write_sphere(it->pos,0.1,it->col,GetName());
   }
   for(LineEntryList::const_iterator it=lines_.begin();it!=lines_.end();++it) {
-    pov.write_sphere(it->pos1,radius_,it->color,GetName());
-    pov.write_sphere(it->pos2,radius_,it->color,GetName());
-    pov.write_cyl(it->pos1,it->pos2,radius_,it->color,GetName(),true);
+    pov.write_sphere(it->pos1,0.1,it->col1,GetName());
+    pov.write_sphere(it->pos2,0.1,it->col2,GetName());
+    pov.write_cyl(it->pos1,it->pos2,0.1,it->col1,GetName(),true);
+  }
+  for(PointEntryList::const_iterator it=spheres_.begin();it!=spheres_.end();++it) {
+    pov.write_sphere(it->pos,it->rad,it->col,GetName());
+  }
+  for(LineEntryList::const_iterator it=cyls_.begin();it!=cyls_.end();++it) {
+    pov.write_sphere(it->pos1,it->rad1,it->col1,GetName());
+    pov.write_sphere(it->pos2,it->rad2,it->col2,GetName());
+    pov.write_cyl(it->pos1,it->pos2,it->rad1,it->col1,GetName(),true);
   }
   pov.inc() << " }\n";
 }
 
-void PrimList::AddPoint(geom::Vec3& p, const Color& col)
+void PrimList::AddPoint(const geom::Vec3& p, const Color& col)
 {
-  points_.push_back(PointEntry(p,col));
+  points_.push_back(PointEntry(p, 0.0, col));
   Scene::Instance().RequestRedraw();
   FlagRebuild();
 }
 
-void PrimList::AddLine(geom::Vec3& p1, geom::Vec3& p2, const Color& col)
+void PrimList::AddLine(const geom::Vec3& p1, const geom::Vec3& p2, const Color& col1, const Color& col2)
 {
-  lines_.push_back(LineEntry(p1,p2,col));
+  lines_.push_back(LineEntry(p1,p2,0.0,0.0,col1,col2));
   Scene::Instance().RequestRedraw();
   FlagRebuild();
 }
 
-void PrimList::SetDiameter(float d)
+void PrimList::AddSphere(const geom::Vec3& c, float r, const Color& col)
 {
-  radius_=d*0.5;
+  spheres_.push_back(PointEntry(c, r, col));
   Scene::Instance().RequestRedraw();
   FlagRebuild();
 }
 
-void PrimList::SetRadius(float r)
+void PrimList::AddCyl(const geom::Vec3& p1, const geom::Vec3& p2, float r1, float r2, const Color& col1, const Color& col2)
 {
-  radius_=r;
+  cyls_.push_back(LineEntry(p1, p2, r1, r2, col1, col2));
   Scene::Instance().RequestRedraw();
   FlagRebuild();
 }
 
+void PrimList::SetDiameter(float d)
+{
+  LOG_WARNING("PrimList::SetDiameter is defunct");
+}
+
+void PrimList::SetRadius(float r)
+{
+  LOG_WARNING("PrimList::SetDiameter is defunct");
+}
+
 void PrimList::SetSphereDetail(unsigned int d)
 {
   sphere_detail_=d;
@@ -197,8 +219,19 @@ void PrimList::SetArcDetail(unsigned int d)
 
 void PrimList::SetColor(const Color& c)
 {
+  for(PointEntryList::iterator it=points_.begin();it!=points_.end();++it) {
+    it->col=c;
+  }
   for(LineEntryList::iterator it=lines_.begin();it!=lines_.end();++it) {
-    it->color=c;
+    it->col1=c;
+    it->col2=c;
+  }
+  for(PointEntryList::iterator it=spheres_.begin();it!=spheres_.end();++it) {
+    it->col=c;
+  }
+  for(LineEntryList::iterator it=cyls_.begin();it!=cyls_.end();++it) {
+    it->col1=c;
+    it->col2=c;
   }
   Scene::Instance().RequestRedraw();
   FlagRebuild();
@@ -208,28 +241,29 @@ void PrimList::SetColor(const Color& c)
 ////////////////////////////////
 // private methods
 
-void PrimList::render_simple()
+void PrimList::prep_simple_va()
 {
-  va_.Clear();
-  va_.SetLighting(false);
-  va_.SetCullFace(false);
-  va_.SetColorMaterial(false);
-  va_.SetMode(0x3);
-  va_.SetTwoSided(true);
-  va_.SetAALines(GetAALines());
+  simple_va_.Clear();
+  simple_va_.SetLighting(false);
+  simple_va_.SetCullFace(false);
+  simple_va_.SetColorMaterial(false);
+  simple_va_.SetMode(0x3);
+  simple_va_.SetTwoSided(true);
+  simple_va_.SetAALines(GetAALines());
+  simple_va_.SetOpacity(GetOpacity());
 
   for(PointEntryList::const_iterator it=points_.begin();it!=points_.end();++it) {
-    va_.Add(it->pos,geom::Vec3(),it->color);
+    simple_va_.Add(it->pos,geom::Vec3(),it->col);
   }
 
   for(LineEntryList::const_iterator it=lines_.begin();it!=lines_.end();++it) {
-    VertexID id0 = va_.Add(it->pos1,geom::Vec3(),it->color);
-    VertexID id1 = va_.Add(it->pos2,geom::Vec3(),it->color);
-    va_.AddLine(id0,id1);
+    VertexID id0 = simple_va_.Add(it->pos1,geom::Vec3(),it->col1);
+    VertexID id1 = simple_va_.Add(it->pos2,geom::Vec3(),it->col2);
+    simple_va_.AddLine(id0,id1);
   }
 }
 
-void PrimList::render_custom()
+void PrimList::prep_va()
 {
   va_.Clear();
   va_.SetLighting(true);
@@ -237,18 +271,15 @@ void PrimList::render_custom()
   va_.SetColorMaterial(true);
   va_.SetMode(0x4);
 
-  for(PointEntryList::const_iterator it=points_.begin();it!=points_.end();++it) {
-    va_.AddSphere(SpherePrim(it->pos, radius_, it->color),
+  for(PointEntryList::const_iterator it=spheres_.begin();it!=spheres_.end();++it) {
+    va_.AddSphere(SpherePrim(it->pos, it->rad, it->col), 
                   GetSphereDetail());
   }
 
-  for(LineEntryList::const_iterator it=lines_.begin();it!=lines_.end();++it) {
-    va_.AddSphere(SpherePrim(it->pos1, radius_, it->color),
-                  GetSphereDetail());
-    va_.AddSphere(SpherePrim(it->pos2, radius_, it->color),
-                  GetSphereDetail());
-    va_.AddCylinder(CylinderPrim(it->pos1,it->pos2,radius_,it->color),
-                    GetArcDetail());
+  for(LineEntryList::const_iterator it=cyls_.begin();it!=cyls_.end();++it) {
+    va_.AddCylinder(CylinderPrim(it->pos1, it->pos2, it->rad1, it->rad2, it->col1, it->col2),
+                    GetArcDetail(),
+                    true);
   }
 }
 
diff --git a/modules/gfx/src/prim_list.hh b/modules/gfx/src/prim_list.hh
index daba0e7e50439122f919fc922678d636be32f5bb..bf8fb98ed8ea345c97b408b96d782d609282bc41 100644
--- a/modules/gfx/src/prim_list.hh
+++ b/modules/gfx/src/prim_list.hh
@@ -35,33 +35,27 @@ class PrimList;
 
 typedef boost::shared_ptr<PrimList> PrimListP;
 
-/// \brief rudimentary graphical lines rendering.
-/// 
-/// The primitives may be rendered either with gfx::RenderMode::SIMPLE or 
-/// gfx::RenderMode::CUSTOM. The render mode can be changed with
-/// GfxObj::SetRenderMode().
-/// 
 /// \see gfx::Cuboid, \ref primitives.py "Displaying Lines and Quads",
 ///      gfx::Quad, \ref gradient.py "Gradient Example",
 ///      \ref random_lines.py "Random Lines"
 class DLLEXPORT_OST_GFX PrimList: public GfxObj 
 {
   struct PointEntry {
-    PointEntry(const geom::Vec3& p, const Color& c):
-      pos(p), color(c) {}
+    PointEntry(const geom::Vec3& p, float r, const Color& c):
+      pos(p), rad(r), col(c) {}
     geom::Vec3 pos;
-    Color color;
-    geom::Mat3 rotmat;
+    float rad;
+    Color col;
   };
 
   typedef std::vector<PointEntry> PointEntryList;
 
   struct LineEntry {
-    LineEntry(const geom::Vec3& p1, const geom::Vec3& p2, const Color& c):
-      pos1(p1), pos2(p2), color(c) {}
-    geom::Vec3 pos1,pos2;
-    Color color;
-    geom::Mat3 rotmat;
+    LineEntry(const geom::Vec3& p1, const geom::Vec3& p2, float r1, float r2, const Color& c1, const Color& c2):
+      pos1(p1), pos2(p2), rad1(r1), rad2(r2), col1(c1), col2(c2) {}
+    geom::Vec3 pos1, pos2;
+    float rad1, rad2;
+    Color col1, col2;
   };
 
   typedef std::vector<LineEntry> LineEntryList;
@@ -70,6 +64,8 @@ class DLLEXPORT_OST_GFX PrimList: public GfxObj
   /// \brief create new prim list
   PrimList(const String& name);
 
+  virtual geom::AlignedCuboid GetBoundingBox() const;
+
   virtual void ProcessLimits(geom::Vec3& minc, geom::Vec3& maxc, 
                              const mol::Transform& tf) const;
   /// \brief get center      
@@ -81,19 +77,49 @@ class DLLEXPORT_OST_GFX PrimList: public GfxObj
 
   virtual void OnRenderModeChange();
 
-  /// \brief  clear all prims
+  /// \brief clear all prims
   void Clear();
 
-  /// \brief add line as defined by two endpoints
-  void AddPoint(geom::Vec3& p, const Color& col=Color());
+  /*!
+     \brief add point
+
+     python interface:
+
+     PrimList.AddPoint(pos, color=gfx.Color())
+  */
+  void AddPoint(const geom::Vec3& p, const Color& col);
+
+  /*!
+    \brief add line
+
+    Python interface:
+
+    PrimList.AddLine(pos1,pos2,col=gfx.WHITE,col1=gfx.WHITE,col2=gfx.WHITE)
+  */
+  void AddLine(const geom::Vec3& p1, const geom::Vec3& p2, const Color& col1, const Color& col2);
 
-  /// \brief add line as defined by two endpoints
-  void AddLine(geom::Vec3& p1, geom::Vec3& p2, const Color& col=Color());
+  /*!
+    \brief add sphere
 
-  /// \brief cylinder diameter for custom rendering mode
+    Python interface:
+
+    PrimList.AddSphere(cen,rad,col=gfx.WHITE)
+  */
+  void AddSphere(const geom::Vec3& cen, float rad, const Color& col);
+
+  /*!
+    \brief add cylinder
+
+    Python interface:
+
+    PrimList.AddCyl(pos1,pos2,rad=1.0,rad1=1.0,rad2=1.0,col=gfx.WHITE,col1=gfx.WHITE,col2=gfx.WHITE)
+  */
+  void AddCyl(const geom::Vec3& p0, const geom::Vec3& p1, float r1, float r2, const Color& col1, const Color& col2);
+
+  /// defunct
   void SetDiameter(float d);
   
-  /// \brief sphere radius for points in custom rendering mode
+  /// defunct
   void SetRadius(float r);
 
   /// \brief set global prims color, overriding individual ones
@@ -105,19 +131,23 @@ class DLLEXPORT_OST_GFX PrimList: public GfxObj
   void SetArcDetail(unsigned int d);
   unsigned int GetArcDetail() const {return arc_detail_;}
 
+  // TODO: add point and line pixel width
+
  protected:
   virtual void CustomPreRenderGL(bool flag);
 
  private:
   PointEntryList points_;
   LineEntryList lines_;
-  float radius_;
-  float diameter_;
+  PointEntryList spheres_;
+  LineEntryList cyls_;
   unsigned int sphere_detail_;
   unsigned int arc_detail_;
+
+  IndexedVertexArray simple_va_;
   
-  void render_simple();
-  void render_custom();
+  void prep_simple_va();
+  void prep_va();
 };
 
 /// \example primitives.py
diff --git a/modules/gfx/src/vertex_array.cc b/modules/gfx/src/vertex_array.cc
index da764f97f8d808fc4fd7b7cc1b6ac3b9516e9476..ab6db586b8f97c96f963da4c4314fe0f2f17f657 100644
--- a/modules/gfx/src/vertex_array.cc
+++ b/modules/gfx/src/vertex_array.cc
@@ -78,7 +78,7 @@ IndexedVertexArray::IndexedVertexArray()
 {
   initialized_=false;
   Reset(); // replaces ctor initialization list
-  glGenTextures(1,&tex_id_);
+  //glGenTextures(1,&tex_id_);
 }
 
 IndexedVertexArray::~IndexedVertexArray()
@@ -88,7 +88,7 @@ IndexedVertexArray::~IndexedVertexArray()
 IndexedVertexArray::IndexedVertexArray(const IndexedVertexArray& va)
 {
   copy(va);
-  glGenTextures(1,&tex_id_);
+  //glGenTextures(1,&tex_id_);
 }
 
 IndexedVertexArray& IndexedVertexArray::operator=(const IndexedVertexArray& va)
@@ -248,7 +248,7 @@ void IndexedVertexArray::AddIcoSphere(const SpherePrim& prim, unsigned int detai
   }
 }
 
-void IndexedVertexArray::AddCylinder(const CylinderPrim& prim, unsigned int detail,bool cap)
+void IndexedVertexArray::AddCylinder(const CylinderPrim& prim, unsigned int detail, bool cap)
 {
   dirty_=true;
   
@@ -266,12 +266,19 @@ void IndexedVertexArray::AddCylinder(const CylinderPrim& prim, unsigned int deta
   // prepare first vertices to add
   std::vector<Vec3>::const_iterator it=vlist.begin();
   Vec3 v0 = (*it);
-  Vec3 n0 = prim.rotmat * v0; 
-  v0*=prim.radius;
+  bool slant=(prim.radius1!=prim.radius2);
+  // adjust for slant
+  float beta = slant ? atan2(prim.radius1-prim.radius2,prim.length) : 0.0;
+  float cosb = slant ? cos(beta) : 1.0;
+  float sinb = slant ? sin(beta) : 0.0;
+  Vec3 n0 = slant ? prim.rotmat * (cosb*v0+geom::Vec3(0.0,0.0,sinb)) : prim.rotmat*v0;
+
+  v0*=prim.radius1;
+  Vec3 v1 = (*it)*prim.radius2+off;
   VertexID id1 = Add(prim.rotmat * v0 + prim.start, n0, prim.color1);
-  VertexID id2 = Add(prim.rotmat * (v0+off) + prim.start, n0, prim.color2);
+  VertexID id2 = Add(prim.rotmat * v1 + prim.start, n0, prim.color2);
   VertexID cid1 = cap ? Add(prim.rotmat * v0 + prim.start, cn0, prim.color1) : 0;
-  VertexID cid2 = cap ? Add(prim.rotmat * (v0+off) + prim.start, cn1, prim.color2) : 0;
+  VertexID cid2 = cap ? Add(prim.rotmat * v1 + prim.start, cn1, prim.color2) : 0;
   
   // now for the loop around the circle
   VertexID id3=id1;
@@ -281,15 +288,16 @@ void IndexedVertexArray::AddCylinder(const CylinderPrim& prim, unsigned int deta
   ++it;
   for(;it!=vlist.end();++it) {
     v0 = (*it);
-    n0 = prim.rotmat * v0; 
-    v0 *= prim.radius;
+    Vec3 n0 = slant ? prim.rotmat * (cosb*v0+geom::Vec3(0.0,0.0,sinb)) : prim.rotmat*v0;
+    v0 *= prim.radius1;
+    Vec3 v1 = (*it)*prim.radius2+off;
     VertexID id5 = Add(prim.rotmat * v0 + prim.start, n0, prim.color1);
-    VertexID id6 = Add(prim.rotmat * (v0+off) + prim.start, n0, prim.color2);
+    VertexID id6 = Add(prim.rotmat * v1 + prim.start, n0, prim.color2);
     AddTri(id3,id5,id4);
     AddTri(id5,id6,id4);
     if(cap) {
       VertexID cid5 = Add(prim.rotmat * v0 + prim.start, cn0, prim.color1);
-      VertexID cid6 = Add(prim.rotmat * (v0+off) + prim.start, cn1, prim.color2);
+      VertexID cid6 = Add(prim.rotmat * v1 + prim.start, cn1, prim.color2);
       AddTri(cid0,cid5,cid3);
       AddTri(cid7,cid4,cid6);
       cid3=cid5;
diff --git a/modules/gfx/tests/CMakeLists.txt b/modules/gfx/tests/CMakeLists.txt
index 469122e944ac2e30911ce45a1517110d7f19641d..c87cf918355421e221365f43a8592691b7d567d0 100644
--- a/modules/gfx/tests/CMakeLists.txt
+++ b/modules/gfx/tests/CMakeLists.txt
@@ -1,6 +1,7 @@
 set(OST_GFX_UNIT_TESTS
   tests.cc
   test_gfx_node.cc
+  test_primlist.py
 )
 if (ENABLE_IMG)
   list(APPEND OST_GFX_UNIT_TESTS test_map_octree.cc)
diff --git a/modules/gfx/tests/test_primlist.py b/modules/gfx/tests/test_primlist.py
new file mode 100644
index 0000000000000000000000000000000000000000..82fd4a376e284e0526b5f79fb28af5df300349f2
--- /dev/null
+++ b/modules/gfx/tests/test_primlist.py
@@ -0,0 +1,27 @@
+import unittest
+if __name__== '__main__':
+  import sys
+  sys.path.insert(0,"../../../stage/lib64/openstructure/")
+
+import ost
+import ost.gfx
+import ost.geom
+
+class TestPrimList(unittest.TestCase):
+  def setUp(self):
+    pass
+
+  def test_(self):
+    pl=ost.gfx.PrimList("foo")
+    pl.AddPoint([0,0,0])
+    pl.AddPoint(ost.geom.Vec3(1,2,3),col=ost.gfx.RED)
+    pl.AddLine([0,0,0],[1,2,3])
+    pl.AddLine(ost.geom.Vec3(0,0,0),ost.geom.Vec3(1,2,3),col=ost.gfx.BLUE)
+    pl.AddSphere([0,0,0],rad=2.0)
+    pl.AddSphere(ost.geom.Vec3(1,2,3),col=ost.gfx.RED,rad=3.0)
+    pl.AddCyl([0,0,0],[1,2,3],rad=0.5,col=ost.gfx.YELLOW)
+    pl.AddCyl(ost.geom.Vec3(0,0,0),ost.geom.Vec3(1,2,3),rad1=0.5,rad2=0.1,col1=ost.gfx.BLUE,col2=ost.gfx.GREEN)
+
+if __name__== '__main__':
+  unittest.main()
+