Fix ProjectionGroup Automatic scaling

This commit is contained in:
WandererFan
2016-10-26 19:48:33 -04:00
committed by Yorik van Havre
parent 7c4ad55ed6
commit 200ed7a675
11 changed files with 196 additions and 139 deletions

View File

@@ -71,13 +71,16 @@ DrawProjGroup::~DrawProjGroup()
short DrawProjGroup::mustExecute() const
{
if(Views.isTouched() ||
Source.isTouched()) {
return 1;
}
if (ProjectionType.isTouched())
return 1;
short result = 0;
if (!isRestoring()) {
result = Views.isTouched() ||
Source.isTouched() ||
Scale.isTouched() ||
ScaleType.isTouched() ||
ProjectionType.isTouched() ||
Anchor.isTouched();
}
if (result) return result;
return TechDraw::DrawViewCollection::mustExecute();
}
@@ -123,18 +126,14 @@ TechDraw::DrawPage * DrawProjGroup::getPage(void) const
double DrawProjGroup::calculateAutomaticScale() const
{
TechDraw::DrawPage *page = getPage();
if (page == NULL)
throw Base::Exception("No page is assigned to this feature");
if(!page->hasValidTemplate())
throw Base::Exception("Page template isn't valid");
DrawProjGroupItem *viewPtrs[10];
arrangeViewPointers(viewPtrs);
double width, height;
minimumBbViews(viewPtrs, width, height);
minimumBbViews(viewPtrs, width, height); //get 1:1 bbxs
// C++ Standard says casting bool to int gives 0 or 1
int numVertSpaces = (viewPtrs[0] || viewPtrs[3] || viewPtrs[7]) +
@@ -143,9 +142,12 @@ double DrawProjGroup::calculateAutomaticScale() const
int numHorizSpaces = (viewPtrs[0] || viewPtrs[1] || viewPtrs[2]) +
(viewPtrs[7] || viewPtrs[8] || viewPtrs[9]);
double availableX = page->getPageWidth() - spacingX.getValue() * (numVertSpaces + 1);
double availableY = page->getPageHeight() - spacingY.getValue() * (numHorizSpaces + 1);
double availableX = page->getPageWidth();
double availableY = page->getPageHeight();
double xWhite = spacingX.getValue() * (numVertSpaces + 1);
double yWhite = spacingY.getValue() * (numHorizSpaces + 1);
width += xWhite;
height += yWhite;
double scale_x = availableX / width;
double scale_y = availableY / height;
@@ -166,18 +168,25 @@ double DrawProjGroup::calculateAutomaticScale() const
i -= 1; //choosing top list if exponent -ve, bottom list for +ve exponent
//now have the appropriate scale, reapply the *10^b
return valid_scales[(exponent >= 0)][i] * pow(10, exponent);
double result = valid_scales[(exponent >= 0)][i] * pow(10, exponent);
return result;
}
QRectF DrawProjGroup::getRect() const
QRectF DrawProjGroup::getRect() const //this is current rect, not potential rect
{
DrawProjGroupItem *viewPtrs[10];
arrangeViewPointers(viewPtrs);
double width, height;
minimumBbViews(viewPtrs, width, height); //min space for 1:1 drawing
return QRectF(0,0,Scale.getValue() * width,Scale.getValue() * height);
minimumBbViews(viewPtrs, width, height); // w,h of just the views at 1:1 scale
//need to add spacingX,spacingY
double xSpace = spacingX.getValue() * 3.0 * std::max(1.0,Scale.getValue());
double ySpace = spacingY.getValue() * 2.0 * std::max(1.0,Scale.getValue());
double rectW = Scale.getValue() * width + xSpace; //scale the 1:1 w,h and add whitespace
double rectH = Scale.getValue() * height + ySpace;
return QRectF(0,0,rectW,rectH);
}
//find area consumed by Views only in 1:1 Scale
void DrawProjGroup::minimumBbViews(DrawProjGroupItem *viewPtrs[10],
double &width, double &height) const
{
@@ -185,6 +194,8 @@ void DrawProjGroup::minimumBbViews(DrawProjGroupItem *viewPtrs[10],
Base::BoundBox3d bboxes[10];
makeViewBbs(viewPtrs, bboxes, false);
//TODO: note that TLF/TRF/BLF,BRF extent a bit farther than a strict row/col arrangement would suggest.
//get widest view in each row/column
double col0w = std::max(std::max(bboxes[0].LengthX(), bboxes[3].LengthX()), bboxes[7].LengthX()),
col1w = std::max(std::max(bboxes[1].LengthX(), bboxes[4].LengthX()), bboxes[8].LengthX()),
col2w = std::max(std::max(bboxes[2].LengthX(), bboxes[5].LengthX()), bboxes[9].LengthX()),
@@ -201,18 +212,17 @@ void DrawProjGroup::minimumBbViews(DrawProjGroupItem *viewPtrs[10],
void DrawProjGroup::onChanged(const App::Property* prop)
{
//TODO: For some reason, when the projection type is changed, the isometric views show change appropriately, but the orthographic ones dont... Or vice-versa.
if (!isRestoring()) {
if ( prop == &ProjectionType ) {
//if group hasn't been added to page yet, can't scale or distribute projItems
TechDraw::DrawPage *page = getPage();
if (!isRestoring() && page) {
if ( prop == &Views ) {
recompute();
} else if (prop == &Scale) {
updateChildren(Scale.getValue());
resetPositions();
execute();
} else if (prop == &ScaleType ||
prop == &viewOrientationMatrix ||
prop == &Scale ||
prop == &Views) {
execute();
} else if (prop == &spacingX ||
prop == &spacingY) {
execute();
distributeProjections();
} else if (prop == &ScaleType) {
recompute();
}
}
TechDraw::DrawViewCollection::onChanged(prop);
@@ -275,16 +285,24 @@ bool DrawProjGroup::hasProjection(const char *viewProjType) const
App::DocumentObject * DrawProjGroup::addProjection(const char *viewProjType)
{
//if this is the first Projection added, it should automatically be the Anchor/front view
// or if viewProjType == "Front" Anchor.setValue(view)
DrawProjGroupItem *view( nullptr );
if ( checkViewProjType(viewProjType) && !hasProjection(viewProjType) ) {
std::string FeatName = getDocument()->getUniqueObjectName("ProjItem");
auto docObj( getDocument()->addObject( "TechDraw::DrawProjGroupItem", //add to Document
FeatName.c_str() ) );
if( strcmp(viewProjType,"Front") == 0 ) {
Anchor.setValue(docObj);
}
view = static_cast<TechDraw::DrawProjGroupItem *>( docObj );
view->Source.setValue( Source.getValue() );
view->ScaleType.setValue( ScaleType.getValue() );
if (ScaleType.isValue("Automatic")) {
view->ScaleType.setValue("Custom");
} else {
view->ScaleType.setValue( ScaleType.getValue() );
}
view->Scale.setValue( Scale.getValue() );
view->Type.setValue( viewProjType );
view->Label.setValue( viewProjType );
@@ -300,6 +318,7 @@ App::DocumentObject * DrawProjGroup::addProjection(const char *viewProjType)
int DrawProjGroup::removeProjection(const char *viewProjType)
{
// TODO: shouldn't be able to delete "Front" unless deleting whole group
if ( checkViewProjType(viewProjType) ) {
if( !hasProjection(viewProjType) ) {
throw Base::Exception("The projection doesn't exist in the group");
@@ -340,65 +359,38 @@ int DrawProjGroup::purgeProjections()
void DrawProjGroup::setViewOrientation(DrawProjGroupItem *v, const char *projType) const
{
Base::Vector3d dir;
// Base::Vector3d dir, xDir;
// Traditional orthographic
if(strcmp(projType, "Front") == 0) {
dir.Set(0, -1, 0);
// xDir.Set(1, 0, 0);
} else if(strcmp(projType, "Rear") == 0) {
dir.Set(0, 1, 0);
// xDir.Set(-1, 0, 0);
} else if(strcmp(projType, "Right") == 0) {
dir.Set(1, 0, 0);
// xDir.Set(0, -1, 0);
} else if(strcmp(projType, "Left") == 0) {
dir.Set(-1, 0, 0);
// xDir.Set(0, 1, 0);
} else if(strcmp(projType, "Top") == 0) {
dir.Set(0, 0, 1);
// xDir.Set(1, 0, 0);
} else if(strcmp(projType, "Bottom") == 0) {
dir.Set(0, 0, -1);
// xDir.Set(1, 0, 0);
// Isometric
} else if(strcmp(projType, "FrontTopLeft") == 0) {
dir.Set(-1,-1,1);
dir.Normalize();
// dir.Set(-1/sqrt(3), 1/sqrt(3), 1/sqrt(3));
// xDir.Set(sqrt(2)/2.0, sqrt(2.0)/2.0, 0);
} else if(strcmp(projType, "FrontTopRight") == 0) {
dir.Set(1, -1, 1);
dir.Normalize();
// dir.Set(1/sqrt(3), 1/sqrt(3), 1/sqrt(3));
// xDir.Set(sqrt(2)/2.0, -sqrt(2.0)/2.0, 0);
} else if(strcmp(projType, "FrontBottomRight") == 0) {
dir.Set(1, -1, -1);
dir.Normalize();
// dir.Set(1/sqrt(3), 1/sqrt(3), -1/sqrt(3));
// xDir.Set(sqrt(2)/2.0, -sqrt(2.0)/2.0, 0);
} else if(strcmp(projType, "FrontBottomLeft") == 0) {
dir.Set(-1, -1, -1);
dir.Normalize();
// dir.Set(-1/sqrt(3), 1/sqrt(3), -1/sqrt(3));
// xDir.Set(sqrt(2)/2.0, sqrt(2.0)/2.0, 0);
} else {
throw Base::Exception("Unknown view type in DrawProjGroup::setViewOrientation()");
}
dir = viewOrientationMatrix.getValue() * dir; //multiply std dir by transform matrix
//xDir = viewOrientationMatrix.getValue() * xDir;
v->Direction.setValue(dir);
}
@@ -408,12 +400,6 @@ void DrawProjGroup::arrangeViewPointers(DrawProjGroupItem *viewPtrs[10]) const
viewPtrs[i] = NULL;
}
auto anchorView( dynamic_cast<DrawProjGroupItem *>(Anchor.getValue()) );
if (!anchorView) { //TODO: Consider not requiring an anchor view, or allowing ones other than "Front"
throw Base::Exception("No anchor view set in DrawProjGroup::arrangeViewPointers()");
}
// Determine layout - should be either "First Angle" or "Third Angle"
const char* projType;
if (ProjectionType.isValue("Document")) {
@@ -475,7 +461,7 @@ void DrawProjGroup::makeViewBbs(DrawProjGroupItem *viewPtrs[10],
if (viewPtrs[i]) {
bboxes[i] = viewPtrs[i]->getBoundingBox();
if (!documentScale) {
double scale = 1.0 / viewPtrs[i]->Scale.getValue();
double scale = 1.0 / viewPtrs[i]->Scale.getValue(); //convert bbx to 1:1 scale
bboxes[i].ScaleX(scale);
bboxes[i].ScaleY(scale);
bboxes[i].ScaleZ(scale);
@@ -495,6 +481,7 @@ bool DrawProjGroup::distributeProjections()
arrangeViewPointers(viewPtrs);
// TODO: Work on not requiring the front view...
//WF: there is always a "Front" view. That's in the nature of the ProjGroup!
if (!viewPtrs[4]) {
return false;
}
@@ -505,6 +492,7 @@ bool DrawProjGroup::distributeProjections()
// Now that things are setup, do the spacing
double scale = Scale.getValue();
if (scale < 1.0) scale = 1.0;
double xSpacing = scale * spacingX.getValue(); //in mm
double ySpacing = scale * spacingY.getValue(); //in mm
@@ -591,6 +579,7 @@ void DrawProjGroup::resetPositions(void)
}
//TODO: Turn this into a command so it can be issued from python
//????: this sets the orientation for all views, not just Front???
void DrawProjGroup::setFrontViewOrientation(const Base::Matrix4D &newMat)
{
viewOrientationMatrix.setValue(newMat);
@@ -613,55 +602,83 @@ App::DocumentObjectExecReturn *DrawProjGroup::execute(void)
return DrawViewCollection::execute();
}
double newScale = Scale.getValue();
if (ScaleType.isValue("Automatic")) {
//Recalculate scale if Group is too big
//Recalculate scale if Group is too big or too small!
if (!checkFit(page)) {
double newScale = calculateAutomaticScale();
newScale = calculateAutomaticScale();
if(std::abs(Scale.getValue() - newScale) > FLT_EPSILON) {
Scale.setValue(newScale);
//Rebuild the DPGI's
for( const auto it : Views.getValues() ) {
auto view( dynamic_cast<DrawProjGroupItem *>(it) );
if( view ) {
view->ScaleType.setValue("Custom");
view->Scale.setValue(newScale); //not sure we have to set scale here. DVP will do it based on ScaleType
view->Scale.setStatus(App::Property::ReadOnly,true);
//view->touch();
}
}
resetPositions();
}
}
}
} else if (ScaleType.isValue("Document")) {
double docScale = page->Scale.getValue();
if(std::abs(Scale.getValue() - docScale) > FLT_EPSILON) {
Scale.setValue(docScale);
//Rebuild the DPGI's
const std::vector<App::DocumentObject *> &views = Views.getValues();
for(std::vector<App::DocumentObject *>::const_iterator it = views.begin(); it != views.end(); ++it) {
App::DocumentObject *docObj = *it;
if(docObj->getTypeId().isDerivedFrom(DrawProjGroupItem::getClassTypeId())) {
DrawProjGroupItem *view = dynamic_cast<DrawProjGroupItem *>(*it);
view->ScaleType.setValue("Document");
view->Scale.setValue(docScale);
view->Scale.setStatus(App::Property::ReadOnly,true);
//view->touch();
}
}
resetPositions();
newScale = page->Scale.getValue();
if(std::abs(Scale.getValue() - newScale) > FLT_EPSILON) {
Scale.setValue(newScale);
}
} else if (ScaleType.isValue("Custom")) {
//don't have to do anything special
}
// recalculate positions for children
if (Views.getSize()) {
updateChildren(newScale);
resetPositions();
distributeProjections();
}
//touch();
return DrawViewCollection::execute();
}
void DrawProjGroup::updateChildren(double scale)
{
for( const auto it : Views.getValues() ) {
auto view( dynamic_cast<DrawProjGroupItem *>(it) );
if( view ) {
if (ScaleType.isValue("Automatic")) {
view->ScaleType.setValue("Custom");
view->Scale.setStatus(App::Property::ReadOnly,true);
} else if (ScaleType.isValue("Document")) {
view->ScaleType.setValue("Document");
view->Scale.setStatus(App::Property::ReadOnly,true);
} else if (ScaleType.isValue("Custom")) {
view->ScaleType.setValue("Custom");
view->Scale.setStatus(App::Property::ReadOnly,true);
}
if(std::abs(view->Scale.getValue() - scale) > FLT_EPSILON) {
view->Scale.setValue(scale);
}
view->recompute();
}
}
}
//!check if ProjectionGroup fits on Page
bool DrawProjGroup::checkFit(TechDraw::DrawPage* p) const
{
bool result = true;
QRectF viewBox = getRect();
double fudge = 1.1;
double maxWidth = viewBox.width() * fudge;
double maxHeight = viewBox.height() * fudge;
if ( (maxWidth > p->getPageWidth()) ||
(maxHeight > p->getPageHeight()) ) {
result = false;
}
if (ScaleType.isValue("Automatic")) { //expand if too small
double magnifyLimit = 0.60;
if ( (maxWidth < p->getPageWidth() * magnifyLimit) &&
(maxHeight < p->getPageHeight() * magnifyLimit) ) {
result = false;
}
}
return result;
}
App::Enumeration DrawProjGroup::usedProjectionType(void)
{
//TODO: Would've been nice to have an Enumeration(const PropertyEnumeration &) constructor