🐛 Fix incorrect boolean shapes generation on builder

This commit is contained in:
Andrey Antukh 2025-06-01 11:06:00 +02:00
parent 77fa235965
commit 89a09346a5
4 changed files with 130 additions and 26 deletions

View file

@ -10,13 +10,17 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
;; [app.common.features :as cfeat]
[app.common.exceptions :as ex]
[app.common.files.changes :as ch]
;; [app.common.features :as cfeat]
[app.common.files.helpers :as cph]
[app.common.files.migrations :as fmig]
[app.common.geom.shapes :as gsh]
[app.common.schema :as sm]
[app.common.svg :as csvg]
[app.common.types.color :as types.color]
[app.common.types.component :as types.comp]
[app.common.types.container :as types.cont]
[app.common.types.file :as types.file]
[app.common.types.page :as types.page]
[app.common.types.path :as types.path]
@ -330,6 +334,35 @@
(commit-change state change :add-container true)))]
(update state ::parent-stack pop))))
(defn- update-bool-style-properties
[bool-shape objects]
(let [xform
(comp
(map (d/getf objects))
(remove cph/frame-shape?)
(remove types.comp/is-variant?)
(remove (partial types.cont/has-any-copy-parent? objects)))
children
(->> (get bool-shape :shapes)
(into [] xform)
(not-empty))]
(when-not children
(ex/raise :type :validation
:code :empty-children
:hint "expected a group with at least one shape for creating a bool"))
(let [head (if (= type :difference)
(first children)
(last children))
fills (if (and (contains? head :svg-attrs) (empty? (:fills head)))
types.path/default-bool-fills
(get head :fills))]
(-> bool-shape
(assoc :fills fills)
(assoc :stroks (get head :strokes))))))
(defn add-bool
[state params]
(let [{:keys [group-id type]}
@ -338,33 +371,40 @@
group
(get-shape state group-id)
children
(->> (get group :shapes)
(not-empty))]
objects
(get-current-objects state)
(assert (some? children) "expect group to have at least 1 element")
bool
(-> group
(assoc :type :bool)
(assoc :bool-type type)
(update-bool-style-properties objects)
(types.path/update-bool-shape objects))
(let [objects (get-current-objects state)
bool (-> group
(assoc :type :bool)
(assoc :bool-type type)
(types.path/update-bool-shape objects))
change {:type :mod-obj
:id (:id bool)
:operations
[{:type :set :attr :content :val (:content bool) :ignore-touched true}
{:type :set :attr :type :val :bool :ignore-touched true}
{:type :set :attr :bool-type :val type :ignore-touched true}
{:type :set :attr :selrect :val (:selrect bool) :ignore-touched true}
{:type :set :attr :points :val (:points bool) :ignore-touched true}
{:type :set :attr :x :val (-> bool :selrect :x) :ignore-touched true}
{:type :set :attr :y :val (-> bool :selrect :y) :ignore-touched true}
{:type :set :attr :width :val (-> bool :selrect :width) :ignore-touched true}
{:type :set :attr :height :val (-> bool :selrect :height) :ignore-touched true}]}]
selrect
(get bool :selrect)
(-> state
(commit-change change :add-container true)
(assoc ::last-id group-id)))))
operations
[{:type :set :attr :content :val (:content bool) :ignore-touched true}
{:type :set :attr :type :val :bool :ignore-touched true}
{:type :set :attr :bool-type :val type :ignore-touched true}
{:type :set :attr :selrect :val selrect :ignore-touched true}
{:type :set :attr :points :val (:points bool) :ignore-touched true}
{:type :set :attr :x :val (get selrect :x) :ignore-touched true}
{:type :set :attr :y :val (get selrect :y) :ignore-touched true}
{:type :set :attr :width :val (get selrect :width) :ignore-touched true}
{:type :set :attr :height :val (get selrect :height) :ignore-touched true}
{:type :set :attr :fills :val (:fills bool) :ignore-touched true}
{:type :set :attr :strokes :val (:strokes bool) :ignore-touched true}]
change
{:type :mod-obj
:id (:id bool)
:operations operations}]
(-> state
(commit-change change :add-container true)
(assoc ::last-id group-id))))
(defn add-shape
[state params]

View file

@ -55,7 +55,7 @@
[shape (cph/get-position-on-parent objects (:id head))]))
(defn group->bool
(defn- group->bool
[type group objects]
(let [shapes (->> (:shapes group)
(map (d/getf objects)))

View file

@ -1,5 +1,11 @@
# CHANGELOG
## 1.0.2
- Fix incorrect boolean type assignation
- Fix fill and stroke handling on boolean shape creation
- Add sample-bool.js to the playground directory
## 1.0.1
- Make the library generate a .penpot file compatible with penpot 2.7.x

View file

@ -0,0 +1,58 @@
import * as penpot from "#self";
import { writeFile, readFile } from "fs/promises";
(async function () {
const context = penpot.createBuildContext();
{
context.addFile({ name: "Test File 1" });
context.addPage({ name: "Foo Page" });
const groupId = context.addGroup({
name: "Bool Group"
})
context.addRect({
name: "Rect 1",
x: 20,
y: 20,
width:100,
height:100,
});
context.addRect({
name: "Rect 2",
x: 90,
y: 90,
width:100,
height:100,
fills: [{fillColor: "#fabada", fillOpacity:1}]
});
context.closeGroup();
context.addBool({
groupId: groupId,
type: "union"
});
context.closeBoard();
context.closeFile();
}
{
let result = await penpot.exportAsBytes(context);
await writeFile("sample-bool.zip", result);
}
})()
.catch((cause) => {
console.error(cause);
const innerCause = cause.cause;
if (innerCause) {
console.error("Inner cause:", innerCause);
}
process.exit(-1);
})
.finally(() => {
process.exit(0);
});