From: Thomas Bläsius Date: Tue, 17 Dec 2013 17:49:23 +0000 (+0100) Subject: fancy decorator X-Git-Url: https://i11git.iti.kit.edu/anon-gitweb/?p=Misc%2Fipe.git;a=commitdiff_plain;h=1cf3b8269c0fe8d0b8d700b3a79f4b65b0ff9685;ds=sidebyside fancy decorator --- diff --git a/ipelets/decorator/decorator.lua b/ipelets/decorator/decorator.lua index 8797c8b..cffa3b5 100644 --- a/ipelets/decorator/decorator.lua +++ b/ipelets/decorator/decorator.lua @@ -12,32 +12,163 @@ function decorator_names(model) return res 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 + +-- 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 + -- apply to every point + for i,point in ipairs(subpath) do + subpath[i] = matrix_func(point) * point + end + + -- apply to arcs + if (subpath["type"] == "arc") then + local arc = subpath["arc"] + local center = arc:matrix():translation() + subpath["arc"] = matrix_func(center) * arc + end + end + end +end + +-- 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 + transform_shape(shape, matrix_func) +end + +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 + +function report_problem(model, text) + ipeui.messageBox(mainWindow(model), "warning", text, nil, nil) +end + +function run_fancy_decorator (model) + local p = model:page() + local prim = p:primarySelection() + local bbox_target = p:bbox(prim) + + local deco_obj_group = ask_for_decorator(model) + 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()) + + 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("fancy 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) + dialog:add("ok", "button", { label="&Ok", action="accept" }, 2, 2) + dialog:add("cancel", "button", { label="&Cancel", action="reject" }, 2, 1) + local r = dialog:execute() + if not r then return end + local deco_name = decorators[dialog:get("deco")] + local symbol = model.doc:sheets():find("symbol", deco_name) + return symbol:clone() +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.") + report_problem(model, "The decoration needs to be a path.") return end local shape = deco:shape() + local m = deco:matrix() for _,path in pairs(shape) do for _,subpath in ipairs(path) do -- move all points for i,point in ipairs(subpath) do - subpath[i] = translation(bbox, point) * point + subpath[i] = translation(bbox, m*point) * m*point end -- for acs, the center must be translated separately if (subpath["type"] == "arc") then local arc = subpath["arc"] local arc_pos = arc:matrix():translation() - subpath["arc"] = translation(bbox, arc_pos) * arc + subpath["arc"] = translation(bbox, m*arc_pos) * m * arc end end end -- update model deco:setShape(shape) + deco:setMatrix(ipe.Matrix()) model:creation("create", deco) end @@ -94,4 +225,5 @@ end label = "Decorator" methods = { { label = "Decorate", run=run_decorator}, + { label = "Fancy decorator", run=run_fancy_decorator}, } diff --git a/ipelets/decorator/test.ipe b/ipelets/decorator/test.ipe index 29da8f1..d3c57e6 100644 --- a/ipelets/decorator/test.ipe +++ b/ipelets/decorator/test.ipe @@ -1,7 +1,7 @@ - + \usepackage[english]{babel} \usepackage{blindtext} @@ -213,6 +213,47 @@ h h + + +-5 5 m +-5 11 l +1 11 l +4 0 0 -4 1 7 5 7 a +5 5 l +-5 5 m +-5 -1 l +4 0 0 4 -1 -1 -1 -5 a +5 -5 l +5 1 l +5 5 l +-5 5 m +5 5 l + + + + + +10 2 m +8 0 0 8 2 2 2 10 a +-10 10 l +-10 -2 l +8 0 0 8 -2 -2 -2 -10 a +10 -10 l +h + + +-10 10 m +10 -10 l + + +12 20 m +12 12 l +20 12 l +20 20 l +h + + + @@ -284,7 +325,37 @@ h - + + +582 166 m +8 0 0 8 574 166 574 174 a +266 174 l +266 65.503 l +8 0 0 8 274 65.503 274 57.503 a +582 57.503 l +h + + +266 174 m +582 57.503 l + + + + +86 62 m +8 0 0 8 78 62 78 70 a +42 70 l +42 34 l +8 0 0 8 50 34 50 26 a +86 26 l +h + + +42 70 m +86 26 l + + + 469 369 m 4 0 0 4 465 369 465 373 a 159 373 l @@ -314,5 +385,49 @@ h \blindtext \blindtext \blindtext +\blindtext + +48 128 m +48 96 l +80 96 l +80 128 l +h + + +48 64 m +48 32 l +80 32 l +80 64 l +h + + +12 28 m +12 26 l +14 26 l +14 28 l +h + + + +10 2 m +8 0 0 8 2 2 2 10 a +-10 10 l +-10 -2 l +8 0 0 8 -2 -2 -2 -10 a +10 -10 l +h + + +-10 10 m +10 -10 l + + +12 20 m +12 12 l +20 12 l +20 20 l +h + +