* BIM: add Wall options task box to ArchWall's edit task panel
* BIM: update Align property live on the task box
* BIM: make wall options cancellable
* BIM: make the Component task box actions reversible, in particular debasing a wall
* BIM: implement baseless walls creation (#24595)
* BIM: Implement smart base removal for Walls
Previously, removing the Base object from an Arch Wall would cause the
wall to reset its position to the document origin and could lead to
unintended geometric changes for complex walls.
This commit introduces a "smart debasing" mechanism integrated into the
Component Task Panel's "Remove" button:
- For walls based on a single straight line, the operation now preserves
the wall's global position and parametric `Length`, making it an
independent object.
- For walls with complex bases (multi-segment, curved), a warning dialog
is now presented to the user, explaining the consequences (shape
alteration and position reset) before allowing the operation to
proceed.
This is supported by new API functions `Arch.is_debasable()` and
`Arch.debaseWall()`, which contain the core logic for the feature.
Fixes: https://github.com/FreeCAD/FreeCAD/issues/24453
* BIM: Move wall debasing logic into ArchWall proxy
The logic for handling the removal of a wall's base object was previously
implemented directly within the generic `ComponentTaskPanel` in
`ArchComponent.py`. This created a tight coupling, forcing the generic
component UI to have specific knowledge about the `ArchWall` type.
This commit refactors the implementation to follow a more object-oriented
and polymorphic design:
1. A new overridable method, `handleComponentRemoval(subobject)`, has been
added to the base `ArchComponent` proxy class. Its default implementation
maintains the standard removal behavior.
2. The `_Wall` proxy class in `ArchWall.py` now overrides this method. All
wall-specific debasing logic, including the eligibility check and the
user-facing warning dialog, now resides entirely within this override.
3. The `ComponentTaskPanel.removeElement` method has been simplified. It is
now a generic dispatcher that calls `handleComponentRemoval` on the
proxy of the object being edited, with no specific knowledge of object types.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* BIM: Add interactive creation of baseless walls
Introduce a new workflow for creating Arch Walls without a dependency on
a baseline object (e.g., a Draft Line).
- The `Arch_Wall` command is enhanced with a "No baseline" mode, controlled
by a new "Walls baseline" preference, allowing users to create
placement-driven walls directly in the 3D view.
- The existing `debaseWall` function has been refactored for correctness
and consistency with the new baseless wall geometry.
Co-authored-by: Yorik van Havre <yorik@uncreated.net>
* BIM: Refactor structure of the Arch Wall command
Refactor the `Arch_Wall` GUI command (`BimWall.py`) for improved
readability, maintainability, and architectural clarity.
- A `WallBaselineMode` Enum is introduced to replace the original
integer values, making the code self-documenting.
- The monolithic `create_wall` method is broken down into smaller,
single-responsibility helper functions for each creation mode.
- The `addDefault` method has been removed, with its logic
integrated into the new structure.
* BIM: Add Draft Stretch support for baseless walls
This commit makes the new baseless Arch Walls graphically editable using
the `Draft_Stretch` tool.
- An API for stretching (`calc_endpoints` and `set_from_endpoints`)
has been added to the `ArchWall` proxy.
- The `Draft_Stretch` tool is now aware of baseless walls and calls this
new proxy API to perform the stretch operation, enabling users to
stretch them.
Co-authored-by: Yorik van Havre <yorik@uncreated.net>
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* BIM: address CodeQL warnings
* BIM: Fix wall alignment for GUI creation of baseless walls
Fixes an issue whereby creating a baseless wall from the GUI would
ignore the selected `Align` property, always resulting in a
center-aligned wall.
- The underlying geometry generation for baseless walls now correctly
honors the `Align` property passed by the GUI and API.
- To ensure predictable behavior, the implementation uses the same
geometric convention as walls built from a base object, making the
`Align` property work uniformly for all wall types.
- This also corrects the behavior of the `Arch.makeWall` function for
baseless walls.
- Update unit tests to test wall alignment.
* BIM: Refactor wall geometry generation for improved clarity and maintainability
Improves the internal logic for wall geometry creation, addressing CodeQL warnings and enhancing overall maintainability without changing external behavior.
- The `build_base_from_scratch` method is refactored to unify the separate logic paths for single- and multi-layer walls, reducing code duplication.
- A local helper function is introduced to create face geometry, for better modularity and readability.
- In the `_Wall.execute` method, the control flow that relied on implicit type checking is replaced with an explicit strategy pattern for fusing solids, making the logic more robust.
- Variable names are made more descriptive.
- A NumPy-style docstring is added to better document the function.
* Draft: fix stretching of rotated baseless walls
* BIM: add unit test for stretching baseless walls
* BIM: add regression tests for working-plane-relative coordinates and reuse of base sketches
* BIM: Fix baseless wall creation to respect the working plane
Corrects an issue where baseless walls were created using global
coordinates instead of being relative to the active Draft working plane.
The calculated local placement of the wall is now correctly transformed
into the global coordinate system by multiplying it with the working
plane's placement.
* BIM: Ensure unique baselines for subsequent wall creation
Fixes a bug where creating multiple walls with baselines would
incorrectly reuse the same underlying Sketch or Draft Line object.
The object retrieval logic after the `doCommand` call now correctly uses
`ActiveObject` to get a reliable reference to the new object instead of
relying on a hardcoded name.
* BIM: Make the wall's base object label translatable
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* BIM: use singular for consistency with other labels
Co-authored-by: Roy-043 <70520633+Roy-043@users.noreply.github.com>
* Fix typo
* BIM: address reviewer's comments about improving object reference passing between Python and FreeCAD contexts, and functions
* BIM: remove defensive programming: the callback is only executed as a result of a user's GUI action
* BIM: use the params API to define WallBaseline parameter
* BIM: add Arch Wall tests for joining wall logic
* BIM: add joining logic
* BIM: re-add ArchSketch support
* BIM: re-add multimaterial support on wall creation
* BIM: address CodeQL warning, remove module duplication
* BIM: fix check for SketchArch module when creating sketch-based walls
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Yorik van Havre <yorik@uncreated.net>
Co-authored-by: Roy-043 <70520633+Roy-043@users.noreply.github.com>
* CI: upgrade Ubuntu native build to 24.04
* CI: reenable Ubuntu native build
* CI: abandon PySide6 pip approach, try KDE Neon repos
* BIM: fix mistake in casting return value
* BIM: add wall label to better identify transaction in the Undo stack
* BIM: remove superfluous statement
* BIM: add explanatory comment about additional transaction pending flag
* BIM: Refactor Wall transaction logic to use explicit argument passing
This replaces the `Proxy.InTransaction` flag mechanism with a cleaner
transaction architecture based on explicit argument passing.
The motivation for managing the transaction mechanism is that
previously, removing a Wall's base object via the Task Panel triggered
`debaseWall`, which opened and committed its own transaction
immediately. This nested commit finalized the document state
prematurely, rendering the Task Panel's "Cancel" button ineffective.
The Task Panel now owns the transaction lifecycle for the editing
session. It propagates a `manage_transaction=False` flag down to the
logic layer, preventing nested transactions from committing prematurely.
Key changes:
- Arch.py: `debaseWall` now accepts `manage_transaction` (default True).
Setting it to False allows the Task Panel to disable the nested
transaction and include the debasing operation into its own
transaction context instead.
- ArchComponent.py:
- `ComponentTaskPanel` now manages the transaction lifecycle (Open in
init, Commit in accept, Abort in reject).
- `ComponentTaskPanel.removeElement` now passes
`manage_transaction=False` when calling the object's
`handleComponentRemoval` proxy method.
- Updated `Component.handleComponentRemoval` signature to accept the
`manage_transaction` argument.
- ArchWall.py:
- Updated `_Wall.handleComponentRemoval` to pass the
`manage_transaction` flag to `debaseWall`.
- Cleaned up `WallTaskPanel` by removing the deprecated
`InTransaction` logic and redundant overrides.
* BIM: provide immediate visual feedback on additions and subtractions
* BIM: remove low-level API transaction management
* BIM: Refactor transaction management in ComponentTaskPanel
The C++ GUI layer implicitly manages the transaction lifecycle when
entering and exiting edit mode.
Explicitly opening a transaction in init and committing in accept() is
redundant, as the backend establishes the transaction name and performs
the final commit during the resetEdit() cleanup phase.
The reject() method retains an explicit abortTransaction() call to
signal a rollback, which prevents the backend from committing session
changes by default.
Docstrings are added to clarify this implicit interaction between the
Python UI and the C++ document management logic.
* Wall Options in title case
* QtGui.QApplication.translate -> translate
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Yorik van Havre <yorik@uncreated.net>
Co-authored-by: Roy-043 <70520633+Roy-043@users.noreply.github.com>
* BIM: add test for horizontal area of tilted cylinders
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Fix typos and use makeComponent instead of makeStructure
* Change makeComponent back to makeStructure
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
The icon logic in _ViewProviderWall.getIcon previously switched to the
assembly icon for any wall without a Base that contained Additions. This
caused an unexpected icon change when a wall was debased, as it still
retains a parametric body defined by its Length property.
Update the logic to only show the assembly icon if the wall has no Base,
has Additions, and its Length is zero. This ensures that baseless walls
driven by their own dimensions maintain the standard wall icon.
* Base: Add TimeTracker
This adds TimeTracker utility that can be used to orchestrate code
with time measurements in an easy way and detect performance issues.
* Update src/Base/TimeInfo.h
Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
---------
Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
When a child (e.g. a Wall) of an ArchBuildingPart (e.g. of a Level)
had both 'Move With Host' and 'Move Base' enabled, it failed to move
the base (e.g. Line) of the child, and only displaced the child
itself (effectively ignoring the 'Move Base' setting).
Example project structure:
Level
|
+- Wall ('Move With Host' = true, 'Move Base' = true)
|
+- (base of Wall) Line
Since we are linking to boost anyway, use boost function for case
insensitive string compare. While there, make affected methods accept
std::string arguments, which simplifies code a bit.
It can happen that TransactionFactory::createTransaction() fails to create a transaction object because an unsuitable type is passed
(like BadType) and returns a null pointer.
The calling instances (Transaction::addObjectChange, Transaction::addObjectDel, Transaction::addObjectNew, Transaction::addOrRemoveProperty)
do not check for a null pointer and thus cause a segmentation fault by dereferencing it.
To fix the issue change the above methods to explicitly handle a null pointer.
This fixes issue 21095.
Note: In this case it's caused by the class ViewProviderFace which on purpose isn't added to the type system so that its type will be BadType.
If the property is added to a view provider then in debug mode this will
cause a crash while in release mode this will cause undefined behaviour
because of a static_cast.
To fix crashes remove the assert() and replace the static_cast with
freecad_cast.
Put the reading of each expression of an object into a try/catch block. This is to avoid that all expressions of an object may be lost.
This mitigates the issue 19866
- FileOptionsDialog is only used in the 'save screenshot / save image'
codepath, and currently it doesn't set a directory to it defaults to
process CWD, which is surely not what the user wants.
- The load image codepath uses a custom QFileDialog instead of the
helper code in FileDialog, there doesn't seem to be a great reason for
this, although the helper doesn't have support for mime types it seems.
- I haven't changed the dialogs to 'set/store' the working directory. I'm
not sure this is desired as this isn't a 'normal' save/load operation.
- It would be ideal to remove the FileOptionsDialog in future. The
extended functionality could be turned into a separate dialog which
opens before or after the file chooser. This would allow native dialogs
to be used and would unify the code paths for file selection.
- Further, it would be nice to just merge at least the load image into
the existing import/open option, and to put 'save image' next to
'export', but those changes may be much more time consuming.
For now I've kept it simple. Let's see what others think.
* Menu text "Align Horizontal/Vertical Chain Dimensions" to match "Align Oblique Chain Dimensions"
* Menu text "Centerline Between 2 Faces" is not correct.
* Update button text "Replace References With Current Selection" to title case
* Change task panel title from 'Add offset vertex' to 'Offset Vertex'
* Correct text casing for 'Pick points' button
* Correct text casing for 'Pick points' and 'Edit points'
* Fix capitalization of task panel section title
* Correct text case for 'Escape Picking' button
* Update texts for centerline buttons to match menu texts
This commit addresses two regressions in the MultiCommon feature:
1. Computation Logic: Fixed an issue where the common operation was
calculated as the intersection of the first shape with the union of
the rest (the default behavior of makeElementBoolean). It now
correctly computes the intersection of all shapes sequentially.
2. Compound Handling: Added logic to expand a single compound input
into its constituent shapes. Previously, a compound was treated
as a single entity, leading to incorrect intersection results.
To maintain backward compatibility, a hidden 'Behavior' property is
introduced. This ensures that documents created in FreeCAD 1.0, which
rely on the previous behavior, continue to render as originally
intended while new objects use the corrected logic.
* Part: Fix mirror() regression with non-identity Placement
The makeElementMirror() function incorrectly extracted and pre-multiplied
the shape's Location with the mirror transform. Since BRepBuilderAPI_Transform
already handles shapes with Location correctly, this resulted in the placement
being applied twice, producing incorrect results for shapes with non-identity
Placement.
Fixes#20834
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Part: Add regression test for mirror() with Placement
Adds testTopoShapeMirrorWithPlacement to verify that mirror() produces
identical results regardless of whether the shape is positioned via
direct coordinates or via Placement.
This test would have caught the bug fixed in the previous commit where
shapes with non-identity Placement produced incorrect mirror results.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Fix regression test for mirror with Placement
The test was incorrectly trying to create equivalent boxes using
different methods that don't actually produce the same geometry.
Part.makeBox with a direction vector is not equivalent to setting
a Placement with rotation.
Fixed to use the correct approach that demonstrates the actual bug:
- Method 1: Box with geometry at (0,30,0), identity Placement
- Method 2: Box with geometry at origin, moved via Placement
Both should produce identical mirror results, which they now do
with the makeElementMirror() fix.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Timothy Miller <theosib@zeromark.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>