Merge branch 'master' into pathpocketshape_fix

This commit is contained in:
Russell Johnson
2019-06-15 22:06:01 -05:00
committed by GitHub
14 changed files with 288 additions and 24 deletions

View File

@@ -258,10 +258,16 @@ class CommandAddonManager:
from PySide import QtGui
self.repos.append(addon_repo)
if addon_repo[2] == 1 :
self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"),str(addon_repo[0]) + str(" ("+translate("AddonsInstaller","Installed")+")")))
import AddonManager_rc
addonicon = QtGui.QIcon(":/icons/" + addon_repo[0] + "_workbench_icon.svg")
if addonicon.isNull():
addonicon = QtGui.QIcon(":/icons/Group.svg")
if addon_repo[2] == 1:
item = QtGui.QListWidgetItem(addonicon,str(addon_repo[0]) + str(" ("+translate("AddonsInstaller","Installed")+")"))
item.setBackground(QtGui.QBrush(QtGui.QColor(0,182,41)))
self.dialog.listWorkbenches.addItem(item)
else:
self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/Group.svg"),str(addon_repo[0])))
self.dialog.listWorkbenches.addItem(QtGui.QListWidgetItem(addonicon,str(addon_repo[0])))
def show_information(self, label):
@@ -358,7 +364,9 @@ class CommandAddonManager:
from PySide import QtGui
self.macros.append(macro)
if macro.is_installed():
self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"), macro.name + str(' (Installed)')))
item = QtGui.QListWidgetItem(QtGui.QIcon(":/icons/applications-python.svg"), macro.name + str(' (Installed)'))
item.setBackground(QtGui.QBrush(QtGui.QColor(0,182,41)))
self.dialog.listMacros.addItem(item)
else:
self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/applications-python.svg"),macro.name))
@@ -400,15 +408,13 @@ class CommandAddonManager:
"shows or hides the progress bar"
if state == True:
self.dialog.listWorkbenches.setEnabled(False)
self.dialog.listMacros.setEnabled(False)
self.dialog.tabWidget.setEnabled(False)
self.dialog.buttonInstall.setEnabled(False)
self.dialog.buttonUninstall.setEnabled(False)
self.dialog.progressBar.show()
else:
self.dialog.progressBar.hide()
self.dialog.listWorkbenches.setEnabled(True)
self.dialog.listMacros.setEnabled(True)
self.dialog.tabWidget.setEnabled(True)
if not (self.firsttime and self.firstmacro):
self.dialog.buttonInstall.setEnabled(True)
self.dialog.buttonUninstall.setEnabled(True)
@@ -535,7 +541,7 @@ class CommandAddonManager:
wb[2] = 0
for macro in self.macros:
if macro.is_installed():
self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/button_valid.svg"), macro.name + " ("+translate("AddonsInstaller","Installed")+")"))
self.dialog.listMacros.addItem(item)
else:
self.dialog.listMacros.addItem(QtGui.QListWidgetItem(QtGui.QIcon(":/icons/applications-python.svg"),+macro.name))
@@ -548,7 +554,7 @@ class CommandAddonManager:
w = self.dialog.listWorkbenches.item(i)
if w.text().startswith(str(repo)):
w.setText(str(repo) + str(" ("+translate("AddonsInstaller","Update available")+")"))
w.setIcon(QtGui.QIcon(":/icons/debug-marker.svg"))
w.setBackground(QtGui.QBrush(QtGui.QColor(182,90,0)))
if not repo in self.doUpdate:
self.doUpdate.append(repo)

View File

@@ -1,3 +1,7 @@
IF (BUILD_GUI)
PYSIDE_WRAP_RC(AddonManager_QRC_SRCS Resources/AddonManager.qrc)
ENDIF (BUILD_GUI)
SET(AddonManager_SRCS
Init.py
InitGui.py
@@ -12,14 +16,24 @@ SET(AddonManager_SRCS
SOURCE_GROUP("" FILES ${AddonManager_SRCS})
ADD_CUSTOM_TARGET(AddonManager ALL
SOURCES ${AddonManager_SRCS}
SOURCES ${AddonManager_SRCS} ${AddonManager_QRC_SRCS}
)
fc_copy_sources(AddonManager "${CMAKE_BINARY_DIR}/Mod/AddonManager" ${AddonManager_SRCS})
IF (BUILD_GUI)
fc_target_copy_resource(AddonManager
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_BINARY_DIR}/Mod/AddonManager
AddonManager_rc.py)
ENDIF (BUILD_GUI)
INSTALL(
FILES
${AddonManager_SRCS}
${AddonManager_QRC_SRCS}
DESTINATION
Mod/AddonManager
)

View File

@@ -0,0 +1,31 @@
<RCC>
<qresource>
<file>icons/A2plus_workbench-icon.svg</file>
<file>icons/Airplane_workbench-icon.svg</file>
<file>icons/Arch_Textures_workbench_icon.svg</file>
<file>icons/BIM_workbench_icon.svg</file>
<file>icons/BOLTS_workbench_icon.svg</file>
<file>icons/cfd_workbench_icon.svg</file>
<file>icons/Curves_workbench_icon.svg</file>
<file>icons/Defeaturing_workbench_icon.svg</file>
<file>icons/Dodo_workbench_icon.svg</file>
<file>icons/EM_workbench_icon.svg</file>
<file>icons/ExplodedAssembly_workbench_icon.svg</file>
<file>icons/Fasteners_workbench_icon.svg</file>
<file>icons/Flamingo_workbench_icon.svg</file>
<file>icons/GDT_workbench_icon.svg</file>
<file>icons/Gears_workbench_icon.svg</file>
<file>icons/Glider_workbench_icon.svg</file>
<file>icons/Kicad-StepUp-tools-workbench_icon.svg</file>
<file>icons/Lattice2_workbench_icon.svg</file>
<file>icons/Lithophane_workbench_icon.svg</file>
<file>icons/Manipulator_workbench_icon.svg</file>
<file>icons/PartOMagic_workbench_icon.svg</file>
<file>icons/Plot_workbench_icon.svg</file>
<file>icons/Pyrate_workbench_icon.svg</file>
<file>icons/Reporting_workbench_icon.svg</file>
<file>icons/SheetMetal_workbench_icon.svg</file>
<file>icons/Ship_workbench_icon.svg</file>
<file>icons/Timber_workbench_icon.svg</file>
</qresource>
</RCC>

View File

@@ -97,6 +97,17 @@ def urlopen(url):
return u
def getserver(url):
"""returns the server part of an url"""
if sys.version_info.major < 3:
from urlparse import urlparse
else:
from urllib.parse import urlparse
return '{uri.scheme}://{uri.netloc}/'.format(uri=urlparse(url))
def update_macro_details(old_macro, new_macro):
"""Update a macro with information from another one

View File

@@ -35,6 +35,7 @@ from addonmanager_macro import Macro
from addonmanager_utilities import urlopen
from addonmanager_utilities import translate
from addonmanager_utilities import symlink
from addonmanager_utilities import getserver
MACROS_BLACKLIST = ["BOLTS","WorkFeatures","how to install","PartsLibrary","FCGear"]
OBSOLETE = ["assembly2","drawing_dimensioning","cura_engine"] # These addons will print an additional message informing the user
@@ -382,12 +383,12 @@ class ShowWorker(QtCore.QThread):
self.info_label.emit( message )
self.progressbar_show.emit(False)
l = self.loadImages( message )
l = self.loadImages( message, url )
if l:
self.info_label.emit( l )
self.stop = True
def loadImages(self,message):
def loadImages(self,message,url):
"checks if the given page contains images and downloads them"
@@ -402,6 +403,11 @@ class ShowWorker(QtCore.QThread):
if not os.path.exists(store):
os.makedirs(store)
for path in imagepaths:
if "?" in path:
# remove everything after the ?
path = path.split("?")[0]
if not path.startswith("http"):
path = getserver(url) + path
name = path.split("/")[-1]
if name and path.startswith("http"):
storename = os.path.join(store,name)
@@ -426,7 +432,7 @@ class ShowWorker(QtCore.QThread):
pix = pix.fromImage(img.scaled(300,300,QtCore.Qt.KeepAspectRatio,QtCore.Qt.FastTransformation))
pix.save(storename, "jpeg",100)
message = message.replace(path,"file://"+storename.replace("\\","/"))
message = message.replace(path,"file:///"+storename.replace("\\","/"))
return message
return None
@@ -547,7 +553,7 @@ class InstallWorker(QtCore.QThread):
try:
answer = repo.pull()
except:
print("Error updating module",repos[idx][1]," - Please fix manually")
print("Error updating module",self.repos[idx][1]," - Please fix manually")
answer = repo.status()
print(answer)
else:

View File

@@ -58,6 +58,15 @@ class _Fence(ArchComponent.Component):
self.Type = "Fence"
def __getstate__(self):
return (self.sectionFaceNumbers)
def __setstate__(self, state):
if state is not None and isinstance(state, tuple):
self.sectionFaceNumbers = state[0]
return None
def execute(self, obj):
import Part
@@ -97,7 +106,7 @@ class _Fence(ArchComponent.Component):
obj, pathwire, downRotation)
postShapes = self.calculatePosts(obj, postPlacements)
sectionShapes = self.calculateSections(
sectionShapes, sectionFaceNumbers = self.calculateSections(
obj, postPlacements, postLength, sectionLength)
allShapes = []
@@ -106,6 +115,8 @@ class _Fence(ArchComponent.Component):
compound = Part.makeCompound(allShapes)
self.sectionFaceNumbers = sectionFaceNumbers
self.applyShape(obj, compound, obj.Placement,
allowinvalid=True, allownosolid=True)
@@ -146,6 +157,11 @@ class _Fence(ArchComponent.Component):
shapes = []
# For the colorization algorithm we have to store the number of faces for each section
# It is possible that a section is clipped. Then the number of faces is not equals to the
# number of faces in the original section
faceNumbers = []
for i in range(obj.NumberOfSections):
startPlacement = postPlacements[i]
endPlacement = postPlacements[i + 1]
@@ -175,8 +191,9 @@ class _Fence(ArchComponent.Component):
sectionCopy.Placement = placement
shapes.append(sectionCopy)
faceNumbers.append(len(sectionCopy.Faces))
return shapes
return (shapes, faceNumbers)
def clipSection(self, shape, length, clipLength):
import Part
@@ -189,14 +206,12 @@ class _Fence(ArchComponent.Component):
FreeCAD.Vector(boundBox.XMin, boundBox.YMin, boundBox.ZMin))
rightBox = Part.makeBox(halfLengthToCut, boundBox.YMax + 1, boundBox.ZMax + 1,
FreeCAD.Vector(boundBox.XMin + halfLengthToCut + clipLength, boundBox.YMin, boundBox.ZMin))
newShape = shape.cut([leftBox, rightBox])
newBoundBox = newShape.BoundBox
newShape.translate(FreeCAD.Vector(-newBoundBox.XMin, 0, 0))
print(newShape.BoundBox)
return newShape.removeSplitter()
def calculatePathWire(self, obj):
@@ -214,6 +229,17 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent):
def __init__(self, vobj):
ArchComponent.ViewProviderComponent.__init__(self, vobj)
self.setProperties(vobj)
def setProperties(self, vobj):
pl = vobj.PropertiesList
if not "UseOriginalColors" in pl:
vobj.addProperty("App::PropertyBool", "UseOriginalColors", "Fence", QT_TRANSLATE_NOOP(
"App::Property", "When true, the fence will be colored like the original post and section."))
def onDocumentRestored(self, vobj):
self.setProperties(vobj)
def getIcon(self):
import Arch_rc
@@ -234,6 +260,93 @@ class _ViewProviderFence(ArchComponent.ViewProviderComponent):
return children
def updateData(self, obj, prop):
colorProps = ["Shape", "Section", "Post", "Path"]
if prop in colorProps:
self.applyColors(obj)
else:
super().updateData(obj, prop)
def onChanged(self, vobj, prop):
if prop == "UseOriginalColors":
self.applyColors(vobj.Object)
else:
super().onChanged(vobj, prop)
def applyColors(self, obj):
if not hasattr(obj.ViewObject, "UseOriginalColors") or not obj.ViewObject.UseOriginalColors:
obj.ViewObject.DiffuseColor = [obj.ViewObject.ShapeColor]
else:
post = obj.Post
section = obj.Section
numberOfPostFaces = len(post.Shape.Faces)
numberOfSectionFaces = len(section.Shape.Faces)
if hasattr(obj.Proxy, 'sectionFaceNumbers'):
sectionFaceNumbers = obj.Proxy.sectionFaceNumbers
else:
sectionFaceNumbers = [0]
if numberOfPostFaces == 0 or sum(sectionFaceNumbers) == 0:
return
postColors = self.normalizeColors(post, numberOfPostFaces)
defaultSectionColors = self.normalizeColors(
section, numberOfSectionFaces)
ownColors = []
# At first all posts are added to the shape
for i in range(obj.NumberOfPosts):
ownColors.extend(postColors)
# Next all sections are added
for i in range(obj.NumberOfSections):
actualSectionFaceCount = sectionFaceNumbers[i]
if actualSectionFaceCount == numberOfSectionFaces:
ownColors.extend(defaultSectionColors)
else:
ownColors.extend(self.normalizeColors(
section, actualSectionFaceCount))
viewObject = obj.ViewObject
viewObject.DiffuseColor = ownColors
def normalizeColors(self, obj, numberOfFaces):
colors = obj.ViewObject.DiffuseColor
if obj.TypeId == 'PartDesign::Body':
# When colorizing a PartDesign Body we have two options
# 1. The whole body got a shape color, that means the tip has only a single diffuse color set
# so we use the shape color of the body
# 2. "Set colors" was called on the tip and the individual faces where colorized.
# We use the diffuseColors of the tip in that case
tipColors = obj.Tip.ViewObject.DiffuseColor
if len(tipColors) > 1:
colors = tipColors
numberOfColors = len(colors)
if numberOfColors == 1:
return colors * numberOfFaces
colorsToUse = colors.copy()
if numberOfColors == numberOfFaces:
return colorsToUse
else:
# It is possible, that we have less faces than colors when something got clipped.
# Remove the unneeded colors at the beginning and end
halfNumberOfFacesToRemove = (numberOfColors - numberOfFaces) / 2
start = int(math.ceil(halfNumberOfFacesToRemove))
end = start + numberOfFaces
return colorsToUse[start:end]
class _CommandFence:
"the Arch Fence command definition"
@@ -330,12 +443,30 @@ if __name__ == '__main__':
def buildPost():
post = Part.makeBox(100, 100, 1000, FreeCAD.Vector(0, 0, 0))
Part.show(post, "Post")
Part.show(post, 'Post')
return FreeCAD.ActiveDocument.getObject('Post')
def colorizeFaces(o, color=(0.6, 0.0, 0.0, 0.0), faceIndizes=[2]):
numberOfFaces = len(o.Shape.Faces)
vo = o.ViewObject
originalColors = vo.DiffuseColor
if len(originalColors) == 1:
newColors = originalColors * numberOfFaces
else:
newColors = originalColors.copy()
for i in faceIndizes:
newColors[i] = color
vo.DiffuseColor = newColors
section = buildSection()
path = buildPath()
post = buildPost()
colorizeFaces(post)
print(makeFence(section, post, path))

View File

@@ -45,6 +45,7 @@ def getIfcProduct(IfcType):
name = "IfcBuildingElementProxy"
if name in ArchIFCSchema.IfcProducts:
return ArchIFCSchema.IfcProducts[name]
return None
def getIfcProductAttribute(ifcProduct, name):
@@ -81,6 +82,8 @@ def addIfcProductAttribute(obj, attribute):
"Adds a given attribute property"
if not hasattr(obj,"IfcData"):
return
IfcData = obj.IfcData
if "attributes" not in IfcData:
IfcData["attributes"] = "{}"

View File

@@ -189,7 +189,10 @@ def read(filename):
def export(exportList,filename,tessellation=1,colors=None):
"called when freecad exports a file"
"""export(exportList,filename,tessellation=1,colors=None) -- exports FreeCAD contents to a DAE file.
colors is an optional dictionary of objName:shapeColorTuple or objName:diffuseColorList elements
to be used in non-GUI mode if you want to be able to export colors. Tessellation is used when breaking
curved surfaces into triangles."""
if not checkCollada(): return
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch")

View File

@@ -1466,7 +1466,9 @@ class recycler:
def export(exportList,filename,colors=None):
"exports FreeCAD contents to an IFC file"
"""export(exportList,filename,colors=None) -- exports FreeCAD contents to an IFC file.
colors is an optional dictionary of objName:shapeColorTuple or objName:diffuseColorList elements
to be used in non-GUI mode if you want to be able to export colors."""
getPreferences()

View File

@@ -220,7 +220,7 @@ def export(exportList,filename,colors=None):
outfile = pythonopen(filenamemtl,"w")
outfile.write("# FreeCAD v" + ver[0] + "." + ver[1] + " build" + ver[2] + " Arch module\n")
outfile.write("# http://www.freecadweb.org\n")
kinds = {"AmbientColor":"Ka ","DiffuseColor":"Kd ","SpecularColor":"Ks ","EmissiveColor":"Ke ","Transparency":"d "}
kinds = {"AmbientColor":"Ka ","DiffuseColor":"Kd ","SpecularColor":"Ks ","EmissiveColor":"Ke ","Transparency":"Tr "}
done = [] # store names to avoid duplicates
for mat in materials:
if isinstance(mat,tuple):

View File

@@ -218,6 +218,8 @@ App::DocumentObjectExecReturn *Pad::execute(void)
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
if (!Ex.More())
supportface = TopoDS_Face();
#if 0
BRepFeat_MakePrism PrismMaker;
PrismMaker.Init(base, sketchshape, supportface, dir, 2, 1);
PrismMaker.Perform(upToFace);
@@ -225,6 +227,9 @@ App::DocumentObjectExecReturn *Pad::execute(void)
if (!PrismMaker.IsDone())
return new App::DocumentObjectExecReturn("Pad: Up to face: Could not extrude the sketch!");
prism = PrismMaker.Shape();
#else
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, 2, 1);
#endif
base.Nullify();
} else {
// A support object is always required and we need to use BRepFeat_MakePrism
@@ -239,6 +244,7 @@ App::DocumentObjectExecReturn *Pad::execute(void)
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
if (!Ex.More())
supportface = TopoDS_Face();
#if 0
BRepFeat_MakePrism PrismMaker;
PrismMaker.Init(base, sketchshape, supportface, dir, 2, 1);
PrismMaker.Perform(upToFace);
@@ -246,6 +252,9 @@ App::DocumentObjectExecReturn *Pad::execute(void)
if (!PrismMaker.IsDone())
return new App::DocumentObjectExecReturn("Pad: Up to face: Could not extrude the sketch!");
prism = PrismMaker.Shape();
#else
generatePrism(prism, method, base, sketchshape, supportface, upToFace, dir, 2, 1);
#endif
}
} else {
generatePrism(prism, sketchshape, method, dir, L, L2,

View File

@@ -174,6 +174,7 @@ App::DocumentObjectExecReturn *Pocket::execute(void)
TopExp_Explorer Ex(supportface,TopAbs_WIRE);
if (!Ex.More())
supportface = TopoDS_Face();
#if 0
BRepFeat_MakePrism PrismMaker;
PrismMaker.Init(base, profileshape, supportface, dir, 0, 1);
PrismMaker.Perform(upToFace);
@@ -181,6 +182,10 @@ App::DocumentObjectExecReturn *Pocket::execute(void)
if (!PrismMaker.IsDone())
return new App::DocumentObjectExecReturn("Pocket: Up to face: Could not extrude the sketch!");
TopoDS_Shape prism = PrismMaker.Shape();
#else
TopoDS_Shape prism;
generatePrism(prism, method, base, profileshape, supportface, upToFace, dir, 0, 1);
#endif
// And the really expensive way to get the SubShape...
BRepAlgoAPI_Cut mkCut(base, prism);

View File

@@ -34,6 +34,7 @@
# include <BRep_Tool.hxx>
# include <BRepExtrema_DistShapeShape.hxx>
# include <BRepPrimAPI_MakePrism.hxx>
# include <BRepFeat_MakePrism.hxx>
# include <BRepProj_Projection.hxx>
# include <Geom_Plane.hxx>
# include <TopoDS.hxx>
@@ -544,6 +545,35 @@ void ProfileBased::generatePrism(TopoDS_Shape& prism,
}
void ProfileBased::generatePrism(TopoDS_Shape& prism,
const std::string& method,
const TopoDS_Shape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& sketchface,
const TopoDS_Face& uptoface,
const gp_Dir& direction,
Standard_Integer Mode,
Standard_Boolean Modify)
{
if (method == "UpToFirst" || method == "UpToFace") {
BRepFeat_MakePrism PrismMaker;
TopoDS_Shape base = baseshape;
TopoDS_Face supportface = sketchface;
for (TopExp_Explorer xp(profileshape, TopAbs_FACE); xp.More(); xp.Next()) {
PrismMaker.Init(base, xp.Current(), supportface, direction, Mode, Modify);
PrismMaker.Perform(uptoface);
if (!PrismMaker.IsDone())
throw Base::RuntimeError("ProfileBased: Up to face: Could not extrude the sketch!");
base = PrismMaker.Shape();
if (Mode == 2)
Mode = 1;
}
prism = base;
}
}
bool ProfileBased::checkWireInsideFace(const TopoDS_Wire& wire, const TopoDS_Face& face,
const gp_Dir& dir) {
// Project wire onto the face (face, not surface! So limits of face apply)

View File

@@ -134,6 +134,19 @@ protected:
const double L2,
const bool midplane,
const bool reversed);
/**
* Generate a linear prism
* It will be a stand-alone solid created with BRepFeat_MakePrism
*/
static void generatePrism(TopoDS_Shape& prism,
const std::string& method,
const TopoDS_Shape& baseshape,
const TopoDS_Shape& profileshape,
const TopoDS_Face& sketchface,
const TopoDS_Face& uptoface,
const gp_Dir& direction,
Standard_Integer Mode,
Standard_Boolean Modify);
/// Check whether the wire after projection on the face is inside the face
static bool checkWireInsideFace(const TopoDS_Wire& wire,