Re: Tangent Lines
Otfried Cheong <[email protected]> Sat, 02 Jul 2016 19:27:52 +0200
| Newsgroups | gmane.comp.graphics.ipe.general |
|---|---|
| Message-ID | <1467480472.1835598.655081513.5C7109C2@webmail.messagingengine.com> |
On Sat, Jul 2, 2016, at 15:38, Andrew Martchenko wrote: > 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?? Yes, this can be done, but it's a bit more tricky. An ipelet is not allowed to simply modify the current page - it must cooperate with Ipe's undo mechanism. One way to do this is to call model:creation, as you do - this will create an entry for the undo stack that inserts one new object. You call this several times, which means that if you try to undo your ipelet's effect, you'll see that you need to press Ctrl+Z several times to remove the tangents one by one. I'm attaching a modified version that has a new function create_objects that shows how to create an undo item for several new objects. It selects only the ones that have the "select" attribute set. By the way, there seems to be a problem when you make two disks with the same radius and centers at equal y-coordinate: the outer tangents are wrong. Cheers, Otfried _______________________________________________ Ipe-discuss mailing list Ipe-discuss-rGrgPyRx506NN8uzcEdRPYRWq/[email protected] http://lists.science.uu.nl/mailman/listinfo/ipe-discuss
tangentlines.lua
(text/x-lua, 5.7 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_objects(model, objects)
local t = { label="tangent segments",
pno=model.pno,
vno=model.vno,
layer=model:page():active(model.vno),
objects=objects
}
t.undo = function (t, doc)
for i = 1,#t.objects do
doc[t.pno]:remove(#doc[t.pno])
end
end
t.redo = function (t, doc)
doc[t.pno]:deselectAll()
for _,obj in ipairs(t.objects) do
doc[t.pno]:insert(nil, obj.obj, obj.select, t.layer)
end
doc[t.pno]:ensurePrimarySelection()
end
model:register(t)
end
function create_segment(model, seg)
local shape = {type="curve", closed=false;
{type="segment"; seg.from, seg.to}}
return ipe.Path(model.attributes, {shape} )
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
local segments = {}
for i,seg in ipairs(segs) do
segments[#segments+1] = { obj = create_segment(model, seg),
select = seg.selected and 2 or nil }
end
create_objects(model, segments)
end
shortcuts.ipelet_1_tangentlines = "Alt+t"