X-Git-Url: https://i11git.iti.kit.edu/anon-gitweb/?p=Misc%2Fipe.git;a=blobdiff_plain;f=ipelets%2Fdecorator%2Fdecorator.lua;h=76a05fe7b69a565953998d47df7fd2f270656fc8;hp=8797c8bc26429e073775e8e246fddefd13b1ee84;hb=f431cad417b64dd02d7f4f9682fe66ff97599322;hpb=0a034064373daa9ed6081c760772b2c2143aa647 diff --git a/ipelets/decorator/decorator.lua b/ipelets/decorator/decorator.lua index 8797c8b..76a05fe 100644 --- a/ipelets/decorator/decorator.lua +++ b/ipelets/decorator/decorator.lua @@ -1,81 +1,169 @@ --- return a table of names associated with decorator symbols -function decorator_names(model) - local sheets = model.doc:sheets() - local symbols = sheets:allNames("symbol") - local res = {} - for _, name in pairs(symbols) do - if name:find("deco/") == 1 then - res[#res + 1] = name - end +-- Helper for compatibility with different ipe-versions. +function mainWindow(model) + if model.ui.win == nil then + return model.ui + else + return model.ui:win() end - return res end --- Decorate something given by its bounding box with a given deco --- object, which needs to be a path. -function decorate(model, bbox, deco) - if (deco:type() ~= "path") then - model.ui:explain("The decoration needs to be a path.") - return - end +-- Changes the transformation matrix of a path object to the identitiy +-- matrix without canging the appearance of the object (i.e., the +-- transformation matrix is applied to the objects shape) +function cleanup_matrix(path_obj) + local matrix = path_obj:matrix() + local matrix_func = function (point) return matrix end + local shape = path_obj:shape() + transform_shape(shape, matrix_func) + path_obj:setShape(shape) + path_obj:setMatrix(ipe.Matrix()) +end - local shape = deco:shape() +-- Transform a shape by transforming every point using a matrix +-- returend by matrix_func. The function matrix_func should take a +-- point and return a matrix (that is then used to transform this +-- point). +-- +-- Arcs are also transformed. +function transform_shape(shape, matrix_func) for _,path in pairs(shape) do - for _,subpath in ipairs(path) do - -- move all points + for _,subpath in ipairs(path) do + -- apply to every point for i,point in ipairs(subpath) do - subpath[i] = translation(bbox, point) * point + subpath[i] = matrix_func(point) * point end - -- for acs, the center must be translated separately + -- apply to arcs if (subpath["type"] == "arc") then local arc = subpath["arc"] - local arc_pos = arc:matrix():translation() - subpath["arc"] = translation(bbox, arc_pos) * arc + local center = arc:matrix():translation() + subpath["arc"] = matrix_func(center) * arc end end end - -- update model - deco:setShape(shape) - model:creation("create", deco) end --- The translation matrix that should be applied to a given point when --- doing the decoration. -function translation(bbox, point) - local dx = 0 - local dy = 0 - if (point.x > 0) then - dx = dx + bbox:width() +-- Resizes a given shape such that the same transformation also +-- transforms bbox_source to bbox_target. The transformation is done +-- by translating points of the shape such that all points in the same +-- quadrant with respect to center are translated in the same way. +-- +-- Clearly, the center has to lie inside bbox_source. +function resize_shape(shape, center, bbox_source, bbox_target) + -- assert that the center lies inside bbox_source + assert(bbox_source:left() < center.x) + assert(center.x < bbox_source:left() + bbox_source:width()) + assert(bbox_source:bottom() < center.y) + assert(center.y < bbox_source:bottom() + bbox_source:height()) + + -- translation of points to the left/right/top/bottom of the center + local dx_left = bbox_target:left() - bbox_source:left() + local dy_bottom = bbox_target:bottom() - bbox_source:bottom() + local dx_right = bbox_target:width() - bbox_source:width() + local dy_top = bbox_target:height() - bbox_source:height() + + -- transformation + local matrix_func = function (point) + local dx = dx_left + local dy = dy_bottom + if (center.x < point.x) then dx = dx + dx_right end + if (center.y < point.y) then dy = dy + dy_top end + return ipe.Translation(dx, dy) end - if (point.y > 0) then - dy = dy + bbox:height() + transform_shape(shape, matrix_func) +end + +-- Bounding box of a given object that is not currently contained in +-- the page. If the object is already part of the page, simply use +-- p:bbox(obj). +function bbox(obj, page) + local objno = #page + 1 + page:insert(objno, obj, nil, page:layers()[1]) + local bbox = page:bbox(objno) + page:remove(objno) + return bbox +end + +-- The bounding box of all selected objects. +function bbox_of_selected_objects(page) + local bbox = page:bbox(page:primarySelection()) + for obj = 1, #page, 1 do + if page:select(obj) then + bbox:add(page:bbox(obj)) + end end - dx = dx + bbox:left() - dy = dy + bbox:bottom() - return ipe.Translation(dx, dy) + return bbox end -function mainWindow(model) - if model.ui.win == nil then - return model.ui - else - return model.ui:win() +-- Show a warning to the user. +function report_problem(model, text) + ipeui.messageBox(mainWindow(model), "warning", text, nil, nil) +end + +-- Return a table of names associated with decorator symbols. +function decorator_names(model) + local sheets = model.doc:sheets() + local symbols = sheets:allNames("symbol") + local res = {} + for _, name in pairs(symbols) do + if name:find("deco/") == 1 then + res[#res + 1] = name + end end + return res end -function run_decorator(model) - -- get bbox of primary selection +-- Ask the user for a decorator and run the decoration. +function run_decorator (model) local p = model:page() local prim = p:primarySelection() - if not prim then - model.ui:explain("An object must be selected.") + if (not prim) then + report_problem(model, "You must select somethings.") return end - local bbox = p:bbox(prim) + -- local bbox_target = p:bbox(prim) + local bbox_target = bbox_of_selected_objects(p) - -- create decorator object + local deco_obj_group = ask_for_decorator(model) + if (not deco_obj_group) then return end + if (deco_obj_group:type() ~= "group") then + report_problem(model, "The decoration must be a group.") + return + end + + local objects = deco_obj_group:elements() + local last_obj = table.remove(objects, #objects) + local bbox_source = bbox(last_obj, p) + local center = ipe.Vector(bbox_source:left() + 0.5 * bbox_source:width(), + bbox_source:bottom() + 0.5 * bbox_source:height()) + + if (#objects == 0) then + report_problem(model, "The decoration must be a group of at least two elements.") + return + end + for i,deco_obj in ipairs(objects) do + if (deco_obj:type() ~= "path") then + report_problem(model, "Each decoration object needs to be a path.") + return + end + + cleanup_matrix(deco_obj) + local deco_shape = deco_obj:shape() + + resize_shape(deco_shape, center, bbox_source, bbox_target) + + deco_obj:setShape(deco_shape) + end + + local group = ipe.Group(objects) + + model:creation("decoration created", group) +end + +-- Asks the user for a decorator and returns the chosen decorator +-- object or nil. +function ask_for_decorator(model) local dialog = ipeui.Dialog(mainWindow(model), "Select a decorator.") local decorators = decorator_names(model) dialog:add("deco", "combo", decorators, 1, 1, 1, 2) @@ -85,13 +173,10 @@ function run_decorator(model) if not r then return end local deco_name = decorators[dialog:get("deco")] local symbol = model.doc:sheets():find("symbol", deco_name) - local deco = symbol:clone() - - -- run the decoration - decorate(model, bbox, deco) + return symbol:clone() end label = "Decorator" methods = { - { label = "Decorate", run=run_decorator}, + { label = "decorate", run=run_decorator}, }