Merge branch 'master' of ssh://free-cad.git.sourceforge.net/gitroot/free-cad/free-cad into freecad-ship

This commit is contained in:
Jose Luis Cercós pita 2012-02-10 14:50:09 +01:00
commit b8e56edc60
27 changed files with 52518 additions and 13552 deletions

1
.gitattributes vendored
View File

@ -11,5 +11,6 @@ mkinstalldirs export-ignore
package export-ignore
fc.sh export-ignore
UpdateResources.bat export-ignore
BuildVersion.bat export-ignore
*.sln export-ignore
WindowsInstaller export-ignore

View File

@ -467,3 +467,26 @@ if(FREECAD_MAINTAINERS_BUILD AND NOT WIN32)
#ADD_CUSTOM_TARGET(DIST make package_source)
endif(FREECAD_MAINTAINERS_BUILD AND NOT WIN32)
add_custom_target(dist-git
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py
--srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(distdfsg-git
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py
--srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --dfsg
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
if(CMAKE_COMPILER_IS_GNUCXX OR MINGW)
add_custom_target(distcheck-git
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py
--srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --check
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_custom_target(distcheckdfsg-git
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/src/Tools/makedist.py
--srcdir=${CMAKE_SOURCE_DIR} --bindir=${CMAKE_BINARY_DIR} --dfsg --check
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endif(CMAKE_COMPILER_IS_GNUCXX OR MINGW)

View File

@ -22,9 +22,13 @@ elseif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE7X)
elseif(FREECAD_BUILD_GUI)
find_path(COIN_VERSION3 Inventor/scxml/ScXML.h ${COIN3D_INCLUDE_DIR})
if (COIN_VERSION3)
add_subdirectory(Pivy-0.5)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy-0.5)
add_subdirectory(Pivy-0.5)
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy-0.5)
else (COIN_VERSION3)
add_subdirectory(Pivy)
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy)
add_subdirectory(Pivy)
endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Pivy)
endif(COIN_VERSION3)
endif(FREECAD_BUILD_GUI AND FREECAD_LIBPACK_CHECKFILE6X)

View File

@ -1028,12 +1028,18 @@ void Application::initConfig(int argc, char ** argv)
// only for 'BuildVersionMajor'.
if (App::Application::Config().find("BuildVersionMajor") == App::Application::Config().end()) {
std::stringstream str; str << FCVersionMajor << "." << FCVersionMinor;
App::Application::Config()["ExeVersion"] = str.str();
App::Application::Config()["BuildVersionMajor"] = FCVersionMajor;
App::Application::Config()["BuildVersionMinor"] = FCVersionMinor;
App::Application::Config()["BuildRevision"] = FCRevision;
App::Application::Config()["BuildRepositoryURL"] = FCRepositoryURL;
App::Application::Config()["BuildRevisionDate"] = FCCurrentDateT;
App::Application::Config()["ExeVersion" ] = str.str();
App::Application::Config()["BuildVersionMajor" ] = FCVersionMajor;
App::Application::Config()["BuildVersionMinor" ] = FCVersionMinor;
App::Application::Config()["BuildRevision" ] = FCRevision;
App::Application::Config()["BuildRepositoryURL" ] = FCRepositoryURL;
App::Application::Config()["BuildRevisionDate" ] = FCRevisionDate;
#if defined(FCRepositoryHash)
App::Application::Config()["BuildRevisionHash" ] = FCRepositoryHash;
#endif
#if defined(FCRepositoryBranch)
App::Application::Config()["BuildRevisionBranch"] = FCRepositoryBranch;
#endif
}
_argc = argc;

View File

@ -68,6 +68,7 @@ if(SWIG_FOUND)
add_definitions(-DHAVE_SWIG=1)
endif(SWIG_FOUND)
if (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++)
SET(zipios_SRCS
../zipios++/backbuffer.h
../zipios++/basicentry.cpp
@ -121,6 +122,12 @@ SET(zipios_SRCS
../zipios++/zipoutputstream.h
)
SOURCE_GROUP("zipios" FILES ${zipios_SRCS})
else (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++)
set(FreeCADBase_LIBS
${FreeCADBase_LIBS}
-lzipios
)
endif (EXISTS ${CMAKE_SOURCE_DIR}/src/zipios++)
SET(pycxx_SRCS
../CXX/Config.hxx
@ -148,11 +155,11 @@ SET(FreeCADBase_XML_SRCS
VectorPy.xml
)
SOURCE_GROUP("XML" FILES ${FreeCADBase_XML_SRCS})
set(FreeCADBase_MOC_HDRS
FutureWatcherProgress.h
)
fc_wrap_cpp(FreeCADBase_MOC_SRCS ${FreeCADBase_MOC_HDRS})
set(FreeCADBase_MOC_HDRS
FutureWatcherProgress.h
)
fc_wrap_cpp(FreeCADBase_MOC_SRCS ${FreeCADBase_MOC_HDRS})
SET(FreeCADBase_UNITAPI_SRCS
UnitsApi.cpp

View File

@ -176,8 +176,11 @@ std::string FileInfo::getTempFileName(const char* FileName, const char* Path)
else
std::strcat(buf, "/fileXXXXXX");
/*int id =*/ (void) mkstemp(buf);
//FILE* file = fdopen(id, "w");
int id = mkstemp(buf);
if (id > -1) {
FILE* file = fdopen(id, "w");
fclose(file);
}
return std::string(buf);
#endif
}

View File

@ -1,18 +1,10 @@
/// Version Number
// Version Number
#define FCVersionMajor "0"
#define FCVersionMinor "13"
#define FCVersionName "Vulcan"
#define FCRevision "$WCREV$" //Highest committed revision number
#define FCRevisionDate "$WCDATE$" //Date of highest committed revision
#define FCRevisionRange "$WCRANGE$" //Update revision range
#define FCRepositoryURL "$WCURL$" //Repository URL of the working copy
#define FCCurrentDateT "$WCNOW$" //Current system date & time
//Placeholders of the form "$WCxxx?TrueText:FalseText$" are replaced with
//TrueText if the tested condition is true, and FalseText if false.
#define FCScrClean "$WCMODS?Src modified:Src not modified$" //True if local modifications found
#define FCScrMixed "$WCMIXED?Src mixed:Src not mixed$" //True if mixed update revisions found

View File

@ -16,13 +16,17 @@
<property name="modal">
<bool>true</bool>
</property>
<layout class="QGridLayout">
<property name="margin">
<number>9</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="labelSplashPicture">
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<spacer>
<property name="orientation">
@ -36,15 +40,18 @@
</property>
</spacer>
</item>
<item row="0" column="0" colspan="3">
<widget class="QLabel" name="labelSplashPicture">
<property name="text">
<string/>
<item row="2" column="0">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<property name="sizeHint" stdset="0">
<size>
<width>31</width>
<height>20</height>
</size>
</property>
</widget>
</spacer>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout">
@ -161,6 +168,34 @@
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelBranch">
<property name="text">
<string notr="true">Branch</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLabel" name="labelBuildBranch">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;/head&gt;&lt;body style=&quot; white-space: pre-wrap; font-family:MS Shell Dlg 2; font-size:7.8pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Unknown&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="labelBuildHash">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;/head&gt;&lt;body style=&quot; white-space: pre-wrap; font-family:MS Shell Dlg 2; font-size:7.8pt; font-weight:400; font-style:normal; text-decoration:none;&quot;&gt;&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Unknown&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="labelHash">
<property name="text">
<string notr="true">Hash</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -178,19 +213,6 @@
</property>
</spacer>
</item>
<item row="2" column="0">
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>31</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<spacer>
<property name="orientation">
@ -219,6 +241,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="copyButton">
<property name="text">
<string>Copy to clipboard</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
@ -239,19 +268,6 @@
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>181</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
@ -264,7 +280,7 @@
</customwidget>
</customwidgets>
<resources>
<include location="Icons/resource.qrc"/>
<include location="../../../FreeCAD/src/Gui/Icons/resource.qrc"/>
</resources>
<connections>
<connection>

View File

@ -227,6 +227,10 @@ void MacroManager::run(MacroType eType,const char *sName)
PyErr_Clear();
Base::Interpreter().systemExit();
}
catch (const Base::PyException& e) {
Base::Console().Error("%s%s: %s\n",
e.getStackTrace().c_str(), e.getErrorType().c_str(), e.what());
}
catch (const Base::Exception& e) {
qWarning("%s",e.what());
}

View File

@ -23,8 +23,11 @@
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QApplication>
# include <QClipboard>
# include <QMutex>
# include <QSysInfo>
# include <QTextStream>
# include <QWaitCondition>
#endif
@ -248,18 +251,18 @@ static QString getPlatform()
void AboutDialog::setupLabels()
{
QString exeName = QString::fromAscii(App::Application::Config()["ExeName"].c_str());
std::map<std::string,std::string>& cfg = App::Application::Config();
std::map<std::string,std::string>::iterator it = cfg.find("WindowTitle");
if (it != cfg.end())
std::map<std::string, std::string>& config = App::Application::Config();
QString exeName = QString::fromAscii(config["ExeName"].c_str());
std::map<std::string,std::string>::iterator it = config.find("WindowTitle");
if (it != config.end())
exeName = QString::fromUtf8(it->second.c_str());
QString banner = QString::fromUtf8(App::Application::Config()["ConsoleBanner"].c_str());
QString banner = QString::fromUtf8(config["ConsoleBanner"].c_str());
banner = banner.left( banner.indexOf(QLatin1Char('\n')) );
QString major = QString::fromAscii(App::Application::Config()["BuildVersionMajor"].c_str());
QString minor = QString::fromAscii(App::Application::Config()["BuildVersionMinor"].c_str());
QString build = QString::fromAscii(App::Application::Config()["BuildRevision"].c_str());
QString disda = QString::fromAscii(App::Application::Config()["BuildRevisionDate"].c_str());
QString mturl = QString::fromAscii(App::Application::Config()["MaintainerUrl"].c_str());
QString major = QString::fromAscii(config["BuildVersionMajor"].c_str());
QString minor = QString::fromAscii(config["BuildVersionMinor"].c_str());
QString build = QString::fromAscii(config["BuildRevision"].c_str());
QString disda = QString::fromAscii(config["BuildRevisionDate"].c_str());
QString mturl = QString::fromAscii(config["MaintainerUrl"].c_str());
QString author = ui->labelAuthor->text();
author.replace(QString::fromAscii("Unknown Application"), exeName);
@ -283,10 +286,56 @@ void AboutDialog::setupLabels()
platform.replace(QString::fromAscii("Unknown"),
QString::fromAscii("%1 (%2-bit)").arg(getPlatform()).arg(QSysInfo::WordSize));
ui->labelBuildPlatform->setText(platform);
// branch name
it = config.find("BuildRevisionBranch");
if (it != config.end()) {
QString branch = ui->labelBuildBranch->text();
branch.replace(QString::fromAscii("Unknown"), QString::fromAscii(it->second.c_str()));
ui->labelBuildBranch->setText(branch);
}
else {
ui->labelBranch->hide();
ui->labelBuildBranch->hide();
}
// hash id
it = config.find("BuildRevisionHash");
if (it != config.end()) {
QString hash = ui->labelBuildHash->text();
hash.replace(QString::fromAscii("Unknown"), QString::fromAscii(it->second.c_str()));
ui->labelBuildHash->setText(hash);
}
else {
ui->labelHash->hide();
ui->labelBuildHash->hide();
}
}
void AboutDialog::on_licenseButton_clicked()
{
}
void AboutDialog::on_copyButton_clicked()
{
QString data;
QTextStream str(&data);
std::map<std::string, std::string>& config = App::Application::Config();
std::map<std::string,std::string>::iterator it;
QString major = QString::fromAscii(config["BuildVersionMajor"].c_str());
QString minor = QString::fromAscii(config["BuildVersionMinor"].c_str());
QString build = QString::fromAscii(config["BuildRevision"].c_str());
str << "Version: " << major << "." << minor << "." << build << endl;
it = config.find("BuildRevisionBranch");
if (it != config.end())
str << "Branch: " << it->second.c_str() << endl;
it = config.find("BuildRevisionHash");
if (it != config.end())
str << "Hash: " << it->second.c_str() << endl;
QClipboard* cb = QApplication::clipboard();
cb->setText(data);
}
#include "moc_Splashscreen.cpp"

View File

@ -85,6 +85,7 @@ protected:
protected Q_SLOTS:
virtual void on_licenseButton_clicked();
virtual void on_copyButton_clicked();
private:
Ui_AboutApplication* ui;

View File

@ -278,21 +278,30 @@ TaskDialogPython::TaskDialogPython(const Py::Object& o) : dlg(o)
}
}
else if (dlg.hasAttr(std::string("form"))) {
Py::Object widget(dlg.getAttr(std::string("form")));
Py::Module mainmod(PyImport_AddModule((char*)"sip"));
Py::Callable func = mainmod.getDict().getItem("unwrapinstance");
Py::Tuple arguments(1);
arguments[0] = widget; //PyQt pointer
Py::Object result = func.apply(arguments);
void* ptr = PyLong_AsVoidPtr(result.ptr());
QObject* object = reinterpret_cast<QObject*>(ptr);
if (object) {
QWidget* form = qobject_cast<QWidget*>(object);
if (form) {
Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox(
form->windowIcon().pixmap(32), form->windowTitle(), true, 0);
taskbox->groupLayout()->addWidget(form);
Content.push_back(taskbox);
Py::Object f(dlg.getAttr(std::string("form")));
Py::List widgets;
if (f.isList()) {
widgets = f;
}
else {
widgets.append(f);
}
for (Py::List::iterator it = widgets.begin(); it != widgets.end(); ++it) {
Py::Module mainmod(PyImport_AddModule((char*)"sip"));
Py::Callable func = mainmod.getDict().getItem("unwrapinstance");
Py::Tuple arguments(1);
arguments[0] = *it; //PyQt pointer
Py::Object result = func.apply(arguments);
void* ptr = PyLong_AsVoidPtr(result.ptr());
QObject* object = reinterpret_cast<QObject*>(ptr);
if (object) {
QWidget* form = qobject_cast<QWidget*>(object);
if (form) {
Gui::TaskView::TaskBox* taskbox = new Gui::TaskView::TaskBox(
form->windowIcon().pixmap(32), form->windowTitle(), true, 0);
taskbox->groupLayout()->addWidget(form);
Content.push_back(taskbox);
}
}
}
}

View File

@ -140,6 +140,7 @@ class ComponentTaskPanel:
QtCore.QObject.connect(self.addButton, QtCore.SIGNAL("clicked()"), self.addElement)
QtCore.QObject.connect(self.delButton, QtCore.SIGNAL("clicked()"), self.removeElement)
QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemClicked(QTreeWidgetItem*,int)"), self.check)
QtCore.QObject.connect(self.tree, QtCore.SIGNAL("itemDoubleClicked(QTreeWidgetItem *,int)"), self.editObject)
self.update()
def isAllowedAlterSelection(self):
@ -224,7 +225,21 @@ class ComponentTaskPanel:
if self.obj:
self.obj.ViewObject.finishEditing()
return True
def editObject(self,wid,col):
if wid.parent():
obj = FreeCAD.ActiveDocument.getObject(str(wid.text(0)))
if obj:
self.obj.ViewObject.Transparency = 80
self.obj.ViewObject.Selectable = False
obj.ViewObject.show()
self.accept()
if obj.isDerivedFrom("Sketcher::SketchObject"):
FreeCADGui.activateWorkbench("SketcherWorkbench")
FreeCAD.ArchObserver = ArchSelectionObserver(self.obj,obj)
FreeCADGui.Selection.addObserver(FreeCAD.ArchObserver)
FreeCADGui.ActiveDocument.setEdit(obj.Name,0)
def retranslateUi(self, TaskPanel):
TaskPanel.setWindowTitle(QtGui.QApplication.translate("Arch", "Components", None, QtGui.QApplication.UnicodeUTF8))
self.delButton.setText(QtGui.QApplication.translate("Arch", "Remove", None, QtGui.QApplication.UnicodeUTF8))
@ -251,8 +266,7 @@ class Component:
obj.Proxy = self
self.Type = "Component"
self.Subvolume = None
class ViewProviderComponent:
"A default View Provider for Component objects"
def __init__(self,vobj):
@ -295,4 +309,18 @@ class ViewProviderComponent:
def unsetEdit(self,vobj,mode):
FreeCADGui.Control.closeDialog()
return
class ArchSelectionObserver:
def __init__(self,origin,watched):
self.origin = origin
self.watched = watched
def addSelection(self,document, object, element, position):
if object == self.watched.Name:
if not element:
print "closing Sketch edit"
self.origin.ViewObject.Transparency = 0
self.origin.ViewObject.Selectable = True
self.watched.ViewObject.hide()
FreeCADGui.activateWorkbench("ArchWorkbench")
FreeCADGui.Selection.removeObserver(FreeCAD.ArchObserver)
del FreeCAD.ArchObserver

View File

@ -32,7 +32,8 @@ __url__ = "http://free-cad.sourceforge.net"
def makeWall(baseobj=None,width=None,height=None,align="Center",name="Wall"):
'''makeWall(obj,[width],[height],[align],[name]): creates a wall based on the
given object'''
given object, which can be a sketch, a draft object, a face or a solid. align
can be "Center","Left" or "Right"'''
obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name)
_Wall(obj)
_ViewProviderWall(obj.ViewObject)
@ -55,6 +56,8 @@ def joinWalls(walls):
return None
if not isinstance(walls,list):
walls = [walls]
if not areSameWallTypes(walls):
return None
base = walls.pop()
if base.Base:
if base.Base.Shape.Faces:
@ -63,9 +66,8 @@ def joinWalls(walls):
sk = base.Base
else:
sk = Draft.makeSketch(base.Base,autoconstraints=True)
old = base.Base.name
base.Base = sk
FreeCAD.ActiveDocument.removeObject(old)
if sk:
base.Base = sk
for w in walls:
if w.Base:
if not base.Base.Shape.Faces:
@ -74,6 +76,24 @@ def joinWalls(walls):
FreeCAD.ActiveDocument.recompute()
return base
def areSameWallTypes(walls):
"returns True is all the walls in the given list have same height, width, and alignment"
for att in ["Width","Height","Align"]:
value = None
for w in walls:
if not hasattr(w,att):
return False
if not value:
value = getattr(w,att)
else:
if type(value) == float:
if round(value,Draft.precision()) != round(getattr(w,att),Draft.precision()):
return False
else:
if value != getattr(w,att):
return False
return True
class _CommandWall:
"the Arch Wall command definition"
def GetResources(self):
@ -83,6 +103,14 @@ class _CommandWall:
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Wall","Creates a wall object from scratch or from a selected object (wire, face or solid)")}
def Activated(self):
global QtGui, QtCore
from PyQt4 import QtGui, QtCore
self.Width = 0.1
self.Height = 1
self.Align = "Center"
sel = FreeCADGui.Selection.getSelection()
done = False
self.existing = []
@ -98,41 +126,106 @@ class _CommandWall:
import DraftTrackers
self.points = []
self.tracker = DraftTrackers.boxTracker()
FreeCADGui.Snapper.getPoint(callback=self.getPoint)
FreeCADGui.Snapper.getPoint(callback=self.getPoint,extradlg=self.taskbox())
def getPoint(self,point):
def getPoint(self,point=None,obj=None):
"this function is called by the snapper when it has a 3D point"
pos = FreeCADGui.ActiveDocument.ActiveView.getCursorPos()
exi = FreeCADGui.ActiveDocument.ActiveView.getObjectInfo(pos)
if exi:
exi = FreeCAD.ActiveDocument.getObject(exi['Object'])
if Draft.getType(exi) == "Wall":
if not exi in self.existing:
self.existing.append(exi)
if obj:
if Draft.getType(obj) == "Wall":
if not obj in self.existing:
self.existing.append(obj)
if point == None:
self.tracker.finalize()
return
self.points.append(point)
if len(self.points) == 1:
self.tracker.on()
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint,movecallback=self.update)
FreeCADGui.Snapper.getPoint(last=self.points[0],callback=self.getPoint,movecallback=self.update,extradlg=self.taskbox())
elif len(self.points) == 2:
import Part
l = Part.Line(self.points[0],self.points[1])
self.tracker.finalize()
FreeCAD.ActiveDocument.openTransaction("Wall")
if not self.existing:
s = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace")
s.addGeometry(l)
makeWall(s)
self.addDefault(l)
else:
w = joinWalls(self.existing)
w.Base.addGeometry(l)
self.tracker.finalize()
if w:
if areSameWallTypes([w,self]):
w.Base.addGeometry(l)
else:
self.addDefault(l)
else:
self.addDefault(l)
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
def addDefault(self,l):
s = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObject","WallTrace")
s.addGeometry(l)
makeWall(s,width=self.Width,height=self.Height,align=self.Align)
def update(self,point):
"this function is called by the Snapper when the mouse is moved"
self.tracker.update([self.points[0],point])
b = self.points[0]
n = FreeCAD.DraftWorkingPlane.axis
bv = point.sub(b)
dv = bv.cross(n)
dv = fcvec.scaleTo(dv,self.Width/2)
if self.Align == "Center":
self.tracker.update([b,point])
elif self.Align == "Left":
self.tracker.update([b.add(dv),point.add(dv)])
else:
dv = fcvec.neg(dv)
self.tracker.update([b.add(dv),point.add(dv)])
def taskbox(self):
"sets up a taskbox widget"
w = QtGui.QWidget()
w.setWindowTitle("Wall options")
lay0 = QtGui.QVBoxLayout(w)
lay1 = QtGui.QHBoxLayout()
lay0.addLayout(lay1)
label1 = QtGui.QLabel("Width")
lay1.addWidget(label1)
value1 = QtGui.QDoubleSpinBox()
value1.setDecimals(2)
value1.setValue(self.Width)
lay1.addWidget(value1)
lay2 = QtGui.QHBoxLayout()
lay0.addLayout(lay2)
label2 = QtGui.QLabel("Height")
lay2.addWidget(label2)
value2 = QtGui.QDoubleSpinBox()
value2.setDecimals(2)
value2.setValue(self.Height)
lay2.addWidget(value2)
lay3 = QtGui.QHBoxLayout()
lay0.addLayout(lay3)
label3 = QtGui.QLabel("Alignment")
lay3.addWidget(label3)
value3 = QtGui.QComboBox()
items = ["Center","Left","Right"]
value3.addItems(items)
value3.setCurrentIndex(items.index(self.Align))
lay3.addWidget(value3)
QtCore.QObject.connect(value1,QtCore.SIGNAL("valueChanged(double)"),self.setWidth)
QtCore.QObject.connect(value2,QtCore.SIGNAL("valueChanged(double)"),self.setHeight)
QtCore.QObject.connect(value3,QtCore.SIGNAL("currentIndexChanged(int)"),self.setAlign)
return w
def setWidth(self,d):
self.Width = d
self.tracker.width(d)
def setHeight(self,d):
self.Height = d
self.tracker.height(d)
def setAlign(self,i):
self.Align = ["Center","Left","Right"][i]
class _Wall(ArchComponent.Component):
"The Wall object"
def __init__(self,obj):
@ -162,6 +255,10 @@ class _Wall(ArchComponent.Component):
import Part
from draftlibs import fcgeo
flat = False
if hasattr(obj.ViewObject,"DisplayMode"):
flat = (obj.ViewObject.DisplayMode == "Flat 2D")
def getbase(wire):
"returns a full shape from a base wire"
@ -182,7 +279,9 @@ class _Wall(ArchComponent.Component):
dvec = fcvec.neg(dvec)
w2 = fcgeo.offsetWire(wire,dvec)
sh = fcgeo.bind(w1,w2)
if height:
# fixing self-intersections
sh.fix(0.1,0,1)
if height and (not flat):
norm = Vector(normal).multiply(height)
sh = sh.extrude(norm)
return sh
@ -258,4 +357,18 @@ class _ViewProviderWall(ArchComponent.ViewProviderComponent):
def getIcon(self):
return ":/icons/Arch_Wall_Tree.svg"
def getDisplayModes(self,vobj):
return ["Flat 2D"]
def setDisplayMode(self,mode):
self.Object.Proxy.createGeometry(self.Object)
if mode == "Flat 2D":
return "Flat Lines"
else:
return mode
def attach(self,vobj):
self.Object = vobj.Object
return
FreeCADGui.addCommand('Arch_Wall',_CommandWall())

View File

@ -256,18 +256,11 @@ def formatObject(target,origin=None):
if "ShapeColor" in obrep.PropertiesList: obrep.ShapeColor = fcol
else:
matchrep = origin.ViewObject
if ("LineWidth" in obrep.PropertiesList) and \
("LineWidth" in matchrep.PropertiesList):
obrep.LineWidth = matchrep.LineWidth
if ("PointColor" in obrep.PropertiesList) and \
("PointColor" in matchrep.PropertiesList):
obrep.PointColor = matchrep.PointColor
if ("LineColor" in obrep.PropertiesList) and \
("LineColor" in matchrep.PropertiesList):
obrep.LineColor = matchrep.LineColor
if ("ShapeColor" in obrep.PropertiesList) and \
("ShapeColor" in matchrep.PropertiesList):
obrep.ShapeColor = matchrep.ShapeColor
for p in matchrep.PropertiesList:
if not p in ["DisplayMode","BoundingBox","Proxy","RootNode"]:
if p in obrep.PropertiesList:
val = getattr(matchrep,p)
setattr(obrep,p,val)
if matchrep.DisplayMode in obrep.listDisplayModes():
obrep.DisplayMode = matchrep.DisplayMode
@ -516,48 +509,52 @@ def makeText(stringslist,point=Vector(0,0,0),screen=False):
select(obj)
return obj
def makeCopy(obj):
def makeCopy(obj,force=None,reparent=False):
'''makeCopy(object): returns an exact copy of an object'''
if getType(obj) == "Rectangle":
if (getType(obj) == "Rectangle") or (force == "Rectangle"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Rectangle(newobj)
_ViewProviderRectangle(newobj.ViewObject)
elif getType(obj) == "Wire":
elif (getType(obj) == "Dimension") or (force == "Dimension"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Dimension(newobj)
_ViewProviderDimension(newobj.ViewObject)
elif (getType(obj) == "Wire") or (force == "Wire"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Wire(newobj)
_ViewProviderWire(newobj.ViewObject)
elif getType(obj) == "Circle":
elif (getType(obj) == "Circle") or (force == "Circle"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Circle(newobj)
_ViewProviderCircle(newobj.ViewObject)
elif getType(obj) == "Polygon":
_ViewProviderDraft(newobj.ViewObject)
elif (getType(obj) == "Polygon") or (force == "Polygon"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Polygon(newobj)
_ViewProviderPolygon(newobj.ViewObject)
elif getType(obj) == "BSpline":
elif (getType(obj) == "BSpline") or (force == "BSpline"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_BSpline(newobj)
_ViewProviderBSpline(newobj.ViewObject)
elif getType(obj) == "Block":
elif (getType(obj) == "Block") or (force == "BSpline"):
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
_Block(newobj)
_ViewProviderDraftPart(newobj.ViewObject)
elif getType(obj) == "Structure":
elif (getType(obj) == "Structure") or (force == "Structure"):
import ArchStructure
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
ArchStructure._Structure(newobj)
ArchStructure._ViewProviderStructure(newobj.ViewObject)
elif getType(obj) == "Wall":
elif (getType(obj) == "Wall") or (force == "Wall"):
import ArchWall
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
ArchWall._Wall(newobj)
ArchWall._ViewProviderWall(newobj.ViewObject)
elif getType(obj) == "Window":
elif (getType(obj) == "Window") or (force == "Window"):
import ArchWindow
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
ArchWindow._Window(newobj)
Archwindow._ViewProviderWindow(newobj.ViewObject)
elif getType(obj) == "Cell":
elif (getType(obj) == "Cell") or (force == "Cell"):
import ArchCell
newobj = FreeCAD.ActiveDocument.addObject(obj.Type,getRealName(obj.Name))
ArchCell._Cell(newobj)
@ -569,8 +566,19 @@ def makeCopy(obj):
print "Error: Object type cannot be copied"
return None
for p in obj.PropertiesList:
if p in newobj.PropertiesList:
setattr(newobj,p,obj.getPropertyByName(p))
if not p in ["Proxy"]:
if p in newobj.PropertiesList:
setattr(newobj,p,obj.getPropertyByName(p))
if reparent:
parents = obj.InList
if parents:
for par in parents:
if par.Type == "App::DocumentObjectGroup":
par.addObject(newobj)
else:
for prop in par.PropertiesList:
if getattr(par,prop) == obj:
setattr(par,prop,newobj)
formatObject(newobj,obj)
return newobj
@ -685,7 +693,7 @@ def move(objectslist,vector,copy=False):
if copy:
newobj = FreeCAD.ActiveDocument.addObject("App::FeaturePython",getRealName(obj.Name))
_Dimension(newobj)
_DimensionViewProvider(newobj.ViewObject)
_ViewProviderDimension(newobj.ViewObject)
else:
newobj = obj
newobj.Start = obj.Start.add(vector)
@ -1401,6 +1409,57 @@ def clone(obj,delta=None):
if delta:
cl.Placement.move(delta)
return cl
def heal(objlist=None,delete=True,reparent=True):
'''heal([objlist],[delete],[reparent]) - recreates Draft objects that are damaged,
for example if created from an earlier version. If delete is True,
the damaged objects are deleted (default). If ran without arguments, all the objects
in the document will be healed if they are damaged. If reparent is True (default),
new objects go at the very same place in the tree than their original.'''
if not objlist:
objlist = FreeCAD.ActiveDocument.Objects
print "Healing whole document..."
if not isinstance(objlist,list):
objlist = [objlist]
dellist = []
got = False
for obj in objlist:
dtype = getType(obj)
ftype = obj.Type
if ftype in ["Part::FeaturePython","App::FeaturePython"]:
if obj.ViewObject.Proxy == 1 and dtype in ["Unknown","Part"]:
got = True
dellist.append(obj.Name)
props = obj.PropertiesList
if ("Dimline" in props) and ("Start" in props):
print "Healing " + obj.Name + " of type Dimension"
nobj = makeCopy(obj,force="Dimension",reparent=reparent)
elif ("Height" in props) and ("Length" in props):
print "Healing " + obj.Name + " of type Rectangle"
nobj = makeCopy(obj,force="Rectangle",reparent=reparent)
elif ("Points" in props) and ("Closed" in props):
print "Healing " + obj.Name + " of type Wire"
nobj = makeCopy(obj,force="Wire",reparent=reparent)
elif ("Radius" in props) and ("FirstAngle" in props):
print "Healing " + obj.Name + " of type Circle"
nobj = makeCopy(obj,force="Circle",reparent=reparent)
else:
dellist.pop()
print "Object " + obj.Name + " is not healable"
if not got:
print "No object seems to need healing"
else:
print "Healed ",len(dellist)," objects"
if dellist and delete:
for n in dellist:
FreeCAD.ActiveDocument.removeObject(n)
#---------------------------------------------------------------------------
# Python Features definitions
@ -1539,10 +1598,13 @@ class _ViewProviderDimension:
if hasattr(obj.ViewObject,"DisplayMode"):
if obj.ViewObject.DisplayMode == "3D":
offset = fcvec.neg(offset)
if obj.ViewObject.TextPosition == Vector(0,0,0):
tbase = midpoint.add(offset)
if hasattr(obj.ViewObject,"TextPosition"):
if obj.ViewObject.TextPosition == Vector(0,0,0):
tbase = midpoint.add(offset)
else:
tbase = obj.ViewObject.TextPosition
else:
tbase = obj.ViewObject.TextPosition
tbase = midpoint.add(offset)
rot = FreeCAD.Placement(fcvec.getPlaneRotation(u,v,norm)).Rotation.Q
return p1,p2,p3,p4,tbase,norm,rot
@ -1966,6 +2028,7 @@ class _Rectangle:
def __init__(self, obj):
obj.addProperty("App::PropertyDistance","Length","Base","Length of the rectangle")
obj.addProperty("App::PropertyDistance","Height","Base","Height of the rectange")
obj.addProperty("App::PropertyDistance","FilletRadius","Base","Radius to use to fillet the corners")
obj.Proxy = self
obj.Length=1
obj.Height=1
@ -1980,12 +2043,18 @@ class _Rectangle:
def createGeometry(self,fp):
import Part
from draftlibs import fcgeo
plm = fp.Placement
p1 = Vector(0,0,0)
p2 = Vector(p1.x+fp.Length,p1.y,p1.z)
p3 = Vector(p1.x+fp.Length,p1.y+fp.Height,p1.z)
p4 = Vector(p1.x,p1.y+fp.Height,p1.z)
shape = Part.makePolygon([p1,p2,p3,p4,p1])
if "FilletRadius" in fp.PropertiesList:
if fp.FilletRadius != 0:
w = fcgeo.filletWire(shape,fp.FilletRadius)
if w:
shape = w
shape = Part.Face(shape)
fp.Shape = shape
fp.Placement = plm
@ -2017,9 +2086,9 @@ class _Circle:
"The Circle object"
def __init__(self, obj):
obj.addProperty("App::PropertyAngle","FirstAngle","Arc",
obj.addProperty("App::PropertyAngle","FirstAngle","Base",
"Start angle of the arc")
obj.addProperty("App::PropertyAngle","LastAngle","Arc",
obj.addProperty("App::PropertyAngle","LastAngle","Base",
"End angle of the arc (for a full circle, give it same value as First Angle)")
obj.addProperty("App::PropertyDistance","Radius","Base",
"Radius of the circle")
@ -2060,6 +2129,7 @@ class _Wire:
"The start point of this line")
obj.addProperty("App::PropertyVector","End","Base",
"The end point of this line")
obj.addProperty("App::PropertyDistance","FilletRadius","Base","Radius to use to fillet the corners")
obj.Proxy = self
obj.Closed = False
self.Type = "Wire"
@ -2068,7 +2138,7 @@ class _Wire:
self.createGeometry(fp)
def onChanged(self, fp, prop):
if prop in ["Points","Closed","Base","Tool"]:
if prop in ["Points","Closed","Base","Tool","FilletRadius"]:
self.createGeometry(fp)
if prop == "Points":
if fp.Start != fp.Points[0]:
@ -2121,6 +2191,11 @@ class _Wire:
fp.Points.pop()
if fp.Closed and (len(fp.Points) > 2):
shape = Part.makePolygon(fp.Points+[fp.Points[0]])
if "FilletRadius" in fp.PropertiesList:
if fp.FilletRadius != 0:
w = fcgeo.filletWire(shape,fp.FilletRadius)
if w:
shape = w
shape = Part.Face(shape)
else:
edges = []
@ -2130,6 +2205,11 @@ class _Wire:
edges.append(Part.Line(lp,p).toShape())
lp = p
shape = Part.Wire(edges)
if "FilletRadius" in fp.PropertiesList:
if fp.FilletRadius != 0:
w = fcgeo.filletWire(shape,fp.FilletRadius)
if w:
shape = w
fp.Shape = shape
fp.Placement = plm
@ -2178,6 +2258,7 @@ class _Polygon:
obj.addProperty("App::PropertyInteger","FacesNumber","Base","Number of faces")
obj.addProperty("App::PropertyDistance","Radius","Base","Radius of the control circle")
obj.addProperty("App::PropertyEnumeration","DrawMode","Base","How the polygon must be drawn from the control circle")
obj.addProperty("App::PropertyDistance","FilletRadius","Base","Radius to use to fillet the corners")
obj.DrawMode = ['inscribed','circumscribed']
obj.FacesNumber = 3
obj.Radius = 1
@ -2193,6 +2274,7 @@ class _Polygon:
def createGeometry(self,fp):
import Part
from draftlibs import fcgeo
plm = fp.Placement
angle = (math.pi*2)/fp.FacesNumber
if fp.DrawMode == 'inscribed':
@ -2205,6 +2287,11 @@ class _Polygon:
pts.append(Vector(delta*math.cos(ang),delta*math.sin(ang),0))
pts.append(pts[0])
shape = Part.makePolygon(pts)
if "FilletRadius" in fp.PropertiesList:
if fp.FilletRadius != 0:
w = fcgeo.filletWire(shape,fp.FilletRadius)
if w:
shape = w
shape = Part.Face(shape)
fp.Shape = shape
fp.Placement = plm

View File

@ -136,8 +136,14 @@ class DraftLineEdit(QtGui.QLineEdit):
QtGui.QLineEdit.keyPressEvent(self, event)
class DraftTaskPanel:
def __init__(self,widget):
self.form = widget
def __init__(self,widget,extra=None):
if extra:
if isinstance(extra,list):
self.form = [widget] + extra
else:
self.form = [widget,extra]
else:
self.form = widget
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Cancel)
def accept(self):
@ -155,6 +161,7 @@ class DraftToolBar:
self.tray = None
self.sourceCmd = None
self.cancel = None
self.pointcallback = None
self.taskmode = Draft.getParam("UiMode")
self.paramcolor = Draft.getParam("color")>>8
self.color = QtGui.QColor(self.paramcolor)
@ -171,7 +178,7 @@ class DraftToolBar:
self.fillmode = Draft.getParam("fillmode")
if self.taskmode:
# only a dummy widget, since widgets are created on demand
# add only a dummy widget, since widgets are created on demand
self.baseWidget = QtGui.QWidget()
else:
# create the draft Toolbar
@ -250,6 +257,12 @@ class DraftToolBar:
if hide: chk.hide()
layout.addWidget(chk)
return chk
def _combo (self,name,layout,hide=True):
cb = QtGui.QComboBox(self.baseWidget)
cb.setObjectName(name)
if hide: cb.hide()
layout.addWidget(cb)
def setupToolBar(self,task=False):
"sets the draft toolbar up"
@ -463,19 +476,17 @@ class DraftToolBar:
# Interface modes
#---------------------------------------------------------------------------
def taskUi(self,title):
def taskUi(self,title,extra=None):
if self.taskmode:
self.isTaskOn = True
todo.delay(FreeCADGui.Control.closeDialog,None)
self.baseWidget = QtGui.QWidget()
self.setTitle(title)
self.layout = QtGui.QVBoxLayout(self.baseWidget)
self.setupToolBar(task=True)
self.retranslateUi(self.baseWidget)
self.panel = DraftTaskPanel(self.baseWidget)
self.panel = DraftTaskPanel(self.baseWidget,extra)
todo.delay(FreeCADGui.Control.showDialog,self.panel)
else:
self.setTitle(title)
self.setTitle(title)
def selectPlaneUi(self):
self.taskUi(translate("draft", "Select Plane"))
@ -510,9 +521,10 @@ class DraftToolBar:
self.labelx.setText(translate("draft", "Center X"))
self.continueCmd.show()
def pointUi(self,title=translate("draft","Point"),cancel=None):
def pointUi(self,title=translate("draft","Point"),cancel=None,extra=None,getcoords=None,rel=False):
if cancel: self.cancel = cancel
self.taskUi(title)
if getcoords: self.pointcallback = getcoords
self.taskUi(title,extra)
self.xValue.setEnabled(True)
self.yValue.setEnabled(True)
self.labelx.setText(translate("draft", "X"))
@ -522,9 +534,13 @@ class DraftToolBar:
self.xValue.show()
self.yValue.show()
self.zValue.show()
if rel: self.isRelative.show()
self.xValue.setFocus()
self.xValue.selectAll()
def extraUi(self):
pass
def offsetUi(self):
self.taskUi(translate("draft","Offset"))
self.radiusUi()
@ -536,6 +552,9 @@ class DraftToolBar:
def offUi(self):
todo.delay(FreeCADGui.Control.closeDialog,None)
self.cancel = None
self.sourceCmd = None
self.pointcallback = None
if self.taskmode:
self.isTaskOn = False
self.baseWidget = QtGui.QWidget()
@ -570,7 +589,7 @@ class DraftToolBar:
self.textValue.hide()
self.continueCmd.hide()
self.occOffset.hide()
def trimUi(self,title=translate("draft","Trim")):
self.taskUi(title)
self.radiusUi()
@ -792,7 +811,7 @@ class DraftToolBar:
def validatePoint(self):
"function for checking and sending numbers entered manually"
if self.sourceCmd != None:
if self.sourceCmd or self.pointcallback:
if (self.labelRadius.isVisible()):
try:
rad=float(self.radiusValue.text())
@ -815,22 +834,25 @@ class DraftToolBar:
except ValueError:
pass
else:
if self.isRelative.isVisible() and self.isRelative.isChecked():
if self.sourceCmd.node:
if self.sourceCmd.featureName == "Rectangle":
last = self.sourceCmd.node[0]
else:
last = self.sourceCmd.node[-1]
numx = last.x + numx
numy = last.y + numy
numz = last.z + numz
if FreeCAD.DraftWorkingPlane:
v = FreeCAD.Vector(numx,numy,numz)
v = FreeCAD.DraftWorkingPlane.getGlobalCoords(v)
numx = v.x
numy = v.y
numz = v.z
self.sourceCmd.numericInput(numx,numy,numz)
if self.pointcallback:
self.pointcallback(FreeCAD.Vector(numx,numy,numz),(self.isRelative.isVisible() and self.isRelative.isChecked()))
else:
if self.isRelative.isVisible() and self.isRelative.isChecked():
if self.sourceCmd.node:
if self.sourceCmd.featureName == "Rectangle":
last = self.sourceCmd.node[0]
else:
last = self.sourceCmd.node[-1]
numx = last.x + numx
numy = last.y + numy
numz = last.z + numz
if FreeCAD.DraftWorkingPlane:
v = FreeCAD.Vector(numx,numy,numz)
v = FreeCAD.DraftWorkingPlane.getGlobalCoords(v)
numx = v.x
numy = v.y
numz = v.z
self.sourceCmd.numericInput(numx,numy,numz)
def finish(self):
"finish button action"

View File

@ -68,6 +68,7 @@ class Snapper:
self.grid = None
self.constrainLine = None
self.trackLine = None
self.lastSnappedObject = None
# the snapmarker has "dot","circle" and "square" available styles
self.mk = {'passive':'circle',
@ -175,6 +176,8 @@ class Snapper:
obj = FreeCAD.ActiveDocument.getObject(info['Object'])
if not obj:
return cstr(point)
self.lastSnappedObject = obj
if hasattr(obj.ViewObject,"Selectable"):
if not obj.ViewObject.Selectable:
@ -531,6 +534,7 @@ class Snapper:
for v in self.views:
v.unsetCursor()
self.views = []
self.cursorMode = None
else:
if mode != self.cursorMode:
if not self.views:
@ -559,11 +563,9 @@ class Snapper:
self.extLine.off()
if self.grid:
self.grid.off()
if self.constrainLine:
self.constrainLine.off()
self.unconstrain()
self.radius = 0
self.setCursor()
self.cursorMode = None
def constrain(self,point,basepoint=None,axis=None):
'''constrain(point,basepoint=None,axis=None: Returns a
@ -627,11 +629,12 @@ class Snapper:
if self.constrainLine:
self.constrainLine.off()
def getPoint(self,last=None,callback=None,movecallback=None):
def getPoint(self,last=None,callback=None,movecallback=None,extradlg=None):
"""getPoint([last],[callback],[movecallback]) : gets a 3D point from the screen. You
can provide an existing point, in that case additional snap options and a tracker
are available. You can also pass a function as callback, which will get called
"""getPoint([last],[callback],[movecallback],[extradlg]) : gets a 3D point
from the screen. You can provide an existing point, in that case additional
snap options and a tracker are available.
You can also pass a function as callback, which will get called
with the resulting point as argument, when a point is clicked, and optionally
another callback which gets called when the mouse is moved.
@ -642,7 +645,12 @@ class Snapper:
def cb(point):
if point:
print "got a 3D point: ",point
FreeCADGui.Snapper.getPoint(callback=cb)"""
FreeCADGui.Snapper.getPoint(callback=cb)
If the callback function accepts more than one argument, it will also receive
the last snapped object. Finally, a pyqt dialog can be passed as extra taskbox."""
import inspect
self.pt = None
self.ui = FreeCADGui.draftToolBar
@ -667,19 +675,33 @@ class Snapper:
if movecallback:
movecallback(self.pt)
def getcoords(point,relative=False):
self.pt = point
if relative and last:
v = FreeCAD.DraftWorkingPlane.getGlobalCoords(point)
self.pt = last.add(v)
accept()
def click(event_cb):
event = event_cb.getEvent()
if event.getButton() == 1:
if event.getState() == coin.SoMouseButtonEvent.DOWN:
self.view.removeEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(),self.callbackClick)
self.view.removeEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(),self.callbackMove)
FreeCADGui.Snapper.off()
self.ui.offUi()
if self.trackLine:
self.trackLine.off()
if callback:
callback(self.pt)
self.pt = None
accept()
def accept():
self.view.removeEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(),self.callbackClick)
self.view.removeEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(),self.callbackMove)
obj = FreeCADGui.Snapper.lastSnappedObject
FreeCADGui.Snapper.off()
self.ui.offUi()
if self.trackLine:
self.trackLine.off()
if callback:
if len(inspect.getargspec(callback).args) > 2:
callback(self.pt,obj)
else:
callback(self.pt)
self.pt = None
def cancel():
self.view.removeEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(),self.callbackClick)
@ -691,8 +713,8 @@ class Snapper:
if callback:
callback(None)
# adding 2 callback functions
self.ui.pointUi(cancel=cancel)
# adding callback functions
self.ui.pointUi(cancel=cancel,getcoords=getcoords,extra=extradlg,rel=bool(last))
self.callbackClick = self.view.addEventCallbackPivy(coin.SoMouseButtonEvent.getClassTypeId(),click)
self.callbackMove = self.view.addEventCallbackPivy(coin.SoLocation2Event.getClassTypeId(),move)

View File

@ -476,7 +476,7 @@ class Line(Creator):
if self.isWire:
msg(translate("draft", "Pick next point, or (F)inish or (C)lose:\n"))
else:
currentshape = self.obj.Shape
currentshape = self.obj.Shape.copy()
last = self.node[len(self.node)-2]
newseg = Part.Line(last,point).toShape()
newshape=currentshape.fuse(newseg)

View File

@ -680,7 +680,7 @@ class boxTracker(Tracker):
else:
return self.cube.width.getValue()
def heigth(self,h=None):
def height(self,h=None):
if h:
self.cube.depth.setValue(h)
self.update()

File diff suppressed because it is too large Load Diff

View File

@ -356,6 +356,43 @@ If color mapping is choosed, you must choose a color mapping file containing a t
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Export Style</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefComboBox" name="svg_export_style_combobox">
<property name="toolTip">
<string>Style of SVG file to write when exporting a Sketch.</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>svg_export_style</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/Draft</cstring>
</property>
<item>
<property name="text">
<string>Translated (for print &amp; display)</string>
</property>
</item>
<item>
<property name="text">
<string>Raw (for CAM)</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@ -959,9 +959,19 @@ def getTangent(edge,frompoint=None):
def bind(w1,w2):
'''bind(wire1,wire2): binds 2 wires by their endpoints and
returns a face'''
w3 = Part.Line(w1.Vertexes[0].Point,w2.Vertexes[0].Point).toShape()
w4 = Part.Line(w1.Vertexes[-1].Point,w2.Vertexes[-1].Point).toShape()
return Part.Face(Part.Wire(w1.Edges+[w3]+w2.Edges+[w4]))
if w1.isClosed() and w2.isClosed():
d1 = w1.BoundBox.DiagonalLength
d2 = w2.BoundBox.DiagonalLength
if d1 > d2:
#w2.reverse()
return Part.Face([w1,w2])
else:
#w1.reverse()
return Part.Face([w2,w1])
else:
w3 = Part.Line(w1.Vertexes[0].Point,w2.Vertexes[0].Point).toShape()
w4 = Part.Line(w1.Vertexes[-1].Point,w2.Vertexes[-1].Point).toShape()
return Part.Face(Part.Wire(w1.Edges+[w3]+w2.Edges+[w4]))
def cleanFaces(shape):
"removes inner edges from coplanar faces"
@ -1185,6 +1195,280 @@ def arcFromSpline(edge):
return Part.makeCircle(radius,center)
except:
print "couldn't make a circle out of this edge"
# Fillet code graciously donated by Jacques-Antoine Gaudin
def fillet(lEdges,r):
''' Take a list of two Edges & a float as argument,
Returns a list of sorted edges describing a round corner'''
def getCurveType(edge,existingCurveType = None):
'''Builds or completes a dictionnary containing edges with keys "Arc" and "Line"'''
if not existingCurveType :
existingCurveType = { 'Line' : [], 'Arc' : [] }
if issubclass(type(edge.Curve),Part.Line) :
existingCurveType['Line'] += [edge]
elif issubclass(type(edge.Curve),Part.Circle) :
existingCurveType['Arc'] += [edge]
else :
raise Exception("Edge's curve must be either Line or Arc")
return existingCurveType
rndEdges = lEdges[0:2]
rndEdges = sortEdges(rndEdges)
if len(rndEdges) < 2 :
return rndEdges
if r <= 0 :
print "fcgeo.fillet : Error : radius is negative."
return rndEdges
curveType = getCurveType(rndEdges[0])
curveType = getCurveType(rndEdges[1],curveType)
lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]]
if len(curveType['Line']) == 2:
# Deals with 2-line-edges lists --------------------------------------
U1 = lVertexes[0].Point.sub(lVertexes[1].Point) ; U1.normalize()
U2 = lVertexes[2].Point.sub(lVertexes[1].Point) ; U2.normalize()
alpha = U1.getAngle(U2)
if round(alpha,precision) == 0 or round(alpha - math.pi,precision) == 0: # Edges have same direction
print "fcgeo.fillet : Warning : edges have same direction. Did nothing"
return rndEdges
dToCenter = r / math.sin(alpha/2.)
dToTangent = (dToCenter**2-r**2)**(0.5)
dirVect = Vector(U1) ; dirVect.scale(dToTangent,dToTangent,dToTangent)
arcPt1 = lVertexes[1].Point.add(dirVect)
dirVect = U2.add(U1) ; dirVect.normalize()
dirVect.scale(dToCenter-r,dToCenter-r,dToCenter-r)
arcPt2 = lVertexes[1].Point.add(dirVect)
dirVect = Vector(U2) ; dirVect.scale(dToTangent,dToTangent,dToTangent)
arcPt3 = lVertexes[1].Point.add(dirVect)
if (dToTangent>lEdges[0].Length) or (dToTangent>lEdges[1].Length) :
print "fcgeo.fillet : Error : radius value ", r," is too high"
return rndEdges
rndEdges[1] = Part.Edge(Part.Arc(arcPt1,arcPt2,arcPt3))
rndEdges[0] = Part.Edge(Part.Line(lVertexes[0].Point,arcPt1))
rndEdges += [Part.Edge(Part.Line(arcPt3,lVertexes[2].Point))]
return rndEdges
elif len(curveType['Arc']) == 1 :
# Deals with lists containing an arc and a line ----------------------------------
if lEdges[0] in curveType['Arc'] :
lineEnd = lVertexes[2] ; arcEnd = lVertexes[0] ; arcFirst = True
else :
lineEnd = lVertexes[0] ; arcEnd = lVertexes[2] ; arcFirst = False
arcCenter = curveType['Arc'][0].Curve.Center
arcRadius = curveType['Arc'][0].Curve.Radius
arcAxis = curveType['Arc'][0].Curve.Axis
arcLength = curveType['Arc'][0].Length
U1 = lineEnd.Point.sub(lVertexes[1].Point) ; U1.normalize()
toCenter = arcCenter.sub(lVertexes[1].Point)
if arcFirst : # make sure the tangent points towards the arc
T = arcAxis.cross(toCenter)
else :
T = toCenter.cross(arcAxis)
projCenter = toCenter.dot(U1)
if round(abs(projCenter),precision) > 0 :
normToLine = U1.cross(T).cross(U1)
else :
normToLine = Vector(toCenter)
normToLine.normalize()
dCenterToLine = toCenter.dot(normToLine) - r
if round(projCenter,precision) > 0 :
newRadius = arcRadius - r
elif round(projCenter,precision) < 0 or (round(projCenter,precision) == 0 and U1.dot(T) > 0):
newRadius = arcRadius + r
else :
print "fcgeo.fillet : Warning : edges are already tangent. Did nothing"
return rndEdges
toNewCent = newRadius**2-dCenterToLine**2
if toNewCent > 0 :
toNewCent = abs(abs(projCenter) - toNewCent**(0.5))
else :
print "fcgeo.fillet : Error : radius value ", r," is too high"
return rndEdges
U1.scale(toNewCent,toNewCent,toNewCent)
normToLine.scale(r,r,r)
newCent = lVertexes[1].Point.add(U1).add(normToLine)
arcPt1= lVertexes[1].Point.add(U1)
arcPt2= lVertexes[1].Point.sub(newCent); arcPt2.normalize()
arcPt2.scale(r,r,r) ; arcPt2 = arcPt2.add(newCent)
if newRadius == arcRadius - r :
arcPt3= newCent.sub(arcCenter)
else :
arcPt3= arcCenter.sub(newCent)
arcPt3.normalize()
arcPt3.scale(r,r,r) ; arcPt3 = arcPt3.add(newCent)
arcPt = [arcPt1,arcPt2,arcPt3]
# Warning : In the following I used a trick for calling the right element
# in arcPt or V : arcFirst is a boolean so - not arcFirst is -0 or -1
# list[-1] is the last element of a list and list[0] the first
# this way I don't have to proceed tests to know the position of the arc
myTrick = not arcFirst
V = [arcPt3]
V += [arcEnd.Point]
toCenter.scale(-1,-1,-1)
delLength = arcRadius * V[0].sub(arcCenter).getAngle(toCenter)
if delLength > arcLength or toNewCent > curveType['Line'][0].Length:
print "fcgeo.fillet : Error : radius value ", r," is too high"
return rndEdges
arcAsEdge = arcFrom2Pts(V[-arcFirst],V[-myTrick],arcCenter,arcAxis)
V = [lineEnd.Point,arcPt1]
lineAsEdge = Part.Edge(Part.Line(V[-arcFirst],V[myTrick]))
rndEdges[not arcFirst] = arcAsEdge
rndEdges[arcFirst] = lineAsEdge
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[- arcFirst],arcPt[1],arcPt[- myTrick]))]
return rndEdges
elif len(curveType['Arc']) == 2 :
# Deals with lists of 2 arc-edges --------------------------------------------
arcCenter, arcRadius, arcAxis, arcLength, toCenter, T, newRadius = [], [], [], [], [], [], []
for i in range(2) :
arcCenter += [curveType['Arc'][i].Curve.Center]
arcRadius += [curveType['Arc'][i].Curve.Radius]
arcAxis += [curveType['Arc'][i].Curve.Axis]
arcLength += [curveType['Arc'][i].Length]
toCenter += [arcCenter[i].sub(lVertexes[1].Point)]
T += [arcAxis[0].cross(toCenter[0])]
T += [toCenter[1].cross(arcAxis[1])]
CentToCent = toCenter[1].sub(toCenter[0])
dCentToCent = CentToCent.Length
sameDirection = (arcAxis[0].dot(arcAxis[1]) > 0)
TcrossT = T[0].cross(T[1])
if sameDirection :
if round(TcrossT.dot(arcAxis[0]),precision) > 0 :
newRadius += [arcRadius[0]+r]
newRadius += [arcRadius[1]+r]
elif round(TcrossT.dot(arcAxis[0]),precision) < 0 :
newRadius += [arcRadius[0]-r]
newRadius += [arcRadius[1]-r]
elif T[0].dot(T[1]) > 0 :
newRadius += [arcRadius[0]+r]
newRadius += [arcRadius[1]+r]
else :
print "fcgeo.fillet : Warning : edges are already tangent. Did nothing"
return rndEdges
elif not sameDirection :
if round(TcrossT.dot(arcAxis[0]),precision) > 0 :
newRadius += [arcRadius[0]+r]
newRadius += [arcRadius[1]-r]
elif round(TcrossT.dot(arcAxis[0]),precision) < 0 :
newRadius += [arcRadius[0]-r]
newRadius += [arcRadius[1]+r]
elif T[0].dot(T[1]) > 0 :
if arcRadius[0] > arcRadius[1] :
newRadius += [arcRadius[0]-r]
newRadius += [arcRadius[1]+r]
elif arcRadius[1] > arcRadius[0] :
newRadius += [arcRadius[0]+r]
newRadius += [arcRadius[1]-r]
else :
print "fcgeo.fillet : Warning : arcs are coincident. Did nothing"
return rndEdges
else :
print "fcgeo.fillet : Warning : edges are already tangent. Did nothing"
return rndEdges
if newRadius[0]+newRadius[1] < dCentToCent or \
newRadius[0]-newRadius[1] > dCentToCent or \
newRadius[1]-newRadius[0] > dCentToCent :
print "fcgeo.fillet : Error : radius value ", r," is too high"
return rndEdges
x = (dCentToCent**2+newRadius[0]**2-newRadius[1]**2)/(2*dCentToCent)
y = (newRadius[0]**2-x**2)**(0.5)
CentToCent.normalize() ; toCenter[0].normalize() ; toCenter[1].normalize()
if abs(toCenter[0].dot(toCenter[1])) != 1 :
normVect = CentToCent.cross(CentToCent.cross(toCenter[0]))
else :
normVect = T[0]
normVect.normalize()
CentToCent.scale(x,x,x) ; normVect.scale(y,y,y)
newCent = arcCenter[0].add(CentToCent.add(normVect))
CentToNewCent = [newCent.sub(arcCenter[0]),newCent.sub(arcCenter[1])]
for i in range(2) :
CentToNewCent[i].normalize()
if newRadius[i] == arcRadius[i]+r :
CentToNewCent[i].scale(-r,-r,-r)
else :
CentToNewCent[i].scale(r,r,r)
toThirdPt = lVertexes[1].Point.sub(newCent) ; toThirdPt.normalize()
toThirdPt.scale(r,r,r)
arcPt1 = newCent.add(CentToNewCent[0])
arcPt2 = newCent.add(toThirdPt)
arcPt3 = newCent.add(CentToNewCent[1])
arcPt = [arcPt1,arcPt2,arcPt3]
arcAsEdge = []
for i in range(2) :
toCenter[i].scale(-1,-1,-1)
delLength = arcRadius[i] * arcPt[-i].sub(arcCenter[i]).getAngle(toCenter[i])
if delLength > arcLength[i] :
print "fcgeo.fillet : Error : radius value ", r," is too high"
return rndEdges
V = [arcPt[-i],lVertexes[-i].Point]
arcAsEdge += [arcFrom2Pts(V[i-1],V[-i],arcCenter[i],arcAxis[i])]
rndEdges[0] = arcAsEdge[0]
rndEdges[1] = arcAsEdge[1]
rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[0],arcPt[1],arcPt[2]))]
return rndEdges
def filletWire(aWire,r,makeClosed=True):
''' Fillets each angle of a wire with r as radius value'''
edges = aWire.Edges
edges = sortEdges(edges)
filEdges = [edges[0]]
for i in range(len(edges)-1):
result = fillet([filEdges[-1],edges[i+1]],r)
if len(result)>2:
filEdges[-1:] = result[0:3]
else :
filEdges[-1:] = result[0:2]
if isReallyClosed(aWire) and makeClosed :
result = fillet([filEdges[-1],filEdges[0]],r)
if len(result)>2:
filEdges[-1:] = result[0:2]
filEdges[0] = result[2]
return Part.Wire(filEdges)
# circle functions *********************************************************

View File

@ -28,12 +28,15 @@ __url__ = ["http://free-cad.sourceforge.net"]
'''
This script imports SVG files in FreeCAD. Currently only reads the following entities:
paths, lines, circular arcs ,rects, circles, ellipses, polygons, polylines.
currently unsupported: image, rounded rect(rx,ry), elliptical arcs
currently unsupported: use, image
'''
#ToDo:
# elliptical arc segments
# rounded rects (elliptical arcs)
# ignoring CDATA
# handle image element (external references and inline base64)
# debug Problem with 'Sans' font from Inkscape
# debug Problem with fill color
# implement inherting fill style from group
# handle viewbox and units
import xml.sax, string, FreeCAD, os, math, re, Draft
from draftlibs import fcvec
@ -48,154 +51,154 @@ except: draftui = None
pythonopen = open
svgcolors = {
'Pink': [255, 192, 203],
'Blue': [0, 0, 255],
'Honeydew': [240, 255, 240],
'Purple': [128, 0, 128],
'Fuchsia': [255, 0, 255],
'LawnGreen': [124, 252, 0],
'Amethyst': [153, 102, 204],
'Crimson': [220, 20, 60],
'White': [255, 255, 255],
'NavajoWhite': [255, 222, 173],
'Cornsilk': [255, 248, 220],
'Bisque': [255, 228, 196],
'PaleGreen': [152, 251, 152],
'Brown': [165, 42, 42],
'DarkTurquoise': [0, 206, 209],
'DarkGreen': [0, 100, 0],
'MediumOrchid': [186, 85, 211],
'Chocolate': [210, 105, 30],
'PapayaWhip': [255, 239, 213],
'Olive': [128, 128, 0],
'Silver': [192, 192, 192],
'PeachPuff': [255, 218, 185],
'Plum': [221, 160, 221],
'DarkGoldenrod': [184, 134, 11],
'SlateGrey': [112, 128, 144],
'MintCream': [245, 255, 250],
'CornflowerBlue': [100, 149, 237],
'Gold': [255, 215, 0],
'HotPink': [255, 105, 180],
'DarkBlue': [0, 0, 139],
'LimeGreen': [50, 205, 50],
'DeepSkyBlue': [0, 191, 255],
'DarkKhaki': [189, 183, 107],
'LightGrey': [211, 211, 211],
'Yellow': [255, 255, 0],
'Gainsboro': [220, 220, 220],
'MistyRose': [255, 228, 225],
'SandyBrown': [244, 164, 96],
'DeepPink': [255, 20, 147],
'Magenta': [255, 0, 255],
'AliceBlue': [240, 248, 255],
'DarkCyan': [0, 139, 139],
'DarkSlateGrey': [47, 79, 79],
'GreenYellow': [173, 255, 47],
'DarkOrchid': [153, 50, 204],
'OliveDrab': [107, 142, 35],
'Chartreuse': [127, 255, 0],
'Peru': [205, 133, 63],
'Orange': [255, 165, 0],
'Red': [255, 0, 0],
'Wheat': [245, 222, 179],
'LightCyan': [224, 255, 255],
'LightSeaGreen': [32, 178, 170],
'BlueViolet': [138, 43, 226],
'LightSlateGrey': [119, 136, 153],
'Cyan': [0, 255, 255],
'MediumPurple': [147, 112, 219],
'MidnightBlue': [25, 25, 112],
'FireBrick': [178, 34, 34],
'PaleTurquoise': [175, 238, 238],
'PaleGoldenrod': [238, 232, 170],
'Gray': [128, 128, 128],
'MediumSeaGreen': [60, 179, 113],
'Moccasin': [255, 228, 181],
'Ivory': [255, 255, 240],
'DarkSlateBlue': [72, 61, 139],
'Beige': [245, 245, 220],
'Green': [0, 128, 0],
'SlateBlue': [106, 90, 205],
'Teal': [0, 128, 128],
'Azure': [240, 255, 255],
'LightSteelBlue': [176, 196, 222],
'DimGrey': [105, 105, 105],
'Tan': [210, 180, 140],
'AntiqueWhite': [250, 235, 215],
'SkyBlue': [135, 206, 235],
'GhostWhite': [248, 248, 255],
'MediumTurquoise': [72, 209, 204],
'FloralWhite': [255, 250, 240],
'LavenderBlush': [255, 240, 245],
'SeaGreen': [46, 139, 87],
'Lavender': [230, 230, 250],
'BlanchedAlmond': [255, 235, 205],
'DarkOliveGreen': [85, 107, 47],
'DarkSeaGreen': [143, 188, 143],
'SpringGreen': [0, 255, 127],
'Navy': [0, 0, 128],
'Orchid': [218, 112, 214],
'SaddleBrown': [139, 69, 19],
'IndianRed': [205, 92, 92],
'Snow': [255, 250, 250],
'SteelBlue': [70, 130, 180],
'MediumSlateBlue': [123, 104, 238],
'Black': [0, 0, 0],
'LightBlue': [173, 216, 230],
'Turquoise': [64, 224, 208],
'MediumVioletRed': [199, 21, 133],
'DarkViolet': [148, 0, 211],
'DarkGray': [169, 169, 169],
'Salmon': [250, 128, 114],
'DarkMagenta': [139, 0, 139],
'Tomato': [255, 99, 71],
'WhiteSmoke': [245, 245, 245],
'Goldenrod': [218, 165, 32],
'MediumSpringGreen': [0, 250, 154],
'DodgerBlue': [30, 144, 255],
'Aqua': [0, 255, 255],
'ForestGreen': [34, 139, 34],
'LemonChiffon': [255, 250, 205],
'LightSlateGray': [119, 136, 153],
'SlateGray': [112, 128, 144],
'LightGray': [211, 211, 211],
'Indigo': [75, 0, 130],
'CadetBlue': [95, 158, 160],
'LightYellow': [255, 255, 224],
'DarkOrange': [255, 140, 0],
'PowderBlue': [176, 224, 230],
'RoyalBlue': [65, 105, 225],
'Sienna': [160, 82, 45],
'Thistle': [216, 191, 216],
'Lime': [0, 255, 0],
'Seashell': [255, 245, 238],
'DarkRed': [139, 0, 0],
'LightSkyBlue': [135, 206, 250],
'YellowGreen': [154, 205, 50],
'Aquamarine': [127, 255, 212],
'LightCoral': [240, 128, 128],
'DarkSlateGray': [47, 79, 79],
'Khaki': [240, 230, 140],
'DarkGrey': [169, 169, 169],
'BurlyWood': [222, 184, 135],
'LightGoldenrodYellow': [250, 250, 210],
'MediumBlue': [0, 0, 205],
'DarkSalmon': [233, 150, 122],
'RosyBrown': [188, 143, 143],
'LightSalmon': [255, 160, 122],
'PaleVioletRed': [219, 112, 147],
'Coral': [255, 127, 80],
'Violet': [238, 130, 238],
'Grey': [128, 128, 128],
'LightGreen': [144, 238, 144],
'Linen': [250, 240, 230],
'OrangeRed': [255, 69, 0],
'DimGray': [105, 105, 105],
'Maroon': [128, 0, 0],
'LightPink': [255, 182, 193],
'MediumAquamarine': [102, 205, 170],
'OldLace': [253, 245, 230]
'Pink': (255, 192, 203),
'Blue': (0, 0, 255),
'Honeydew': (240, 255, 240),
'Purple': (128, 0, 128),
'Fuchsia': (255, 0, 255),
'LawnGreen': (124, 252, 0),
'Amethyst': (153, 102, 204),
'Crimson': (220, 20, 60),
'White': (255, 255, 255),
'NavajoWhite': (255, 222, 173),
'Cornsilk': (255, 248, 220),
'Bisque': (255, 228, 196),
'PaleGreen': (152, 251, 152),
'Brown': (165, 42, 42),
'DarkTurquoise': (0, 206, 209),
'DarkGreen': (0, 100, 0),
'MediumOrchid': (186, 85, 211),
'Chocolate': (210, 105, 30),
'PapayaWhip': (255, 239, 213),
'Olive': (128, 128, 0),
'Silver': (192, 192, 192),
'PeachPuff': (255, 218, 185),
'Plum': (221, 160, 221),
'DarkGoldenrod': (184, 134, 11),
'SlateGrey': (112, 128, 144),
'MintCream': (245, 255, 250),
'CornflowerBlue': (100, 149, 237),
'Gold': (255, 215, 0),
'HotPink': (255, 105, 180),
'DarkBlue': (0, 0, 139),
'LimeGreen': (50, 205, 50),
'DeepSkyBlue': (0, 191, 255),
'DarkKhaki': (189, 183, 107),
'LightGrey': (211, 211, 211),
'Yellow': (255, 255, 0),
'Gainsboro': (220, 220, 220),
'MistyRose': (255, 228, 225),
'SandyBrown': (244, 164, 96),
'DeepPink': (255, 20, 147),
'Magenta': (255, 0, 255),
'AliceBlue': (240, 248, 255),
'DarkCyan': (0, 139, 139),
'DarkSlateGrey': (47, 79, 79),
'GreenYellow': (173, 255, 47),
'DarkOrchid': (153, 50, 204),
'OliveDrab': (107, 142, 35),
'Chartreuse': (127, 255, 0),
'Peru': (205, 133, 63),
'Orange': (255, 165, 0),
'Red': (255, 0, 0),
'Wheat': (245, 222, 179),
'LightCyan': (224, 255, 255),
'LightSeaGreen': (32, 178, 170),
'BlueViolet': (138, 43, 226),
'LightSlateGrey': (119, 136, 153),
'Cyan': (0, 255, 255),
'MediumPurple': (147, 112, 219),
'MidnightBlue': (25, 25, 112),
'FireBrick': (178, 34, 34),
'PaleTurquoise': (175, 238, 238),
'PaleGoldenrod': (238, 232, 170),
'Gray': (128, 128, 128),
'MediumSeaGreen': (60, 179, 113),
'Moccasin': (255, 228, 181),
'Ivory': (255, 255, 240),
'DarkSlateBlue': (72, 61, 139),
'Beige': (245, 245, 220),
'Green': (0, 128, 0),
'SlateBlue': (106, 90, 205),
'Teal': (0, 128, 128),
'Azure': (240, 255, 255),
'LightSteelBlue': (176, 196, 222),
'DimGrey': (105, 105, 105),
'Tan': (210, 180, 140),
'AntiqueWhite': (250, 235, 215),
'SkyBlue': (135, 206, 235),
'GhostWhite': (248, 248, 255),
'MediumTurquoise': (72, 209, 204),
'FloralWhite': (255, 250, 240),
'LavenderBlush': (255, 240, 245),
'SeaGreen': (46, 139, 87),
'Lavender': (230, 230, 250),
'BlanchedAlmond': (255, 235, 205),
'DarkOliveGreen': (85, 107, 47),
'DarkSeaGreen': (143, 188, 143),
'SpringGreen': (0, 255, 127),
'Navy': (0, 0, 128),
'Orchid': (218, 112, 214),
'SaddleBrown': (139, 69, 19),
'IndianRed': (205, 92, 92),
'Snow': (255, 250, 250),
'SteelBlue': (70, 130, 180),
'MediumSlateBlue': (123, 104, 238),
'Black': (0, 0, 0),
'LightBlue': (173, 216, 230),
'Turquoise': (64, 224, 208),
'MediumVioletRed': (199, 21, 133),
'DarkViolet': (148, 0, 211),
'DarkGray': (169, 169, 169),
'Salmon': (250, 128, 114),
'DarkMagenta': (139, 0, 139),
'Tomato': (255, 99, 71),
'WhiteSmoke': (245, 245, 245),
'Goldenrod': (218, 165, 32),
'MediumSpringGreen': (0, 250, 154),
'DodgerBlue': (30, 144, 255),
'Aqua': (0, 255, 255),
'ForestGreen': (34, 139, 34),
'LemonChiffon': (255, 250, 205),
'LightSlateGray': (119, 136, 153),
'SlateGray': (112, 128, 144),
'LightGray': (211, 211, 211),
'Indigo': (75, 0, 130),
'CadetBlue': (95, 158, 160),
'LightYellow': (255, 255, 224),
'DarkOrange': (255, 140, 0),
'PowderBlue': (176, 224, 230),
'RoyalBlue': (65, 105, 225),
'Sienna': (160, 82, 45),
'Thistle': (216, 191, 216),
'Lime': (0, 255, 0),
'Seashell': (255, 245, 238),
'DarkRed': (139, 0, 0),
'LightSkyBlue': (135, 206, 250),
'YellowGreen': (154, 205, 50),
'Aquamarine': (127, 255, 212),
'LightCoral': (240, 128, 128),
'DarkSlateGray': (47, 79, 79),
'Khaki': (240, 230, 140),
'DarkGrey': (169, 169, 169),
'BurlyWood': (222, 184, 135),
'LightGoldenrodYellow': (250, 250, 210),
'MediumBlue': (0, 0, 205),
'DarkSalmon': (233, 150, 122),
'RosyBrown': (188, 143, 143),
'LightSalmon': (255, 160, 122),
'PaleVioletRed': (219, 112, 147),
'Coral': (255, 127, 80),
'Violet': (238, 130, 238),
'Grey': (128, 128, 128),
'LightGreen': (144, 238, 144),
'Linen': (250, 240, 230),
'OrangeRed': (255, 69, 0),
'DimGray': (105, 105, 105),
'Maroon': (128, 0, 0),
'LightPink': (255, 182, 193),
'MediumAquamarine': (102, 205, 170),
'OldLace': (253, 245, 230)
}
def getcolor(color):
@ -227,6 +230,85 @@ def getsize(width):
except ValueError:
return width
def makewire(path,checkclosed=False,donttry=False):
'''try to make a wire out of the list of edges. If the 'Wire' functions fails or the wire is not
closed if required the 'connectEdgesToWires' function is used'''
#ToDo Do not catch all exceptions
if not donttry:
try:
sh = Part.Wire(path)
isok = (not checkclosed) or sh.isClosed()
except:# BRep_API:command not done
isok = False
if donttry or not isok:
#Code from wmayer forum p15549 to fix the tolerance problem
#original tolerance = 0.00001
comp=Part.Compound(path)
sh = comp.connectEdgesToWires(False,10**(-1*(Draft.precision()-2))).Wires[0]
return sh
def arccenter2end(center,rx,ry,angle1,angledelta,xrotation=0.0):
'''calculate start and end vector and flags of an arc given in center parametrization
see http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
returns (v1,v2,largerc,sweep)'''
vr1=Vector(rx*math.cos(angle1),ry*math.sin(angle1),0)
vr2=Vector(rx*math.cos(angle1+angledelta),ry*math.sin(angle1+angledelta),0)
mxrot=FreeCAD.Matrix()
mxrot.rotateZ(xrotation)
v1 = mxrot.multiply(vr1).add(center)
v2 = mxrot.multiply(vr2).add(center)
fa = ((abs(angledelta) / math.pi) % 2) > 1 # <180deg
fs = angledelta < 0
return v1,v2,fa,fs
def arcend2center(lastvec,currentvec,rx,ry,xrotation=0.0,correction=False):
'''calculate (positive and negative) possible centers for an arc in endpoint parameterization
see http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
rotation or x-axis has to be specified in radians (CCW)
the sweepflag is interpreted as: sweepflag <==> arc is travelled clockwise
returns [(vcenter+,angle1+,angledelta+),(...-)]'''
#scalefacsign = 1 if (largeflag != sweepflag) else -1
rx = float(rx)
ry = float(ry)
v0 = lastvec.sub(currentvec)
v0 = v0.multiply(0.5)
m1=FreeCAD.Matrix()
m1.rotateZ(-xrotation) #Formular 6.5.1
v1=m1.multiply(v0)
if correction:
eparam = v1.x**2 / rx**2 + v1.y**2 / ry**2
if eparam > 1:
eproot = math.sqrt(eparam)
rx = eproot * rx
ry = eproot * ry
denom = rx**2 * v1.y**2+ ry**2 * v1.x**2
numer = rx**2 * ry**2 -denom
results=[]
if abs(numer/denom) < 10**(-1*(Draft.precision())):
scalefacpos = 0
else:
try:
scalefacpos = math.sqrt(numer/denom)
except ValueError:
print 'sqrt(%f/%f)' % (numer,denom)
scalefacpos = 0
for scalefacsign in (1,-1):
scalefac = scalefacpos * scalefacsign
vcx1 = Vector(v1.y*rx/ry,-v1.x*ry/rx,0).multiply(scalefac) # Step2 F.6.5.2
m2=FreeCAD.Matrix()
m2.rotateZ(xrotation)
centeroff = currentvec.add(lastvec)
centeroff = fcvec.scale(centeroff,.5)
vcenter = m2.multiply(vcx1).add(centeroff) # Step3 F.6.5.3
#angle1 = Vector(1,0,0).getAngle(Vector((v1.x-vcx1.x)/rx,(v1.y-vcx1.y)/ry,0)) # F.6.5.5
#angledelta = Vector((v1.x-vcx1.x)/rx,(v1.y-vcx1.y)/ry,0).getAngle(Vector((-v1.x-vcx1.x)/rx,(-v1.y-vcx1.y)/ry,0)) # F.6.5.6
#we need the right sign for the angle
angle1 = fcvec.angle(Vector(1,0,0),Vector((v1.x-vcx1.x)/rx,(v1.y-vcx1.y)/ry,0)) # F.6.5.5
angledelta = fcvec.angle(Vector((v1.x-vcx1.x)/rx,(v1.y-vcx1.y)/ry,0),Vector((-v1.x-vcx1.x)/rx,(-v1.y-vcx1.y)/ry,0)) # F.6.5.6
results.append((vcenter,angle1,angledelta))
return results,(rx,ry)
def getrgb(color):
"returns a rgb value #000000 from a freecad color"
r = str(hex(int(color[0]*255)))[2:].zfill(2)
@ -364,203 +446,159 @@ class svgHandler(xml.sax.ContentHandler):
obj = Draft.makeDimension(p1,p2,p3)
self.applyTrans(obj)
self.format(obj)
pathdata = []
self.lastdim = obj
data['d']=[]
pathcommandsre=re.compile('\s*?([mMlLhHvVaAcCqQsStTzZ])\s*?([^mMlLhHvVaAcCqQsStTzZ]*)\s*?',re.DOTALL)
for d,pointsstr in pathcommandsre.findall(' '.join(data['d'])):
#for d in pathdata:
if (d == "M"):
command = "move"
relative = False
point = []
elif (d == "m"):
command = "move"
relative = True
point = []
elif (d == "L"):
command = "line"
relative = False
point = []
elif (d == "l"):
command = "line"
relative = True
point = []
elif (d == "H"):
command = "horizontal"
relative = False
point = []
elif (d == "h"):
command = "horizontal"
relative = True
point = []
elif (d == "V"):
command = "vertical"
relative = False
point = []
elif (d == "v"):
command = "vertical"
relative = True
point = []
elif (d == "A"):
command = "arc"
relative = False
point = []
elif (d == "a"):
command = "arc"
relative = True
point = []
elif (d == "Z") or (d == "z"):
command = "close"
point = []
elif (d == "C"):
command = "cubic"
relative = False
smooth = False
point = []
elif (d == "c"):
command = "cubic"
relative = True
smooth = False
point = []
elif (d == "Q"):
command = "quadratic"
relative = False
smooth = False
point = []
elif (d == "q"):
command = "quadratic"
relative = True
smooth = False
point = []
elif (d == "S"):
command = "cubic"
relative = False
smooth = True
point = []
elif (d == "s"):
command = "cubic"
relative = True
smooth = True
point = []
elif (d == "T"):
command = "quadratic"
relative = False
smooth = True
point = []
elif (d == "t"):
command = "quadratic"
relative = True
smooth = True
point = []
pointlist = pointsstr.replace(',',' ').split()
while pointlist:
if pointlist:
point.append(float(pointlist.pop(0)))
print "command: ",command, ' point: ',point
if (len(point)==2) and (command=="move"):
if path:
sh = Part.Wire(path)
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
obj = self.doc.addObject("Part::Feature",pathname)
obj.Shape = sh
self.format(obj)
path = []
if firstvec:
lastvec = firstvec #Move relative to last move command not last draw command
relative = d.islower()
pointlist = [float(str1) for str1 in pointsstr.replace(',',' ').split()]
if (d == "M" or d == "m"):
x = pointlist.pop(0)
y = pointlist.pop(0)
if path:
#sh = Part.Wire(path)
sh = makewire(path)
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
obj = self.doc.addObject("Part::Feature",pathname)
obj.Shape = sh
self.format(obj)
path = []
#if firstvec:
# lastvec = firstvec #Move relative to last move command not last draw command
if relative:
lastvec = lastvec.add(Vector(x,-y,0))
else:
lastvec = Vector(x,-y,0)
firstvec = lastvec
print "move ",lastvec
lastpole = None
if (d == "L" or d == "l") or \
((d == 'm' or d == 'M') and pointlist) :
for x,y in zip(pointlist[0::2],pointlist[1::2]):
if relative:
lastvec = lastvec.add(Vector(point[0],-point[1],0))
command="line"
currentvec = lastvec.add(Vector(x,-y,0))
else:
lastvec = Vector(point[0],-point[1],0)
firstvec = lastvec
print "move ",lastvec
command = "line"
lastpole = None
point = []
elif (len(point)==2) and (command=="line"):
if relative:
currentvec = lastvec.add(Vector(point[0],-point[1],0))
else:
currentvec = Vector(point[0],-point[1],0)
currentvec = Vector(x,-y,0)
if not fcvec.equals(lastvec,currentvec):
seg = Part.Line(lastvec,currentvec).toShape()
print "line ",lastvec,currentvec
lastvec = currentvec
path.append(seg)
lastpole = None
point = []
elif (len(point)==1) and (command=="horizontal"):
elif (d == "H" or d == "h"):
for x in pointlist:
if relative:
currentvec = lastvec.add(Vector(point[0],0,0))
currentvec = lastvec.add(Vector(x,0,0))
else:
lasty = path[-1].y
currentvec = Vector(point[0],lasty,0)
currentvec = Vector(x,lasty,0)
seg = Part.Line(lastvec,currentvec).toShape()
lastvec = currentvec
lastpole = None
path.append(seg)
point = []
elif (len(point)==1) and (command=="vertical"):
elif (d == "V" or d == "v"):
for y in pointlist:
if relative:
currentvec = lastvec.add(Vector(0,-point[0],0))
currentvec = lastvec.add(Vector(0,-y,0))
else:
lastx = path[-1].x
currentvec = Vector(lastx,-point[0],0)
currentvec = Vector(lastx,-y,0)
seg = Part.Line(lastvec,currentvec).toShape()
lastvec = currentvec
lastpole = None
path.append(seg)
point = []
elif (len(point)==7) and (command=="arc"):
elif (d == "A" or d == "a"):
for rx,ry,xrotation, largeflag, sweepflag,x,y in \
zip(pointlist[0::7],pointlist[1::7],pointlist[2::7],pointlist[3::7],pointlist[4::7],pointlist[5::7],pointlist[6::7]):
#support for large-arc and x-rotation are missing
rx,ry,xrotation, largeflag, sweepflag = point[0:5]
if relative:
currentvec = lastvec.add(Vector(point[-2],-point[-1],0))
currentvec = lastvec.add(Vector(x,-y,0))
else:
currentvec = Vector(point[-2],-point[-1],0)
currentvec = Vector(x,-y,0)
chord = currentvec.sub(lastvec)
# perp = chord.cross(Vector(0,0,-1))
# here is a better way to find the perpendicular
if sweepflag == 1:
# clockwise
perp = fcvec.rotate2D(chord,-math.pi/2)
else:
# anticlockwise
perp = fcvec.rotate2D(chord,math.pi/2)
chord = fcvec.scale(chord,.5)
if chord.Length > rx: a = 0
else: a = math.sqrt(rx**2-chord.Length**2)
s = rx - a
perp = fcvec.scale(perp,s/perp.Length)
midpoint = lastvec.add(chord.add(perp))
seg = Part.Arc(lastvec,midpoint,currentvec).toShape()
if (not largeflag) and abs(rx-ry) < 10**(-1*Draft.precision()): # small circular arc
# perp = chord.cross(Vector(0,0,-1))
# here is a better way to find the perpendicular
if sweepflag == 1:
# clockwise
perp = fcvec.rotate2D(chord,-math.pi/2)
else:
# anticlockwise
perp = fcvec.rotate2D(chord,math.pi/2)
chord = fcvec.scale(chord,.5)
if chord.Length > rx: a = 0
else: a = math.sqrt(rx**2-chord.Length**2)
s = rx - a
perp = fcvec.scale(perp,s/perp.Length)
midpoint = lastvec.add(chord.add(perp))
seg = Part.Arc(lastvec,midpoint,currentvec).toShape()
else:# big arc or elliptical arc
solution,(rx,ry) = arcend2center(lastvec,currentvec,rx,ry,math.radians(-xrotation),True)
negsol = (largeflag != sweepflag)
vcenter,angle1,angledelta = solution[negsol]
print angle1
print angledelta
if ry > rx:
rx,ry=ry,rx
swapaxis = True
else:
swapaxis = False
print 'Elliptical arc %s rx=%f ry=%f' % (vcenter,rx,ry)
e1 = Part.Ellipse(vcenter,rx,ry)
if sweepflag:
#angledelta=-(-angledelta % (math.pi *2)) # Step4
#angledelta=(-angledelta % (math.pi *2)) # Step4
angle1 = angle1-angledelta
#angle1 = math.pi - angle1
e1a = Part.Arc(e1,angle1-swapaxis*math.radians(90),\
angle1+angledelta-swapaxis*math.radians(90))
#e1a = Part.Arc(e1,angle1-0*swapaxis*math.radians(90),angle1+angledelta-0*swapaxis*math.radians(90))
if swapaxis or xrotation > 10**(-1*Draft.precision()):
m3=FreeCAD.Matrix()
m3.move(vcenter)
rot90=FreeCAD.Matrix(0,-1,0,0,1,0) #90
#swapaxism=FreeCAD.Matrix(0,1,0,0,1,0)
if swapaxis:
m3=m3.multiply(rot90)
m3.rotateZ(math.radians(-xrotation))
m3.move(vcenter.multiply(-1))
e1a.transform(m3)
seg = e1a.toShape()
if sweepflag:
seg.reverse()
#obj = self.doc.addObject("Part::Feature",'DEBUG %s'%pathname) #DEBUG
#obj.Shape = seg #DEBUG
#seg = Part.Line(lastvec,currentvec).toShape() #DEBUG
lastvec = currentvec
lastpole = None
path.append(seg)
point = []
elif (command=="cubic") and (((smooth==False) and (len(point)==6)) or (smooth==True and (len(point)==4))) :
elif (d == "C" or d == "c") or\
(d =="S" or d == "s"):
smooth = (d == 'S' or d == 's')
if smooth:
piter = zip(pointlist[2::4],pointlist[3::4],pointlist[0::4],pointlist[1::4],pointlist[2::4],pointlist[3::4])
else:
piter = zip(pointlist[0::6],pointlist[1::6],pointlist[2::6],pointlist[3::6],pointlist[4::6],pointlist[5::6])
for p1x,p1y,p2x,p2y,x,y in piter:
if smooth:
if relative:
currentvec = lastvec.add(Vector(point[2],-point[3],0))
pole2 = lastvec.add(Vector(point[0],-point[1],0))
else:
currentvec = Vector(point[2],-point[3],0)
pole2 = Vector(point[0],-point[1],0)
if lastpole is not None and lastpole[0]=='cubic':
pole1 = lastvec.sub(lastpole[1]).add(lastvec)
else:
pole1 = lastvec
else: #not smooth
else:
if relative:
currentvec = lastvec.add(Vector(point[4],-point[5],0))
pole1 = lastvec.add(Vector(point[0],-point[1],0))
pole2 = lastvec.add(Vector(point[2],-point[3],0))
pole1 = lastvec.add(Vector(p1x,-p1y,0))
else:
currentvec = Vector(point[4],-point[5],0)
pole1 = Vector(point[0],-point[1],0)
pole2 = Vector(point[2],-point[3],0)
pole1 = Vector(p1x,-p1y,0)
if relative:
currentvec = lastvec.add(Vector(x,-y,0))
pole2 = lastvec.add(Vector(p2x,-p2y,0))
else:
currentvec = Vector(x,-y,0)
pole2 = Vector(p2x,-p2y,0)
if not fcvec.equals(currentvec,lastvec):
mainv = currentvec.sub(lastvec)
@ -568,8 +606,8 @@ class svgHandler(xml.sax.ContentHandler):
pole2v = currentvec.add(pole2)
print "cubic curve data:",mainv.normalize(),pole1v.normalize(),pole2v.normalize()
if True and \
pole1.distanceToLine(lastvec,currentvec) < 20**(-1*Draft.precision()) and \
pole2.distanceToLine(lastvec,currentvec) < 20**(-1*Draft.precision()):
pole1.distanceToLine(lastvec,currentvec) < 10**(-1*(2+Draft.precision())) and \
pole2.distanceToLine(lastvec,currentvec) < 10**(-1*(2+Draft.precision())):
print "straight segment"
seg = Part.Line(lastvec,currentvec).toShape()
else:
@ -581,66 +619,63 @@ class svgHandler(xml.sax.ContentHandler):
lastvec = currentvec
lastpole = ('cubic',pole2)
path.append(seg)
point = []
elif (command=="quadratic") and (((smooth==False) and (len(point)==4)) or (smooth==True and (len(point)==2))) :
elif (d == "Q" or d == "q") or\
(d =="T" or d == "t"):
smooth = (d == 'T' or d == 't')
if smooth:
piter = zip(pointlist[1::2],pointlist[1::2],pointlist[0::2],pointlist[1::2])
else:
piter = zip(pointlist[0::4],pointlist[1::4],pointlist[2::4],pointlist[3::4])
for px,py,x,y in piter:
if smooth:
if relative:
currentvec = lastvec.add(Vector(point[0],-point[1],0))
else:
currentvec = Vector(point[0],-point[1],0)
if lastpole is not None and lastpole[0]=='quadratic':
pole1 = lastvec.sub(lastpole[1]).add(lastvec)
pole = lastvec.sub(lastpole[1]).add(lastvec)
else:
pole1 = lastvec
else: #not smooth
pole = lastvec
else:
if relative:
currentvec = lastvec.add(Vector(point[2],-point[3],0))
pole1 = lastvec.add(Vector(point[0],-point[1],0))
pole = lastvec.add(Vector(px,-py,0))
else:
currentvec = Vector(point[2],-point[3],0)
pole1 = Vector(point[0],-point[1],0)
pole = Vector(px,-py,0)
if relative:
currentvec = lastvec.add(Vector(x,-y,0))
else:
currentvec = Vector(x,-y,0)
if not fcvec.equals(currentvec,lastvec):
if True and pole1.distanceToLine(lastvec,currentvec) < 20**(-1*Draft.precision()):
if True and \
pole.distanceToLine(lastvec,currentvec) < 20**(-1*(2+Draft.precision())):
print "straight segment"
seg = Part.Line(lastvec,currentvec).toShape()
else:
print "quadratic bezier segment"
b = Part.BezierCurve()
b.setPoles([lastvec,pole1,currentvec])
b.setPoles([lastvec,pole,currentvec])
seg = b.toShape()
print "connect ",lastvec,currentvec
lastvec = currentvec
lastpole = ('quadratic',pole1)
lastpole = ('quadratic',pole)
path.append(seg)
point = []
#while pointlist or command:
else:
if (command == "close"):
if not fcvec.equals(lastvec,firstvec):
seg = Part.Line(lastvec,firstvec).toShape()
path.append(seg)
if path: #the path should be closed by now
sh = Part.Wire(path)
if not sh.isClosed:
#Code from wmayer forum p15549 to fix the tolerance problem
comp=Part.Compound(path)
sh = comp.connectEdgesToWires(False,10**(-1*Draft.precision())).Wires[0] #original tolerance = 0.00001
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
obj = self.doc.addObject("Part::Feature",pathname)
obj.Shape = sh
self.format(obj)
path = []
if firstvec:
lastvec = firstvec #Move relative to last move command not last draw command
elif (d == "Z") or (d == "z"):
if not fcvec.equals(lastvec,firstvec):
seg = Part.Line(lastvec,firstvec).toShape()
path.append(seg)
if path: #the path should be closed by now
#sh=makewire(path,True)
sh=makewire(path,donttry=True)
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
obj = self.doc.addObject("Part::Feature",pathname)
obj.Shape = sh
self.format(obj)
path = []
if firstvec:
lastvec = firstvec #Move relative to recent draw command
point = []
command = None
if path:
sh = Part.Wire(path)
sh=makewire(path,checkclosed=False)
#sh = Part.Wire(path)
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
obj = self.doc.addObject("Part::Feature",pathname)
@ -653,9 +688,9 @@ class svgHandler(xml.sax.ContentHandler):
if name == "rect":
if not pathname: pathname = 'Rectangle'
edges = []
# if ('rx' not in data or data['rx'] < 10**(-1*Draft.precision())) and \
# ('ry' not in data or data['ry'] < 10**(-1*Draft.precision())): #negative values are invalid
if True:
if ('rx' not in data or data['rx'] < 10**(-1*Draft.precision())) and \
('ry' not in data or data['ry'] < 10**(-1*Draft.precision())): #negative values are invalid
# if True:
p1 = Vector(data['x'],-data['y'],0)
p2 = Vector(data['x']+data['width'],-data['y'],0)
p3 = Vector(data['x']+data['width'],-data['y']-data['height'],0)
@ -665,6 +700,7 @@ class svgHandler(xml.sax.ContentHandler):
edges.append(Part.Line(p3,p4).toShape())
edges.append(Part.Line(p4,p1).toShape())
else: #rounded edges
#ToTo: check for ry>rx !!!!
rx = data.get('rx')
ry = data.get('ry') or rx
rx = rx or ry
@ -672,8 +708,46 @@ class svgHandler(xml.sax.ContentHandler):
rx = data['width'] / 2.0
if ry > 2 * data['height']:
ry = data['height'] / 2.0
#TBD
# Part.Ellipse(c,rx,ry).toShape() #needs a proxy object
if rx > ry:
mj = rx
mi = ry
else:
mj = ry
mi = rx
p1=Vector(data['x']+rx,-data['y']-data['height']+ry,0)
e1=Part.Ellipse(p1,mj,mi)
p2=Vector(data['x']+data['width']-rx,-data['y']-data['height']+ry,0)
e2=Part.Ellipse(p2,mj,mi)
p3=Vector(data['x']+data['width']-rx,-data['y']-ry,0)
e3=Part.Ellipse(p3,mj,mi)
p4=Vector(data['x']+rx,-data['y']-ry,0)
e4=Part.Ellipse(p4,mj,mi)
if rx > ry:
e1a=Part.Arc(e1,math.radians(180),math.radians(270))
e2a=Part.Arc(e2,math.radians(270),math.radians(360))
e3a=Part.Arc(e3,math.radians(0),math.radians(90))
e4a=Part.Arc(e4,math.radians(90),math.radians(180))
esh=[e1a.toShape(),e2a.toShape(),e3a.toShape(),e4a.toShape()]
else:
e1a=Part.Arc(e1,math.radians(90),math.radians(180))
e2a=Part.Arc(e2,math.radians(180),math.radians(270))
e3a=Part.Arc(e3,math.radians(270),math.radians(360))
e4a=Part.Arc(e4,math.radians(0),math.radians(90))
rot90=FreeCAD.Matrix(0,-1,0,0,1,0)
esh=[]
for arc,point in ((e1a,p1),(e2a,p2),(e3a,p3),(e4a,p4)):
m1=FreeCAD.Matrix()
m1.move(point.multiply(1))
m1=m1.multiply(rot90)
m1.move(point.multiply(-1))
#m1.move(point)
arc.transform(m1)
esh.append(arc.toShape())
for esh1,esh2 in zip(esh[-1:]+esh[:-1],esh):
p1,p2 = esh1.Vertexes[-1].Point,esh2.Vertexes[0].Point
if not fcvec.equals(p1,p2):
edges.append(Part.Line(esh1.Vertexes[-1].Point,esh2.Vertexes[0].Point).toShape()) #straight segments
edges.append(esh2) # elliptical segments
sh = Part.Wire(edges)
if self.fill: sh = Part.Face(sh)
sh = self.applyTrans(sh)
@ -938,6 +1012,11 @@ def insert(filename,docname):
def export(exportList,filename):
"called when freecad exports a file"
svg_export_style = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt("svg_export_style")
if svg_export_style != 0 and svg_export_style != 1:
print "unknown svg export style, switching to Translated"
svg_export_style = 0
# finding sheet size
minx = 10000
miny = 10000
@ -950,7 +1029,13 @@ def export(exportList,filename):
if v.Point.x > maxx: maxx = v.Point.x
if v.Point.y < miny: miny = v.Point.y
if v.Point.y > maxy: maxy = v.Point.y
margin = (maxx-minx)*.01
if svg_export_style == 0:
# translated-style exports get a bit of a margin
margin = (maxx-minx)*.01
else:
# raw-style exports get no margin
margin = 0
minx -= margin
maxx += margin
miny -= margin
@ -958,26 +1043,38 @@ def export(exportList,filename):
sizex = maxx-minx
sizey = maxy-miny
miny += margin
boty = sizey+miny
# writing header
# we specify the svg width and height in FreeCAD's physical units (mm),
# and specify the viewBox so that user units maps one-to-one to mm
svg = pythonopen(filename,'wb')
svg.write('<?xml version="1.0"?>\n')
svg.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"')
svg.write(' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
svg.write('<svg')
svg.write(' width="' + str(sizex) + '" height="' + str(sizey) + '"')
svg.write(' viewBox="0 0 ' + str(sizex) + ' ' + str(sizey) + '"')
svg.write(' width="' + str(sizex) + 'mm" height="' + str(sizey) + 'mm"')
if svg_export_style == 0:
# translated-style exports have the viewbox starting at X=0, Y=0
svg.write(' viewBox="0 0 ' + str(sizex) + ' ' + str(sizey) + '"')
else:
# raw-style exports have the viewbox starting at X=0, Y=-height
# we need the funny Y here because SVG is upside down, and we
# flip the sketch right-way up with a scale later
svg.write(' viewBox="0 ' + str(sizey * -1.0) + ' ' + str(sizex) + ' ' + str(sizey) + '"')
svg.write(' xmlns="http://www.w3.org/2000/svg" version="1.1"')
svg.write('>\n')
# writing paths
for ob in exportList:
svg.write('<g transform="translate('+str(-minx)+','+str(-miny+(2*margin))+') scale(1,-1)">\n')
if svg_export_style == 0:
# translated-style exports have the entire sketch translated to fit in the X>0, Y>0 quadrant
svg.write('<g transform="translate('+str(-minx)+','+str(-miny+(2*margin))+') scale(1,-1)">\n')
else:
# raw-style exports do not translate the sketch
svg.write('<g transform="scale(1,-1)">\n')
svg.write(Draft.getSVG(ob))
svg.write('</g>\n')
# closing
svg.write('</svg>')
svg.close()
FreeCAD.Console.PrintMessage("successfully exported "+filename)

View File

@ -596,8 +596,9 @@ static PyObject * makeWedge(PyObject *self, PyObject *args)
d.SetCoord(vec.x, vec.y, vec.z);
}
BRepPrim_Wedge mkWedge(gp_Ax2(p,d), xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max);
TopoDS_Shape resultShape = mkWedge.Shell();
return new TopoShapeShellPy(new TopoShape(resultShape));
BRepBuilderAPI_MakeSolid mkSolid;
mkSolid.Add(mkWedge.Shell());
return new TopoShapeSolidPy(new TopoShape(mkSolid.Solid()));
}
catch (Standard_DomainError) {
PyErr_SetString(PyExc_Exception, "creation of wedge failed");

View File

@ -95,6 +95,14 @@ class TaskCalendar:
def __init__(self):
self.form = QtGui.QCalendarWidget()
class TaskManyTaskBoxes:
"illustrates how to add several taskboxes"
def __init__(self):
widget1 = QtGui.QCalendarWidget()
widget2 = QtGui.QWidget()
widget2.setWindowTitle("My Test Box")
text = QtGui.QLabel("testBox",widget2)
self.form = [widget1,widget2]
def createTask():
Gui.Control.addTaskWatcher([TaskWatcher(), TaskLineEdit(), TaskWatcherFilter()])

View File

@ -50,11 +50,7 @@ class VersionControl:
def __init__(self):
self.rev = ""
self.date = ""
self.range = ""
self.url = ""
self.time = ""
self.mods = "Src not modified"
self.mixed = "Src not mixed"
def extractInfo(self, srcdir):
return False
@ -67,11 +63,7 @@ class VersionControl:
for line in lines:
line = string.replace(line,'$WCREV$',self.rev)
line = string.replace(line,'$WCDATE$',self.date)
line = string.replace(line,'$WCRANGE$',self.range)
line = string.replace(line,'$WCURL$',self.url)
line = string.replace(line,'$WCNOW$',self.time)
line = string.replace(line,'$WCMODS?Src modified:Src not modified$',self.mods)
line = string.replace(line,'$WCMIXED?Src mixed:Src not mixed$',self.mixed)
content.append(line)
return content
@ -82,9 +74,7 @@ class UnknownControl(VersionControl):
return False
self.rev = "Unknown"
self.date = "Unknown"
self.range = "Unknown"
self.url = "Unknown"
self.time = "Unknown"
return True
def printInfo(self):
@ -104,13 +94,10 @@ class DebianChangelog(VersionControl):
r=re.search("bzr(\\d+)",c)
if r != None:
self.rev = r.groups()[0] + " (Launchpad)"
self.range = self.rev
t = time.localtime()
self.url = "https://code.launchpad.net/~vcs-imports/freecad/trunk"
#self.time = time.asctime()
self.date = ("%d/%02d/%02d %02d:%02d:%02d") % (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
self.time = ("%d/%02d/%02d %02d:%02d:%02d") % (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)
self.url = "https://code.launchpad.net/~vcs-imports/freecad/trunk"
return True
def printInfo(self):
@ -121,8 +108,6 @@ class BazaarControl(VersionControl):
info=os.popen("bzr log -l 1 %s" % (srcdir)).read()
if len(info) == 0:
return False
#Get the current local date
self.time = time.strftime("%Y/%m/%d %H:%M:%S")
lines=info.split("\n")
for i in lines:
r = re.match("^revno: (\\d+)$", i)
@ -151,7 +136,6 @@ class GitControl(VersionControl):
if len(info) == 0:
return False
self.rev='%04d (Git)' % (info.count('\n'))
self.range='%04d' % (info.count('\n'))
# date/time
info=os.popen("git log -1 --date=iso").read()
info=info.split("\n")
@ -159,9 +143,7 @@ class GitControl(VersionControl):
r = re.match("^Date:\\W+(\\d+-\\d+-\\d+\\W+\\d+:\\d+:\\d+)", i)
if r != None:
self.date = r.groups()[0].replace('-','/')
self.time = self.date
break
#self.time = time.strftime("%Y/%m/%d %H:%M:%S")
self.url = "Unknown"
info=os.popen("git remote -v").read()
info=info.split("\n")
@ -170,11 +152,20 @@ class GitControl(VersionControl):
if r != None:
self.url = r.groups()[0]
break
self.hash=os.popen("git log -1 --pretty=format:%H").read()
self.branch=os.popen("git branch").read().split('\n')[0][2:]
return True
def printInfo(self):
print "git"
def writeVersion(self, lines):
content = VersionControl.writeVersion(self, lines)
content.append('// Git relevant stuff\n')
content.append('#define FCRepositoryHash "%s"\n' % (self.hash))
content.append('#define FCRepositoryBranch "%s"\n' % (self.branch))
return content
class MercurialControl(VersionControl):
def extractInfo(self, srcdir):
return False

104
src/Tools/makedist.py Normal file
View File

@ -0,0 +1,104 @@
#! python
# -*- coding: utf-8 -*-
# (c) 2006 Werner Mayer LGPL
#
# Python script to make source tarballs.
#
import sys, os, getopt, tarfile, gzip, time, StringIO, platform, shutil
def main():
srcdir="."
bindir="."
dfsg=False
check=False
wta=""
try:
opts, args = getopt.getopt(sys.argv[1:], "sb:", ["srcdir=","bindir=","dfsg", "check"])
except getopt.GetoptError:
pass
for o, a in opts:
if o in ("-s", "--srcdir"):
srcdir = a
if o in ("-b", "--bindir"):
bindir = a
if o in ("--dfsg"):
dfsg = True
wta = "--worktree-attributes"
if o in ("--check"):
check = True
if dfsg:
gitattr = open("src/.gitattributes","w")
gitattr.write("zipios++ export-ignore\n")
gitattr.write("Pivy-0.5 export-ignore\n")
gitattr.write("Pivy export-ignore\n")
gitattr.close()
# revision number
info=os.popen("git rev-list HEAD").read()
revision='%04d' % (info.count('\n'))
PACKAGE_NAME = 'freecad'
version = "0.13.%s" % (revision)
DIRNAME = "%(p)s-%(v)s" % {'p': PACKAGE_NAME, 'v': version}
TARNAME = DIRNAME + '.tar'
TGZNAME = DIRNAME + '.tar.gz'
if dfsg:
TGZNAME = DIRNAME + '-dfsg.tar.gz'
verfile = open("%s/src/Build/Version.h" % (bindir), 'r')
verstream = StringIO.StringIO(verfile.read())
verfile.close()
verinfo = tarfile.TarInfo(DIRNAME + "/src/Build/Version.h")
verinfo.mode = 0660
verinfo.size = len(verstream.getvalue())
verinfo.mtime = time.time()
print "git archive %s --prefix=%s/ HEAD" % (wta, DIRNAME)
if platform.system() == 'Windows':
os.popen("git archive %s --prefix=%s/ --output=%s HEAD"
% (wta, DIRNAME, TARNAME)).read()
tar = tarfile.TarFile(mode="a", name=TARNAME)
tar.addfile(verinfo, verstream)
tar.close()
out = gzip.open(TGZNAME, "wb")
tardata = open(TARNAME, 'rb')
out.write(tardata.read())
out.close()
tardata.close()
os.remove(TARNAME)
else:
tardata = os.popen("git archive %s --prefix=%s/ HEAD"
% (wta, DIRNAME)).read()
tarstream = StringIO.StringIO(tardata)
tar = tarfile.TarFile(mode="a", fileobj=tarstream)
tar.addfile(verinfo, verstream)
tar.close()
out = gzip.open(TGZNAME, "wb")
out.write(tarstream.getvalue())
out.close()
if dfsg:
os.remove("src/.gitattributes")
print "Created " + TGZNAME
# Unpack and build
if check:
archive=tarfile.open(mode='r:gz',name=TGZNAME)
archive.extractall(bindir)
builddir = os.path.join(bindir, DIRNAME)
cwd = os.getcwd()
os.chdir(builddir)
os.system("cmake .")
os.system("make")
os.chdir(cwd)
shutil.rmtree(builddir)
if __name__ == "__main__":
main()