/*************************************************************************** * Copyright (c) 2011 Jürgen Riegel (juergen.riegel@web.de) * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ViewProviderImagePlane.h" using namespace Gui; PROPERTY_SOURCE(Gui::ViewProviderImagePlane, Gui::ViewProviderGeometryObject) const char* ViewProviderImagePlane::LightingEnums[] = {"One side", "Two side", nullptr}; ViewProviderImagePlane::ViewProviderImagePlane() { ADD_PROPERTY_TYPE(Lighting, (1L), "Object Style", App::Prop_None, "Set object lighting."); Lighting.setEnums(LightingEnums); texture = new SoTexture2; texture->ref(); pcCoords = new SoCoordinate3(); pcCoords->ref(); shapeHints = new SoShapeHints; shapeHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; shapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; shapeHints->ref(); sPixmap = "image-plane"; ShapeAppearance.setDiffuseColor(1.0F, 1.0F, 1.0F); } ViewProviderImagePlane::~ViewProviderImagePlane() { pcCoords->unref(); texture->unref(); shapeHints->unref(); } void ViewProviderImagePlane::attach(App::DocumentObject* pcObj) { ViewProviderGeometryObject::attach(pcObj); // NOTE: SoFCSelection node has beem removed because it led to // problems using the image as a construction plane with the // draft commands SoSeparator* shading = new SoSeparator; shading->addChild(pcCoords); SoTextureCoordinate2* textCoord = new SoTextureCoordinate2; textCoord->point.set1Value(0, 0, 0); textCoord->point.set1Value(1, 1, 0); textCoord->point.set1Value(2, 1, 1); textCoord->point.set1Value(3, 0, 1); shading->addChild(textCoord); // texture texture->model = SoTexture2::MODULATE; shading->addChild(texture); shading->addChild(shapeHints); shading->addChild(pcShapeMaterial); // plane pcCoords->point.set1Value(0, 0, 0, 0); pcCoords->point.set1Value(1, 1, 0, 0); pcCoords->point.set1Value(2, 1, 1, 0); pcCoords->point.set1Value(3, 0, 1, 0); SoFaceSet* faceset = new SoFaceSet; faceset->numVertices.set1Value(0, 4); shading->addChild(faceset); addDisplayMaskMode(shading, "Shading"); SoLightModel* lightmodel = new SoLightModel; lightmodel->model = SoLightModel::BASE_COLOR; SoSeparator* noshading = new SoSeparator; noshading->addChild(pcCoords); noshading->addChild(textCoord); noshading->addChild(texture); noshading->addChild(shapeHints); noshading->addChild(pcShapeMaterial); noshading->addChild(lightmodel); noshading->addChild(faceset); addDisplayMaskMode(noshading, "No shading"); } void ViewProviderImagePlane::setDisplayMode(const char* ModeName) { if (strcmp("Shading", ModeName) == 0) { setDisplayMaskMode("Shading"); } else if (strcmp("No shading", ModeName) == 0) { setDisplayMaskMode("No shading"); } ViewProviderGeometryObject::setDisplayMode(ModeName); } std::vector ViewProviderImagePlane::getDisplayModes() const { std::vector StrList; StrList.emplace_back("Shading"); StrList.emplace_back("No shading"); return StrList; } void ViewProviderImagePlane::onChanged(const App::Property* prop) { if (prop == &Lighting) { if (Lighting.getValue() == 0) { shapeHints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING; } else { shapeHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; } } ViewProviderGeometryObject::onChanged(prop); } void ViewProviderImagePlane::setupContextMenu(QMenu* menu, QObject* receiver, const char* member) { Gui::ActionFunction* func = new Gui::ActionFunction(menu); QAction* action = menu->addAction(QObject::tr("Change Image")); action->setIcon(QIcon(QLatin1String("images:image-scaling.svg"))); func->trigger(action, [this]() { this->manipulateImage(); }); ViewProviderGeometryObject::setupContextMenu(menu, receiver, member); } bool ViewProviderImagePlane::doubleClicked() { manipulateImage(); return true; } void ViewProviderImagePlane::manipulateImage() { auto dialog = new TaskImageDialog(getObject()); Gui::Control().showDialog(dialog); } void ViewProviderImagePlane::resizePlane(float xsize, float ysize) { pcCoords->point.set1Value(0, -(xsize / 2), -(ysize / 2), 0.0); pcCoords->point.set1Value(1, +(xsize / 2), -(ysize / 2), 0.0); pcCoords->point.set1Value(2, +(xsize / 2), +(ysize / 2), 0.0); pcCoords->point.set1Value(3, -(xsize / 2), +(ysize / 2), 0.0); } void ViewProviderImagePlane::loadImage() { Image::ImagePlane* imagePlane = static_cast(pcObject); std::string fileName = imagePlane->ImageFile.getValue(); if (!fileName.empty()) { QImage impQ; if (isSvgFile(fileName.c_str())) { impQ = loadSvg(fileName.c_str()); } else { impQ = loadRaster(fileName.c_str()); } QSizeF size = getSizeInMM(impQ); setPlaneSize(size, impQ); convertToSFImage(impQ); } } void ViewProviderImagePlane::setPlaneSize(const QSizeF& size, const QImage& img) { if (!img.isNull()) { Image::ImagePlane* imagePlane = static_cast(pcObject); // When restoring the document or importing a ImagePlane by eg. pasting it // preserve the X and Y size. if (!isRestoring() && !pcObject->testStatus(App::ObjectStatus::ObjImporting)) { imagePlane->XSize.setValue(size.width()); imagePlane->YSize.setValue(size.height()); } imagePlane->XPixelsPerMeter = img.dotsPerMeterX(); imagePlane->YPixelsPerMeter = img.dotsPerMeterY(); } } QImage ViewProviderImagePlane::loadRaster(const char* fileName) const { QImage img; img.load(QString::fromUtf8(fileName)); return img; } void ViewProviderImagePlane::reloadIfSvg() { Image::ImagePlane* imagePlane = static_cast(pcObject); std::string fileName = imagePlane->ImageFile.getValue(); if (isSvgFile(fileName.c_str())) { double xsize = imagePlane->XSize.getValue(); double ysize = imagePlane->YSize.getValue(); QImage impQ = loadSvgOfSize(fileName.c_str(), QSizeF(xsize, ysize)); convertToSFImage(impQ); } } QImage ViewProviderImagePlane::loadSvg(const char* filename) const { QSizeF defaultSize = defaultSizeOfSvg(filename); QPixmap px = BitmapFactory().pixmapFromSvg(filename, defaultSize); return px.toImage(); } QImage ViewProviderImagePlane::loadSvgOfSize(const char* filename, const QSizeF& size) const { QSizeF psize = pixelSize(filename, size); QPixmap px = BitmapFactory().pixmapFromSvg(filename, psize); return px.toImage(); } bool ViewProviderImagePlane::isSvgFile(const char* filename) const { QFileInfo fi(QString::fromUtf8(filename)); return (fi.suffix().toLower() == QLatin1String("svg")); } QSizeF ViewProviderImagePlane::defaultSizeOfSvg(const char* filename) const { QSvgRenderer svg; svg.load(QString::fromUtf8(filename)); return svg.defaultSize(); } QSizeF ViewProviderImagePlane::getSizeInMM(const QImage& img) const { double xPixelsPerM = img.dotsPerMeterX(); double width = img.width(); width = width * 1000 / xPixelsPerM; double yPixelsPerM = img.dotsPerMeterY(); double height = img.height(); height = height * 1000 / yPixelsPerM; QSizeF sizef; sizef.setWidth(width); sizef.setHeight(height); return sizef; } QSizeF ViewProviderImagePlane::pixelSize(const char* filename, const QSizeF& size) const { QImage tmp; if (tmp.load(QString::fromUtf8(filename))) { double xPixelsPerM = tmp.dotsPerMeterX(); double yPixelsPerM = tmp.dotsPerMeterY(); double width = size.width(); double height = size.height(); width = width * xPixelsPerM / 1000; height = height * yPixelsPerM / 1000; return {width, height}; } return size; } void ViewProviderImagePlane::convertToSFImage(const QImage& img) { if (!img.isNull()) { SoSFImage sfimg; // convert to Coin bitmap BitmapFactory().convert(img, sfimg); texture->image = sfimg; } } void ViewProviderImagePlane::updateData(const App::Property* prop) { Image::ImagePlane* pcPlaneObj = static_cast(pcObject); if (prop == &pcPlaneObj->XSize || prop == &pcPlaneObj->YSize) { float xsize = pcPlaneObj->XSize.getValue(); float ysize = pcPlaneObj->YSize.getValue(); resizePlane(xsize, ysize); reloadIfSvg(); } else if (prop == &pcPlaneObj->ImageFile) { loadImage(); } else { Gui::ViewProviderGeometryObject::updateData(prop); } }