예제 #1
0
    def _point_at_cb(self, event_cb):
        """`point_at` callback.

        Args:
            event_cb -- coin event callback object
        """
        event = event_cb.getEvent()
        if (event.getState() == coin.SoMouseButtonEvent.DOWN
                and event.getButton() == coin.SoMouseButtonEvent.BUTTON1):
            # Get point
            picked_point = event_cb.getPickedPoint()
            try:
                point = App.Vector(picked_point.getPoint())
            except AttributeError:
                # No picked point (outside geometry)
                msg = (translate(
                    "Render",
                    "[Point at] Target outside geometry "
                    "-- Aborting",
                ) + "\n")
                App.Console.PrintMessage(msg)
            else:
                # Make underlying object point at target point
                self.fpo.Proxy.point_at(point)
                msg = (translate(
                    "Render",
                    "[Point at] Now pointing at "
                    "({0.x}, {0.y}, {0.z})",
                ) + "\n")
                App.Console.PrintMessage(msg.format(point))
            finally:
                # Remove coin event catcher
                Gui.ActiveDocument.ActiveView.removeEventCallbackPivy(
                    coin.SoMouseButtonEvent.getClassTypeId(), self.callback)
예제 #2
0
    def on_delete_cb(self, feature, subelements):
        """Respond to delete object event (callback)."""
        delete = True

        if self.fpo.Group:
            # Project is not empty
            title = translate("Render", "Warning: Deleting Non-Empty Project")
            msg = translate(
                "Render",
                "Project is not empty. "
                "All its contents will be deleted too.\n\n"
                "Are you sure you want to continue?",
            )
            box = QMessageBox(
                QMessageBox.Warning,
                title,
                msg,
                QMessageBox.Yes | QMessageBox.No,
            )
            usr_confirm = box.exec()
            if usr_confirm == QMessageBox.Yes:
                subobjs = self.fpo.Proxy.all_views(include_groups=True)[1:]
                for obj in subobjs:
                    obj.Document.removeObject(obj.Name)
            else:
                delete = False

        return delete
예제 #3
0
 def message(self):
     """Give error message."""
     msg = (
         translate("Render", "[Render] Error: Renderer '%s' not found")
         % self.renderer
     )
     return msg
예제 #4
0
    def Activated(self):  # pylint: disable=no-self-use
        """Respond to Activated event (callback).

        This code is executed when the command is run in FreeCAD.
        It creates a new rendering project into the active document.
        """
        # First, split selection into objects and projects
        selection = Gui.Selection.getSelection()
        objs, projs = [], []
        for item in selection:
            (projs if RendererHandler.is_project(item) else objs).append(item)

        # Then, get target project.
        # We first look among projects in the selection
        # and, if none, we fall back on active document's projects
        activedoc_projects = filter(RendererHandler.is_project,
                                    App.ActiveDocument.Objects)
        try:
            target_project = next(it.chain(projs, activedoc_projects))
        except StopIteration:
            msg = (translate(
                "Render",
                "[Render] Unable to find a valid project in selection "
                "or document",
            ) + "\n")
            App.Console.PrintError(msg)
            return

        # Finally, add objects to target project and recompute
        target_project.Proxy.add_views(objs)

        App.ActiveDocument.recompute()
예제 #5
0
def user_select_template(renderer):
    """Make user select a template for a given renderer.

    This method opens a UI file dialog and asks user to select a template file.
    The returned path is the *relative* path starting from Render.TEMPLATEDIR.

    Args:
        renderer -- a renderer name (str)

    Returns:
        A string containing the (relative) path to the selected template.
    """
    try:
        handler = RendererHandler(renderer)
    except RendererNotFoundError as err:
        msg = (
            "[Render] Failed to open template selector - Renderer '%s' "
            "not found\n"
        )
        App.Console.PrintError(msg % err.renderer)
        return None
    filefilter = handler.get_template_file_filter()
    filefilter += ";; All files (*.*)"
    caption = translate("Render", "Select template")
    openfilename = QFileDialog.getOpenFileName(
        Gui.getMainWindow(), caption, TEMPLATEDIR, filefilter
    )
    template_path = openfilename[0]
    if not template_path:
        return None
    return os.path.relpath(template_path, TEMPLATEDIR)
예제 #6
0
        def add_to_group(objs, group):
            """Add objects as views to a group.

            objs -- FreeCAD objects to add
            group -- The group (App::DocumentObjectGroup) to add to
            """
            for obj in objs:
                success = False
                if (
                    hasattr(obj, "Group")
                    and not obj.isDerivedFrom("App::Part")
                    and not obj.isDerivedFrom("PartDesign::Body")
                ):
                    assert obj != group  # Just in case (infinite recursion)...
                    label = View.view_label(obj, group, True)
                    new_group = App.ActiveDocument.addObject(
                        "App::DocumentObjectGroup", label
                    )
                    new_group.Label = label
                    group.addObject(new_group)
                    add_to_group(obj.Group, new_group)
                    success = True
                if RendererHandler.is_renderable(obj):
                    View.create(source=obj, project=group)
                    success = True
                if not success:
                    msg = (
                        translate(
                            "Render",
                            "[Render] Unable to create rendering view for "
                            "object '{o}': unhandled object type",
                        )
                        + "\n"
                    )
                    App.Console.PrintWarning(msg.format(o=obj.fpo.Label))
예제 #7
0
    def point_at(self):
        """Make this object interactively point at another object.

        User will be requested to select an object to point at.
        """
        msg = (translate(
            "Render", "[Point at] Please select target (on geometry)") + "\n")
        App.Console.PrintMessage(msg)
        self.callback = Gui.ActiveDocument.ActiveView.addEventCallbackPivy(
            coin.SoMouseButtonEvent.getClassTypeId(), self._point_at_cb)
예제 #8
0
    def render(self):
        """Render project.

        This method calls proxy's 'render' method.
        """
        try:
            self.fpo.Proxy.render()
        except AttributeError as err:
            msg = translate("Render", "[Render] Cannot render: {e}") + "\n"
            App.Console.PrintError(msg.format(e=err))
예제 #9
0
    def get_rendering_string(self, view):
        """Provide a rendering string for the view of an object.

        This method selects the specialized rendering method adapted for
        'view', according to its source object type, and calls it.

        Parameters:
        view -- the view of the object to render

        Returns: a rendering string in the format of the external renderer
        for the supplied 'view'
        """
        try:
            source = view.Source

            objtype = getproxyattr(source, "type", "Object")

            name = str(source.Name)

            switcher = {
                "Object": RendererHandler._render_object,
                "PointLight": RendererHandler._render_pointlight,
                "Camera": RendererHandler._render_camera,
                "AreaLight": RendererHandler._render_arealight,
                "SunskyLight": RendererHandler._render_sunskylight,
                "ImageLight": RendererHandler._render_imagelight,
            }

            res = switcher[objtype](self, name, view)

        except (AttributeError, TypeError, AssertionError) as err:
            msg = (
                translate(
                    "Render",
                    "[Render] Cannot render view '{0}': {1} (file {2}, "
                    "line {3} in {4}). Skipping...",
                )
                + "\n"
            )
            _, _, exc_traceback = sys.exc_info()
            framestack = traceback.extract_tb(exc_traceback)[-1]
            App.Console.PrintWarning(
                msg.format(
                    getattr(view, "Label", "<No label>"),
                    err,
                    framestack.filename,
                    framestack.lineno,
                    framestack.name,
                )
            )
            return ""

        else:
            return res
예제 #10
0
 def Activated(self):
     App.ActiveDocument.openTransaction(
         translate("Render", "Create material"))
     Gui.Control.closeDialog()
     Gui.addModule("Render")
     cmds = [
         "obj = Render.make_material()",
         "Gui.Selection.clearSelection()",
         "Gui.Selection.addSelection(obj.Document.Name, obj.Name)",
         "obj.ViewObject.Document.setEdit(obj.ViewObject, 0)",
     ]
     for cmd in cmds:
         Gui.doCommand(cmd)
     App.ActiveDocument.commitTransaction()
     App.ActiveDocument.recompute()
예제 #11
0
    def __init__(self, color=QColor(127, 127, 127), use_object_color=False):
        """Initialize widget.

        Args:
            color -- RGB color used to initialize the color picker
            use_object_color -- boolean used to initialize the 'use object
                color' checkbox
        """
        super().__init__()
        self.use_object_color = use_object_color
        self.colorpicker = ColorPicker(color)
        self.checkbox = QCheckBox()
        self.checkbox.setText(translate("Render", "Use object color"))
        self.setLayout(QHBoxLayout())
        self.layout().addWidget(self.colorpicker)
        self.layout().addWidget(self.checkbox)
        self.layout().setContentsMargins(0, 0, 0, 0)
        QObject.connect(
            self.checkbox,
            SIGNAL("stateChanged(int)"),
            self.on_object_color_change,
        )
        self.checkbox.setChecked(use_object_color)
예제 #12
0
)

# ===========================================================================
#                                   Export
# ===========================================================================

Param = collections.namedtuple("Param", "name type default desc")

# IMPORTANT: Please note that, by convention, the first parameter of each
# material will be used as default color in fallback mechanisms.
# Please be careful to preserve a color-typed field as first parameter of each
# material, if you modify an existing material or you add a new one...
STD_MATERIALS_PARAMETERS = {
    "Glass": [
        Param("Color", "RGB", (1, 1, 1),
              translate("Render", "Transmitted color")),
        Param("IOR", "float", 1.5, translate("Render", "Index of refraction")),
    ],
    "Disney": [
        Param(
            "BaseColor",
            "RGB",
            (0.8, 0.8, 0.8),
            translate("Render", "Base color"),
        ),
        Param(
            "Subsurface",
            "float",
            0.0,
            translate("Render", "Subsurface coefficient"),
        ),
예제 #13
0
    def Activated(self):  # pylint: disable=no-self-use
        """Respond to Activated event (callback).

        This code is executed when the command is run in FreeCAD.
        It sets the Material property of the selected object(s).
        If the Material property does not exist in the object(s), it is
        created.
        """
        # Get selected objects
        selection = Gui.Selection.getSelection()
        if not selection:
            title = translate("Render", "Empty Selection")
            msg = translate(
                "Render",
                "Please select object(s) before applying "
                "material.",
            )
            QMessageBox.warning(None, title, msg)
            return

        # Let user pick the Material
        mats = [
            o for o in App.ActiveDocument.Objects
            if o.isDerivedFrom("App::MaterialObjectPython")
        ]
        if not mats:
            title = translate("Render", "No Material")
            msg = translate(
                "Render",
                "No Material in document. Please create a "
                "Material before applying.",
            )
            QMessageBox.warning(None, title, msg)
            return
        matlabels = [m.Label for m in mats]
        current_mats_labels = [
            o.Material.Label for o in selection if hasattr(o, "Material")
            and hasattr(o.Material, "Label") and o.Material.Label
        ]
        current_mats = [
            count for count, val in enumerate(matlabels)
            if val in current_mats_labels
        ]
        current_mat = current_mats[0] if len(current_mats) == 1 else 0

        userinput, status = QInputDialog.getItem(
            None,
            translate("Render", "Material Applier"),
            translate("Render", "Choose Material to apply to selection:"),
            matlabels,
            current_mat,
            False,
        )
        if not status:
            return

        material = next(m for m in mats if m.Label == userinput)

        # Update selected objects
        App.ActiveDocument.openTransaction("MaterialApplier")
        for obj in selection:
            # Add Material property to the object if it hasn't got one
            if "Material" not in obj.PropertiesList:
                obj.addProperty(
                    "App::PropertyLink",
                    "Material",
                    "",
                    QT_TRANSLATE_NOOP("App::Property",
                                      "The Material for this object"),
                )
            try:
                obj.Material = material
            except TypeError:
                msg = (translate(
                    "Render",
                    "Cannot apply Material to object '%s': "
                    "object's Material property is of wrong "
                    "type",
                ) + "\n")
                App.Console.PrintError(msg % obj.Label)
        App.ActiveDocument.commitTransaction()
예제 #14
0
    def render(self, external=True, wait_for_completion=False):
        """Render the project, calling an external renderer.

        Args:
            external -- flag to switch between internal/external version of
                renderer
            wait_for_completion -- flag to wait for rendering completion before
                return, in a blocking way (default to False)

        Returns:
            Output file path
        """
        obj = self.fpo
        wait_for_completion = bool(wait_for_completion)

        # Get a handle to renderer module
        try:
            renderer = RendererHandler(
                rdrname=obj.Renderer,
                linear_deflection=obj.LinearDeflection,
                angular_deflection=obj.AngularDeflection,
                transparency_boost=obj.TransparencySensitivity,
            )
        except ModuleNotFoundError:
            msg = (
                translate(
                    "Render",
                    "[Render] Cannot render project: Renderer '%s' not "
                    "found",
                )
                + "\n"
            )
            App.Console.PrintError(msg % obj.Renderer)
            return ""

        # Get the rendering template
        if obj.getTypeIdOfProperty("Template") == "App::PropertyFile":
            # Legacy template path (absolute path)
            template_path = obj.Template
        else:
            # Current template path (relative path)
            template_path = os.path.join(TEMPLATEDIR, obj.Template)

        if not os.path.isfile(template_path):
            msg = translate(
                "Render", "Cannot render project: Template not found ('%s')"
            )
            msg = "[Render] " + (msg % template_path) + "\n"
            App.Console.PrintError(msg)
            return ""

        with open(template_path, "r", encoding="utf8") as template_file:
            template = template_file.read()

        # Build a default camera, to be used if no camera is present in the
        # scene
        camstr = (
            Gui.ActiveDocument.ActiveView.getCamera()
            if App.GuiUp
            else DEFAULT_CAMERA_STRING
        )
        cam = renderer.get_camsource_string(get_cam_from_coin_string(camstr))

        # Get objects rendering strings (including lights, cameras...)
        # views = self.all_views()
        get_rdr_string = (
            renderer.get_rendering_string
            if obj.DelayedBuild
            else attrgetter("ViewResult")
        )
        if App.GuiUp:
            objstrings = [
                get_rdr_string(v)
                for v in self.all_views()
                if v.Source.ViewObject.Visibility
            ]
        else:
            objstrings = [get_rdr_string(v) for v in self.all_views()]

        # Add a ground plane if required
        if getattr(obj, "GroundPlane", False):
            objstrings.append(self.write_groundplane(renderer))

        # Merge all strings (cam, objects, ground plane...) into rendering
        # template
        renderobjs = "\n".join(objstrings)
        if "RaytracingCamera" in template:
            template = re.sub("(.*RaytracingCamera.*)", cam, template)
            template = re.sub("(.*RaytracingContent.*)", renderobjs, template)
        else:
            template = re.sub(
                "(.*RaytracingContent.*)", cam + "\n" + renderobjs, template
            )
        version_major = sys.version_info.major
        template = template.encode("utf8") if version_major < 3 else template

        # Write instantiated template into a temporary file
        fhandle, fpath = mkstemp(
            prefix=obj.Name, suffix=os.path.splitext(obj.Template)[-1]
        )
        with open(fpath, "w", encoding="utf8") as fobj:
            fobj.write(template)
        os.close(fhandle)
        obj.PageResult = fpath
        os.remove(fpath)
        assert obj.PageResult, "Rendering error: No page result"

        # Fetch the rendering parameters
        prefix = PARAMS.GetString("Prefix", "")
        if prefix:
            prefix += " "

        try:
            output = obj.OutputImage
            assert output
        except (AttributeError, AssertionError):
            output = os.path.splitext(obj.PageResult)[0] + ".png"

        try:
            width = int(obj.RenderWidth)
        except (AttributeError, ValueError, TypeError):
            width = 800

        try:
            height = int(obj.RenderHeight)
        except (AttributeError, ValueError, TypeError):
            height = 600

        # Get the renderer command on the generated temp file, with rendering
        # params
        cmd, img = renderer.render(
            obj, prefix, external, output, width, height
        )
        img = img if obj.OpenAfterRender else None
        if not cmd:
            # Command is empty (perhaps lack of data in parameters)
            return None

        # Execute renderer
        rdr_executor = RendererExecutor(cmd, img)
        rdr_executor.start()
        if wait_for_completion:
            # Useful in console mode...
            rdr_executor.join()

        # And eventually return result path
        return img