Tangent Lines

Andrew Martchenko <[email protected]> Sat, 2 Jul 2016 23:38:57 +1000
Newsgroups gmane.comp.graphics.ipe.general
Message-ID <[email protected]>
Hi Otfried,

I decided to take your advice from facebook and I wrote my own ipelet to 
draw tangent segments from a primary selected marker or circle to all 
other selected markers and circles (see attached ipelet).

The ipelet works quite well, but I would like to improve it by having 
some of the line segments be selected after the ipelet executes. 
Specifically in my case, their can be up to four line segments that are 
tangent to two circles (see attached pdf for example), the segments that 
cross one another are rarely useful (to me) and I would like them to be 
selected so that I can easily delete all such segments with a click of a 
button. Is this something that can be done with an ipelet??

Also, if anyone by chance reads the code, I would appreciate some 
feedback as this is my first lua program and I'm sure there are many 
things that can be done more efficiently.

Cheers,

Andrew M.

_______________________________________________
Ipe-discuss mailing list
Ipe-discuss-rGrgPyRx506NN8uzcEdRPYRWq/[email protected]
http://lists.science.uu.nl/mailman/listinfo/ipe-discuss
tangent_lines_example.pdf (application/pdf, 19.6 KB) - not displayed
tangentlines.lua (text/x-lua, 5.1 KB)
----------------------------------------------------------------------
-- Tangent Lines ipelet
----------------------------------------------------------------------
--[[

   This file is part of the extensible drawing editor Ipe.
   Copyright (C) 1993-2016  Otfried Cheong

   Ipe 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 3 of the License, or
   (at your option) any later version.

   As a special exception, you have permission to link Ipe with the
   CGAL library and distribute executables, as long as you follow the
   requirements of the Gnu General Public License in regard to all of
   the software in the executable aside from CGAL.

   Ipe 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 Ipe; if not, you can find it at
   "http://www.gnu.org/copyleft/gpl.html", or write to the Free
   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   --]]

   label = "Tangent Lines"


   about = [[
Draws tangent segments from a primary selected marker/circle to all other selected markers and circles.
By Andrew Martchenko
]]





function circ_intersect(x1,y1,r1,x2,y2,r2)
   local dx = x2 - x1                          -- difference in x coordinates
   local dy = y2 - y1                          -- difference in y coordinates
   local dc = math.sqrt(dx*dx + dy*dy)         -- distance between centers
   local k = (dc*dc + r1*r1 - r2*r2)/(2*dc)    -- distance from center 1 to line
   -- joining points of intersection
   local p1 = ipe.Vector(x1 + dx*k/dc + (dy/dc)*math.sqrt(r1*r1 - k*k),
			 y1 + dy*k/dc - (dx/dc)*math.sqrt(r1*r1 - k*k))
   --OR
   local p2 = ipe.Vector(x1 + dx*k/dc - (dy/dc)*math.sqrt(r1*r1 - k*k),
			 y1 + dy*k/dc + (dx/dc)*math.sqrt(r1*r1 - k*k))
   return p1, p2
end

function create_segment(model,seg)
   local shape = {type="curve", closed=false;
		  {type="segment"; seg.from, seg.to}}

   local obj = ipe.Path(model.attributes,  {shape} )

   model:creation("tangent segment", obj)
end


function get_circ(obj)
   if obj:type()=="path" then
      if obj:shape()[1].type == "ellipse" then
	 local circ = obj:matrix()*obj:shape()[1][1]
	 return ipe.Vector(circ:elements()[5],circ:elements()[6]),circ:elements()[1]
      end
   end
end


function get_mark(obj)
   if obj:type()=="reference" then
      return obj:matrix()*obj:position()
   end
end
function mark_circ_tangent_points(m,c,r)
   local d = c-m
   local r2 = math.sqrt(d:len()*d:len() - r*r)
   if(d:len()<=r) then return end -- there are no tagent lines in this case

   return circ_intersect(m.x, m.y, r2, c.x, c.y, r)
end

function print_selection_warning(model)
   model:warning("Must select either:\nA marker and circle\nOR\nTwo circles")
end

function run(model)


   local c1,r1,c2,r2,p1,p2
   local p = model:page()
   local prim = p:primarySelection()
   if not prim then print_selection_warning(model) return end
   local obj = p[prim]
   local pc=nil -- primary circle center, radius, marker
   local pr=nil
   local pm=nil
   
   pc,pr = get_circ(obj)
   pm = get_mark(obj)

   local segs = {}


   for i, obj, sel, layer in p:objects() do

      c=nil
      r=nil
      m=nil
      if sel and i~=prim then




	 c,r = get_circ(obj)
	 m = get_mark(obj)

	 if pm and m  then
	    segs[#segs+1] = {from=pm,to=m,selected=false}
	    
	 elseif pm and c then
	    p1,p2 = mark_circ_tangent_points(pm,c,r)
	    if(p1) then -- if there are tangent point
	       segs[#segs+1] = {from=pm,to=p1,selected=false}
	       segs[#segs+1] = {from=pm,to=p2,selected=false}
	    end

	 elseif pc and m then
	    p1,p2 = mark_circ_tangent_points(m,pc,pr)
	    if(p1) then -- if there are tangent point
	       segs[#segs+1] = {from=p1,to=m,selected=false}
	       segs[#segs+1] = {from=p2,to=m,selected=false}
	    end
	    
	 elseif pc and c then

	    
	    
	    if pr < r then
	       c1,r1=pc,pr
	       c2,r2=c,r
	    else
	       c2,r2=pc,pr
	       c1,r1=c,r
	    end


	        p1,p2 = mark_circ_tangent_points(c1,c2,r2-r1)

	       if(p1) then 
		  local v1 = p1-c2
		  local v2 = p2-c2
		  v1 = v1:normalized()*r1
		  v2 = v2:normalized()*r1
		  p1 = p1+v1
		  p2 = p2+v2
		  local p3=c1+v1
		  local p4=c1+v2
		  -- outer tangent segments
		  segs[#segs+1] = {from=p1,to=p3,selected=false}
		  segs[#segs+1] = {from=p2,to=p4,selected=false}


		  p1,p2 = mark_circ_tangent_points(c1,c2,r2+r1)
		  if(p1) then 
		     v1 = c2-p1
		     v2 = c2-p2
		     v1 = v1:normalized()*r1
		     v2 = v2:normalized()*r1
		     p1 = p1+v1
		     p2 = p2+v2
		     p3=c1+v1
		     p4=c1+v2
		     -- inner/crisscross tangent segments
		     segs[#segs+1] = {from=p1,to=p3,selected=true}
		     segs[#segs+1] = {from=p2,to=p4,selected=true}

		  end
	       end
	    end

	    
	 end
      end


   for i,seg in pairs(segs) do
      create_segment(model, seg)
   end

      
end



   shortcuts.ipelet_1_tangentlines = "Alt+t"