119 lines
4.6 KiB
Python
119 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Extension Inkscape : génère une boîte à créneaux à découper au laser.
|
|
|
|
Chaque pièce est un objet indépendant, placé directement dans le calque courant,
|
|
pour pouvoir être sélectionnée et déplacée seule.
|
|
"""
|
|
|
|
import inkex
|
|
from inkex import Group, PathElement, TextElement
|
|
|
|
import box_core
|
|
|
|
|
|
class BoxMaker(inkex.EffectExtension):
|
|
def add_arguments(self, pars):
|
|
pars.add_argument("--tab")
|
|
pars.add_argument("--length", type=float, default=100.0)
|
|
pars.add_argument("--width", type=float, default=80.0)
|
|
pars.add_argument("--height", type=float, default=50.0)
|
|
pars.add_argument("--dim_type", default="exterior")
|
|
pars.add_argument("--thickness", type=float, default=3.0)
|
|
pars.add_argument("--finger", type=float, default=0.0)
|
|
pars.add_argument("--kerf", type=float, default=0.1)
|
|
pars.add_argument("--lid", type=inkex.Boolean, default=True)
|
|
pars.add_argument("--gap", type=float, default=5.0)
|
|
pars.add_argument("--stroke_color", type=inkex.Color, default=inkex.Color("red"))
|
|
pars.add_argument("--stroke_width", type=float, default=0.1)
|
|
pars.add_argument("--labels", type=inkex.Boolean, default=False)
|
|
|
|
def effect(self):
|
|
opt = self.options
|
|
try:
|
|
panels = box_core.make_box(
|
|
opt.length, opt.width, opt.height, opt.thickness,
|
|
finger=opt.finger, kerf=opt.kerf,
|
|
interior=opt.dim_type == "interior", lid=opt.lid, gap=opt.gap,
|
|
)
|
|
except ValueError as err:
|
|
raise inkex.AbortExtension(str(err))
|
|
|
|
mm = self._uu_per_mm()
|
|
origin_x, origin_y = self._origin(panels, mm)
|
|
layer = self.svg.get_current_layer()
|
|
# Les coordonnées sont calculées dans le repère du document : on annule
|
|
# la transformation éventuelle du calque (et de ses parents).
|
|
to_layer = -layer.composed_transform()
|
|
style = {
|
|
"fill": "none",
|
|
"stroke": str(inkex.Color(opt.stroke_color).to_rgb()),
|
|
"stroke-width": opt.stroke_width * mm,
|
|
}
|
|
|
|
for panel in panels:
|
|
path = PathElement()
|
|
path.set_id(self.svg.get_unique_id("boite-"))
|
|
path.label = panel.name
|
|
path.style = style
|
|
path.path = "M " + " L ".join(
|
|
f"{origin_x + x * mm:.4f},{origin_y + y * mm:.4f}" for x, y in panel.points
|
|
) + " Z"
|
|
|
|
if opt.labels:
|
|
group = Group()
|
|
group.set_id(self.svg.get_unique_id("boite-"))
|
|
group.label = panel.name
|
|
group.transform = to_layer
|
|
group.add(path)
|
|
group.add(self._label(panel, origin_x, origin_y, mm))
|
|
layer.add(group)
|
|
else:
|
|
path.transform = to_layer
|
|
layer.add(path)
|
|
|
|
def _uu_per_mm(self):
|
|
"""Unités utilisateur par mm, comme Inkscape les affiche.
|
|
|
|
svg.unittouu() se trompe quand le viewBox n'a pas les proportions de la
|
|
page : Inkscape applique alors une échelle uniforme (preserveAspectRatio
|
|
« meet »), soit la plus petite des deux échelles page / viewBox.
|
|
"""
|
|
svg = self.svg
|
|
vb_w, vb_h = svg.viewbox_width, svg.viewbox_height
|
|
page_w = svg.to_dimensional(svg.viewport_width, "mm")
|
|
page_h = svg.to_dimensional(svg.viewport_height, "mm")
|
|
if not (vb_w and vb_h and page_w and page_h):
|
|
return svg.unittouu("1mm")
|
|
return 1 / min(page_w / vb_w, page_h / vb_h)
|
|
|
|
def _origin(self, panels, mm):
|
|
"""Centre l'ensemble des pièces sur la vue (ou la page à défaut)."""
|
|
max_x = max(p.bbox()[2] for p in panels) * mm
|
|
max_y = max(p.bbox()[3] for p in panels) * mm
|
|
namedview = self.svg.namedview
|
|
if namedview is not None and namedview.get("inkscape:cx") is not None:
|
|
cx, cy = namedview.center
|
|
else:
|
|
cx, cy = self.svg.get_page_bbox().center
|
|
return cx - max_x / 2, cy - max_y / 2
|
|
|
|
def _label(self, panel, origin_x, origin_y, mm):
|
|
min_x, min_y, max_x, max_y = panel.bbox()
|
|
size = min(5.0, (max_y - min_y) / 4) * mm
|
|
text = TextElement()
|
|
text.set_id(self.svg.get_unique_id("boite-"))
|
|
text.set("x", str(origin_x + (min_x + max_x) / 2 * mm))
|
|
text.set("y", str(origin_y + (min_y + max_y) / 2 * mm + size / 3))
|
|
text.style = {
|
|
"font-size": size,
|
|
"text-anchor": "middle",
|
|
"fill": "#0000ff",
|
|
"stroke": "none",
|
|
}
|
|
text.text = panel.name
|
|
return text
|
|
|
|
|
|
if __name__ == "__main__":
|
|
BoxMaker().run()
|