AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
# * Copyright (c) 2025 The FreeCAD Project Association *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
# * as published by the Free Software Foundation; either version 2 of *
# * the License, or (at your option) any later version. *
# * for detail see the LICENCE text file. *
# * *
# * This program is distributed in the hope that it will be useful, *
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
# * GNU Library General Public License for more details. *
# * *
# * You should have received a copy of the GNU Library General Public *
# * License along with this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""Provides functions to downgrade objects by different methods.
See also the `upgrade` function.
"""
## @package downgrade
# \ingroup draftfunctions
# \brief Provides functions to downgrade objects by different methods.
## \addtogroup draftfunctions
# @{
import FreeCAD as App
from draftmake import make_copy
from draftutils import utils
from draftutils import params
from draftutils import gui_utils
from draftutils.groups import is_group
from draftutils.messages import _msg
from draftutils.translate import translate
def downgrade(objects, delete=False, force=None):
"""Downgrade the given objects.
This is a counterpart to `upgrade`.
Parameters
----------
objects: Part::Feature or list
A single object to downgrade or a list
containing various such objects.
delete: bool, optional
It defaults to `False`.
If it is `True`, the old objects are deleted, and only the resulting
object is kept.
force: str, optional
It defaults to `None`.
Its value can be used to force a certain method of downgrading.
It can be any of: `'explode'`, `'shapify'`, `'subtr'`, `'splitFaces'`,
`'cut2'`, `'getWire'`, `'splitWires'`, or `'splitCompounds'`.
Returns
-------
tuple
A tuple containing two lists, a list of new objects
and a list of objects to be deleted.
See Also
--------
upgrade
"""
# definitions of actions to perform
def explode(obj):
"""Explode a Draft block or array."""
obj_pl = obj.Placement
# block:
if getattr(obj, "Components", []):
delete_list.append(obj)
for comp in obj.Components:
# Objects in Components are in the same parent group as the block.
comp.Placement = obj_pl.multiply(comp.Placement)
comp.Visibility = True
return True
# array:
if getattr(obj, "Base", None) is None:
return False
if not hasattr(obj, "PlacementList"):
return False
# Array must be added to delete_list before base. See can_be_deleted.
delete_list.append(obj)
base = obj.Base
if not base.Visibility:
# Delete base if it is not visible. The can_be_deleted
# function will check if it is not used elsewhere.
delete_list.append(base)
new_list = []
if getattr(obj, "ExpandArray", False):
delete_list.extend(obj.ElementList)
for lnk in obj.ElementList:
newobj = make_copy.make_copy(base)
newobj.Placement = obj_pl.multiply(lnk.Placement)
newobj.Visibility = True
new_list.append(newobj)
else:
for arr_pl in obj.PlacementList:
newobj = make_copy.make_copy(base)
newobj.Placement = obj_pl.multiply(arr_pl)
newobj.Visibility = True
new_list.append(newobj)
add_to_parent(obj, new_list)
add_list.extend(new_list)
return True
def cut2(objects):
"""Cut 2nd object from 1st."""
newobj = doc.addObject("Part::Cut", "Cut")
newobj.Base = objects[0]
newobj.Tool = objects[1]
format(objects[0], [newobj])
add_to_parent(objects[0], [newobj])
add_list.append(newobj)
return True
def splitCompounds(objects):
"""Split solids contained in compound objects into new objects."""
result = False
for obj in objects:
if not obj.Shape.Solids:
continue
new_list = []
for solid in obj.Shape.Solids:
newobj = doc.addObject("Part::Feature", "Solid")
newobj.Shape = solid
new_list.append(newobj)
format(obj, new_list)
add_to_parent(obj, new_list)
add_list.extend(new_list)
delete_list.append(obj)
result = True
return result
def splitFaces(objects):
"""Split faces contained in objects into new objects."""
result = False
preserveFaceColor = params.get_param("preserveFaceColor")
preserveFaceNames = params.get_param("preserveFaceNames")
for obj in objects:
if not obj.Shape.Faces:
continue
new_list = []
if App.GuiUp and preserveFaceColor and obj.ViewObject:
colors = obj.ViewObject.DiffuseColor
else:
colors = None
label = getattr(obj, "Label", "")
for ind, face in enumerate(obj.Shape.Faces):
newobj = doc.addObject("Part::Feature", "Face")
newobj.Shape = face
format(obj, [newobj])
if App.GuiUp and preserveFaceColor and colors:
# At this point, some single-color objects might have just
# a single value in colors for all faces, so we handle that:
if ind < len(colors):
color = colors[ind]
else:
color = colors[0]
newobj.ViewObject.ShapeColor = color
if preserveFaceNames:
newobj.Label = "{} {}".format(label, newobj.Label)
new_list.append(newobj)
add_to_parent(obj, new_list)
add_list.extend(new_list)
delete_list.append(obj)
result = True
return result
def subtr(objects):
"""Subtract faces from the first one."""
faces = []
done_list = []
for obj in objects:
if obj.Shape.Faces:
faces.extend(obj.Shape.Faces)
done_list.append(obj)
if not faces:
return False
main_face = faces.pop(0)
for face in faces:
main_face = main_face.cut(face)
if main_face.isNull():
return False
newobj = doc.addObject("Part::Feature", "Subtraction")
newobj.Shape = main_face
format(done_list[0], [newobj])
add_to_parent(done_list[0], [newobj])
add_list.append(newobj)
delete_list.extend(done_list)
return True
def getWire(obj):
"""Get the wire from a face object."""
if not obj.Shape.Faces:
return False
new_list = []
for wire in obj.Shape.Faces[0].Wires:
newobj = doc.addObject("Part::Feature", "Wire")
newobj.Shape = wire
new_list.append(newobj)
format(obj, new_list)
add_to_parent(obj, new_list)
add_list.extend(new_list)
delete_list.append(obj)
return True
def splitWires(objects):
"""Split the wires contained in objects into edges."""
result = False
for obj in objects:
if not obj.Shape.Edges:
continue
new_list = []
for edge in obj.Shape.Edges:
newobj = doc.addObject("Part::Feature", "Edge")
newobj.Shape = edge
new_list.append(newobj)
format(obj, new_list)
add_to_parent(obj, new_list)
add_list.extend(new_list)
delete_list.append(obj)
result = True
return result
def _shapify(obj):
"""Wrapper for utils.shapify."""
newobj = utils.shapify(obj, delete=False)
if newobj:
format(obj, [newobj])
add_to_parent(obj, [newobj])
add_list.append(newobj)
delete_list.append(obj)
return True
return False
# helper functions (same as in upgrade.py)
def get_parent(obj):
# Problem with obj.getParent():
# https://github.com/FreeCAD/FreeCAD/issues/19600
parent = obj.getParentGroup()
if parent is not None:
return parent
return obj.getParentGeoFeatureGroup()
def can_be_deleted(obj):
if not obj.InList:
return True
for other in obj.InList:
if is_group(other):
continue
if other.TypeId == "App::Part":
continue
return False
return True
def delete_object(obj):
if utils.is_deleted(obj):
return
parent = get_parent(obj)
if parent is not None and parent.TypeId == "PartDesign::Body":
obj = parent
if not can_be_deleted(obj):
# Make obj invisible instead:
obj.Visibility = False
return
if obj.TypeId == "PartDesign::Body":
obj.removeObjectsFromDocument()
doc.removeObject(obj.Name)
def add_to_parent(obj, new_list):
parent = get_parent(obj)
if parent is None:
if doc.getObject("Draft_Construction"):
# This cludge is required because the make_* commands may
# put new objects in the construction group.
constr_group = doc.getObject("Draft_Construction")
for newobj in new_list:
constr_group.removeObject(newobj)
return
if parent.TypeId == "PartDesign::Body":
# We don't add to a PD Body. We process its placement and
# add to its parent instead.
for newobj in new_list:
newobj.Placement = parent.Placement.multiply(newobj.Placement)
add_to_parent(parent, new_list)
return
for newobj in new_list:
# Using addObject is different from just changing the Group property.
# With addObject the object will be added to the parent group, but if
# that is a normal group, also to that group's parent GeoFeatureGroup,
# if available.
parent.addObject(newobj)
def format(obj, new_list):
for newobj in new_list:
gui_utils.format_object(newobj, obj, ignore_construction=True)
doc = App.ActiveDocument
add_list = []
delete_list = []
result = False
if not isinstance(objects, list):
objects = [objects]
if not objects:
return add_list, delete_list
# analyzing objects
solids = []
faces = []
edges = []
onlyedges = True
parts = []
for obj in objects:
if hasattr(obj, "Shape"):
for solid in obj.Shape.Solids:
solids.append(False)
for face in obj.Shape.Faces:
faces.append(face)
for edge in obj.Shape.Edges:
edges.append(edge)
if obj.Shape.ShapeType != "Edge":
onlyedges = False
parts.append(obj)
objects = parts
if not objects:
result = False
elif force:
# functions that work on a single object:
single_funcs = {"explode": explode, "getWire": getWire, "shapify": _shapify}
# functions that work on multiple objects:
multi_funcs = {
"cut2": cut2,
"splitCompounds": splitCompounds,
"splitFaces": splitFaces,
"splitWires": splitWires,
"subtr": subtr,
}
if force in single_funcs:
result = any([single_funcs[force](obj) for obj in objects])
elif force in multi_funcs:
result = multi_funcs[force](objects)
else:
_msg(translate("draft", "Downgrade: Unknown force method:") + " " + force)
result = False
else:
parent = get_parent(objects[0])
same_parent = True
same_parent_type = getattr(parent, "TypeId", "") # "" for global space.
if len(objects) > 1:
for obj in objects[1:]:
if get_parent(obj) != parent:
same_parent = False
same_parent_type = None
break
# we have a block, we explode it
if len(objects) == 1 and utils.get_type(objects[0]) == "Block":
result = explode(objects[0])
if result:
_msg(translate("draft", "Found 1 block: exploding it"))
# we have an array, we explode it
elif len(objects) == 1 and "Array" in utils.get_type(objects[0]):
result = explode(objects[0])
if result:
_msg(translate("draft", "Found 1 array: exploding it"))
# special case, we have one parametric object: we "de-parametrize" it
elif (
len(objects) == 1
and hasattr(objects[0], "Shape")
and (
hasattr(objects[0], "Base")
or hasattr(objects[0], "Profile")
or hasattr(objects[0], "Sections")
)
):
result = _shapify(objects[0])
if result:
_msg(translate("draft", "Found 1 parametric object: breaking its dependencies"))
# we have one multi-solids compound object: extract its solids
elif len(objects) == 1 and hasattr(objects[0], "Shape") and len(solids) > 1:
result = splitCompounds(objects)
if result:
_msg(translate("draft", "Found 1 multi-solids compound: exploding it"))
# we have only 2 objects: cut 2nd from 1st
elif len(objects) == 2 and same_parent and same_parent_type != "PartDesign::Body":
result = cut2(objects)
if result:
_msg(translate("draft", "Found 2 objects: subtracting them"))
elif len(faces) > 1:
# one object with several faces: split it
if len(objects) == 1:
result = splitFaces(objects)
if result:
_msg(translate("draft", "Found several faces: splitting them"))
# several objects: remove all the faces from the first one
elif same_parent and same_parent_type != "PartDesign::Body":
result = subtr(objects)
if result:
_msg(
translate(
"draft", "Found several faces: subtracting them from the first one"
)
)
# only one face: we extract its wires
elif len(faces) > 0:
result = getWire(objects[0])
if result:
_msg(translate("draft", "Found 1 face: extracting its wires"))
# no faces: split wire into single edges
elif not onlyedges:
result = splitWires(objects)
if result:
_msg(translate("draft", "Found only wires: extracting their edges"))
# no result has been obtained
if not result:
_msg(translate("draft", "Unable to downgrade these objects"))
if delete:
for obj in delete_list:
delete_object(obj)
gui_utils.select(add_list)
return add_list, delete_list
## @}