Exemplo n.º 1
0
    def __init__(self, project: 'Project') -> None:
        BaseViewWidget.__init__(self)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.project = project

        self.image_view_cache = {}

        self.compositor = CompositeManager()

        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)

        self.debug_renderer = DebugRender(self)

        self.render_commands = StackupRenderCommands()
        self.__cad_cache = CADCache(self.project)
        self.__sel_cache = SelectionHighlightCache(self.project)

        # TODO, currently broken
        self.poly_renderer = CachedPolygonRenderer(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.set_scale(1./100000)

        self.boardViewState = BoardViewState()
        self.boardViewState.changed.connect(self.update)
Exemplo n.º 2
0
    def __init__(self, project):
        BaseViewWidget.__init__(self)
        self.project = project

        self.image_view_cache = { }

        self.pad_renderer = PadRender(self)
        self.dip_renderer = DIPRender(self)
        self.smd_renderer = SMDRender(self)
        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.text_batch = TextBatcher(self.gls.text)
        self.poly_renderer = CachedPolygonRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)
        self.passive_renderer = PassiveRender(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.transform = translate(-0.9, -0.9).dot(scale(1./100000))
Exemplo n.º 3
0
    def __init__(self, project):
        BaseViewWidget.__init__(self)
        self.project = project

        self.image_view_cache = { }

        self.pad_renderer = PadRender(self)
        self.dip_renderer = DIPRender(self)
        self.smd_renderer = SMDRender(self)
        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.text_batch = TextBatcher(self.gls.text)
        self.poly_renderer = CachedPolygonRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)
        self.passive_renderer = PassiveRender(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.transform = translate(-0.9, -0.9).dot(scale(1./100000))
Exemplo n.º 4
0
class BoardViewWidget(BaseViewWidget):
    def __init__(self, project):
        BaseViewWidget.__init__(self)
        self.project = project

        self.image_view_cache = { }

        self.pad_renderer = PadRender(self)
        self.dip_renderer = DIPRender(self)
        self.smd_renderer = SMDRender(self)
        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.text_batch = TextBatcher(self.gls.text)
        self.poly_renderer = CachedPolygonRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)
        self.passive_renderer = PassiveRender(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.transform = translate(-0.9, -0.9).dot(scale(1./100000))



    def text_color(self):
        return [1,1,1]

    def current_layer_hack(self):
        return self.viewState.current_layer

    def color_for_pad(self, pad):
        if pad.th_diam != 0:
            return [0.5, 0.5, 0.5]

        return self.color_for_layer(pad.layer)

    def color_for_trace(self, trace):
        return self.color_for_layer(trace.layer)

    def color_for_layer(self, layer):
        return list(layer.color)

    def sel_colormod(self, t, oldcolor):
        if t:
            return [1,1,1,1]
        return oldcolor



    def image_view_cache_load(self, il):
        key = id(il)
        if key not in self.image_view_cache:
            iv = ImageView(il)
            iv.initGL(self.gls)
            self.image_view_cache[key] = iv

        return self.image_view_cache[key]

    def reinit(self):
        self.pad_renderer.initializeGL(self, self.gls)
        self.dip_renderer.initializeGL(self.gls)
        self.smd_renderer.initializeGL(self.gls)
        self.trace_renderer.initializeGL(self.gls)
        self.via_renderer.initializeGL(self.gls)
        self.text_batch.initializeGL()
        self.poly_renderer.initializeGL()
        self.hairline_renderer.initializeGL()

        for i in list(self.image_view_cache.values()):
            i.initGL()

    def getVisibleArtwork(self):
        objects = []
        objects += self.project.artwork.vias
        objects += self.project.artwork.traces
        objects += self.project.artwork.polygons
        objects += self.project.artwork.airwires
        return objects


    def render_component(self, mat, cmp, render_mode=RENDER_STANDARD, render_hint=RENDER_HINT_NORMAL):
        if not self.layer_visible_m(cmp.on_layers()):
            return

        if isinstance(cmp, DIPComponent):
            self.dip_renderer.render(mat, cmp, render_mode, render_hint)
        elif isinstance(cmp, SMD4Component):
            self.smd_renderer.render(mat, cmp, render_mode, render_hint)
        elif isinstance(cmp, PassiveComponent):
            self.passive_renderer.render(mat, cmp, render_mode, render_hint)
        else:
            pass
            #raise TypeError("Can't render %s" % cmp)

        cm = mat.dot(cmp.matrix)

        for pad in cmp.get_pads():
            pad_render_mode = render_mode
            if not pad.is_through() and not self.layer_visible(pad.layer):
                continue

            if pad in self.selectionList:
                pad_render_mode |= RENDER_SELECTED
            self.pad_renderer.render(cm, pad, pad_render_mode, render_hint)


    def layer_visible(self, l):
        return l is self.viewState.current_layer or self.viewState.draw_other_layers

    def layer_visible_m(self, l):
        return self.viewState.current_layer in l or self.viewState.draw_other_layers

    def render(self):
        t_render_start = time.time()

        # zero accuum buffers for restarts
        self.trace_renderer.restart()
        self.via_renderer.restart()
        self.text_batch.restart()
        self.poly_renderer.restart()
        self.hairline_renderer.restart()

        stackup_layer = self.viewState.current_layer
        if stackup_layer is None:
            return

        # Render all images down onto the layer
        with Timer() as il_timer:
            if self.viewState.show_images:
                for l in stackup_layer.imagelayers:
                    self.image_view_cache_load(l).render(self.viewState.glMatrix)

        # Now render features
        self.lt = time.time()


        GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)

        artwork = self.getVisibleArtwork()

        # Build rendering batches
        with Timer() as t_aw:
            for i in artwork:
                rs = RENDER_SELECTED if i in self.selectionList else 0

                if isinstance(i, Trace):
                    if self.layer_visible(i.layer):
                        self.trace_renderer.deferred(i, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Via):
                    if self.layer_visible_m(i.viapair.all_layers):
                        self.via_renderer.deferred(i.pt, i.r, 0, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Polygon):
                    if self.layer_visible(i.layer):
                        self.poly_renderer.deferred(i, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Airwire):
                    self.hairline_renderer.deferred(i.p0, i.p1, AIRWIRE_COLOR, None, RENDER_HINT_NORMAL)
                else:
                    raise NotImplementedError()


        with Timer() as cmp_timer:
            for cmp in self.project.artwork.components:
                render_state = 0
                if cmp in self.selectionList:
                    render_state |= RENDER_SELECTED
                self.render_component(self.viewState.glMatrix, cmp, render_state)

        with Timer() as other_timer:
            super(BoardViewWidget, self).render()

        def ly_order_func(layer):
            # We always draw the current layer last
            if layer is self.viewState.current_layer:
                return 1

            return -layer.order

        with Timer() as t:
            # Draw all the layers
            layers = sorted(self.project.stackup.layers, key=ly_order_func)
            for layer in layers:
                self.trace_renderer.render_deferred_layer(self.viewState.glMatrix, layer)
                self.poly_renderer.render(self.viewState.glMatrix, layer)
                self.text_batch.render(key=layer)
                self.hairline_renderer.render_group(self.viewState.glMatrix, layer)

            self.via_renderer.render(self.viewState.glMatrix)

            # Render the non-layer text
            self.text_batch.render()
            self.hairline_renderer.render_group(self.viewState.glMatrix, None)

            self.hairline_renderer.render_group(self.viewState.glWMatrix, "OVERLAY_VS")
            #self.hairline_renderer.render_group(self.viewState.glMatrix, "OVERLAY_VS")

            GL.glFinish()

        all_time = time.time() - t_render_start
        print("Render time all: %f ot: %f cmp: %f aw: %f gl: %f" % (all_time, other_timer.interval, cmp_timer.interval, t_aw.interval, t.interval))
Exemplo n.º 5
0
class BoardViewWidget(BaseViewWidget):
    def __init__(self, project: 'Project') -> None:
        BaseViewWidget.__init__(self)
        self.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.project = project

        self.image_view_cache = {}

        self.compositor = CompositeManager()

        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)

        self.debug_renderer = DebugRender(self)

        self.render_commands = StackupRenderCommands()
        self.__cad_cache = CADCache(self.project)
        self.__sel_cache = SelectionHighlightCache(self.project)

        # TODO, currently broken
        self.poly_renderer = CachedPolygonRenderer(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.set_scale(1./100000)

        self.boardViewState = BoardViewState()
        self.boardViewState.changed.connect(self.update)


    def resizeGL(self, width: int, height: int) -> None:
        super(BoardViewWidget, self).resizeGL(width, height)

        self.compositor.resize(width, height)

    def text_color(self) -> List[float]:
        return [1, 1, 1]

    def current_layer_hack(self) -> 'Layer':
        return self.boardViewState.current_layer

    def color_for_pad(self, pad) -> List[float]:
        if pad.th_diam != 0:
            return [0.5, 0.5, 0.5]

        return self.color_for_layer(pad.layer)

    def color_for_trace(self, trace: Trace) -> List[float]:
        return self.color_for_layer(trace.layer)

    def color_for_layer(self, layer: Layer) -> List[float]:
        return list(layer.color)

    def sel_colormod(self, t: bool, oldcolor: List[float]) -> List[float]:
        if t:
            return [1, 1, 1, 1]
        return oldcolor

    def image_view_cache_load(self, il: 'ImageLayer') -> ImageView:
        key = id(il)
        if key not in self.image_view_cache:
            iv = ImageView(il)
            iv.initGL(self.gls)
            self.image_view_cache[key] = iv

        return self.image_view_cache[key]

    def reinit(self) -> None:
        self.trace_renderer.initializeGL(self.gls)
        self.via_renderer.initializeGL(self.gls)
        self.poly_renderer.initializeGL()
        self.hairline_renderer.initializeGL()
        self.debug_renderer.initializeGL(self.gls)

        self.compositor.initializeGL(self.gls, self.width(), self.height())

        for i in list(self.image_view_cache.values()):
            i.initGL()

    def current_side(self) -> None:
        side = self.project.stackup.side_for_layer(self.boardViewState.current_layer)
        return side

    def vp_is_visible(self, via_pair: ViaPair) -> bool:
        if self.boardViewState.current_layer is None:
            return False

        if self.boardViewState.render_mode == MODE_CAD:
            return True
        else:
            # In layer mode, viapair is visible if we're on any layer
            layer = self.boardViewState.current_layer
            f, s = via_pair.layers
            return f.order <= layer.order <= s.order

    def get_visible_point_cloud(self) -> List[Vec2]:
        bboxes = [i.bbox for i in self.getVisible()]
        points = []

        # Create points from the AABB corners of geometry
        # TODO: stop using AABB details, use convex hull points
        for bb in bboxes:
            points.append(bb.tl)
            points.append(bb.tr)
            points.append(bb.bl)
            points.append(bb.br)

        if self.boardViewState.render_mode == MODE_TRACE and self.boardViewState.show_images and len(self.boardViewState.current_layer.imagelayers) > 0:
            for i in self.boardViewState.current_layer.imagelayers:
                points.extend(i.get_corner_points())

        if not points:
            return

        return points

    def getVisible(self) -> Sequence[Geom]:
        objects = []

        if self.boardViewState.render_mode == MODE_CAD and not self.boardViewState.show_trace_mode_geom:
            return

        # Add visible vias
        vp_visible = {}
        for i in self.project.stackup.via_pairs:
            vp_visible[i] = self.vp_is_visible(i)

        for via in self.project.artwork.vias:
            if vp_visible[via.viapair]:
                objects.append(via)

        # Traces
        for trace in self.project.artwork.traces:
            if self.layer_visible(trace.layer):
                objects.append(trace)

        for polygon in self.project.artwork.polygons:
            if self.layer_visible(polygon):
                objects.append(polygon)

        if self.boardViewState.render_mode == MODE_CAD:
            objects.extend(self.project.artwork.components)
            for cmp in self.project.artwork.components:
                objects.extend(cmp.get_pads())
        elif self.boardViewState.current_layer is None:
            pass
        else:
            cur_side = self.current_side()
            for cmp in self.project.artwork.components:
                if cmp.side == cur_side:
                    objects.append(cmp)

                for pad in cmp.get_pads():
                    if pad.is_through():
                        objects.append(pad)
                    elif pad.side == cur_side:
                        objects.append(pad)

        # TODO: Airwires are always visible
        objects += self.project.artwork.airwires

        return objects

    def _layer_visible(self, l: Layer) -> bool:
        if self.boardViewState.render_mode == MODE_CAD:
            return True
        else:
            return l is self.boardViewState.current_layer

    def layer_visible(self, l: Layer) -> bool:
        return self.__layer_visible_lut[l.number]

    def layer_visible_m(self, l: Layer) -> bool:
        return self.boardViewState.current_layer in l

    def query_point(self, pt: Point2) -> Optional[Geom]:
        # todo: don't use this function anywhere, or introduce some picking heuristic
        all_aw = self.query_point_multiple(pt)
        if not all_aw:
            return None

        # Return an arbitrary element
        return all_aw.pop()

    def query_point_multiple(self, pt: Point2) -> Sequence[Geom]:
        all_aw = set(self.project.artwork.query_point_multiple(pt))
        vis_aw = set(self.getVisible())
        return vis_aw.intersection(all_aw)

    def __render_top_half(self) -> None:
        """
        :return:
        """

        # Forcibly init all layers
        for l in self.project.stackup.layers:
            self.render_commands.layers[l]

        for v in self.project.stackup.via_pairs:
            self.render_commands.vias[v]

        self.render_commands.clear()

        # Update CAD cache
        self.__cad_cache.update_if_necessary()
        self.__cad_cache.extendTo(self.render_commands)

        # Update Selection cache
        self.__sel_cache.update_if_necessary(self.selectionList)

        for k, v in self.render_commands.layers.items():
            GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Layer %r" % k)
            with self.compositor.get(k):
                self.trace_renderer.render_va(v.va_traces, self.viewState.glMatrix, COL_LAYER_MAIN)
            GL.glPopDebugGroup()

                # TODO: Render Text

        # Draw all the viapairs
        for k, v in self.render_commands.vias.items():
            GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1, "ViaPair %r" % k)
            with self.compositor.get(k):
                self.via_renderer.render_filled(self.viewState.glMatrix, v.va_vias)
            GL.glPopDebugGroup()

        # Draw the multilayer components
        GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Multi Cmp")
        with self.compositor.get("MULTI"):
            self.via_renderer.render_filled(self.viewState.glMatrix, self.render_commands.multi.va_vias)
            # TODO: Render text
        GL.glPopDebugGroup()

        # Draw the front and back sides
        for k, v in self.render_commands.sides.items():
            GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Cmp %r" % k)
            with self.compositor.get(("LINEART", k)):
                self.hairline_renderer.render_va(self.viewState.glMatrix, v.va_outlines, COL_CMP_LINE)
                # TODO: Render text
            GL.glPopDebugGroup()

        # Just create (don't actually bind) the overlay layer
        GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Overlay")
        with self.compositor.get("OVERLAY"):
            self.hairline_renderer.render_va(self.viewState.glMatrix, self.__cad_cache.airwire_va, COL_SEL)
            self.hairline_renderer.render_va(self.viewState.glMatrix, self.__sel_cache.thinline_va, COL_SEL)
            self.trace_renderer.render_va(self.__sel_cache.thickline_va, self.viewState.glMatrix, COL_SEL)
            self.via_renderer.render_filled(self.viewState.glMatrix, self.__sel_cache.via_va, COL_SEL)
        GL.glPopDebugGroup()

        GL.glPushDebugGroup(GL.GL_DEBUG_SOURCE_APPLICATION, 1, -1, "Tool")
        self.render_tool()
        GL.glPopDebugGroup()

    def render_mode_cad(self) -> None:

        # Composite all the layers
        GL.glDisable(GL.GL_DEPTH_TEST)
        GL.glClear(GL.GL_COLOR_BUFFER_BIT)

        draw_things = []

        # Prepare to draw all the layers
        for i in self.project.stackup.layers:
            if not self.layer_visible(i):
                continue

            draw_things.append((i, self.color_for_layer(i)))

        # and the via pairs
        for i in self.project.stackup.via_pairs:
            if not self.vp_is_visible(i):
                continue

            draw_things.append((i, (255, 0, 255)))

        # Now, sort them based on an order in which layers are drawn from bottom-to-top
        # (unless the board is flipped, in which case, top-to-bottom), followed by the currently selected layer
        # Via pairs are draw immediately _after_ the artwork of the layer

        def layer_key(a):
            lt, _ = a
            if isinstance(lt, Layer):
                a_l = lt
                b = 0
            elif isinstance(lt, ViaPair):
                a_l = lt.layers[0]
                b = 1
            else:
                raise ValueError("Unknown layer key type for %r" % type(a))

            # We always draw the current layer last
            if a_l == self.boardViewState.current_layer:
                a = 1
            else:
                a = -a_l.order

            return (a, b)

        draw_things.sort(key=layer_key)
        draw_things.insert(0, (("LINEART", SIDE.Top), (255, 255, 255)))
        draw_things.append(("MULTI", (255, 255, 255)))
        draw_things.append((("LINEART", SIDE.Bottom), (255, 255, 255)))

        with self.compositor.composite_prebind() as pb:
            for key, color in draw_things:
                pb.composite(key, color)

            pb.composite("OVERLAY", (255, 255, 255))

        return

    def render_mode_trace(self) -> None:

        # Composite all the layers
        GL.glDisable(GL.GL_DEPTH_TEST)
        GL.glClear(GL.GL_COLOR_BUFFER_BIT)

        layer = self.boardViewState.current_layer
        if layer is None:
            return

        # Draw the imagery
        stackup_layer = self.boardViewState.current_layer

        if stackup_layer is not None and self.boardViewState.show_images and (len(stackup_layer.imagelayers) > 0):
            images = list(stackup_layer.imagelayers)
            i = self.boardViewState.layer_permute % len(images)
            images_cycled = images[i:] + images[:i]
            for l in images_cycled:
                self.image_view_cache_load(l).render(self.viewState.glMatrix)

        if not self.boardViewState.show_trace_mode_geom:
            return

        with self.compositor.composite_prebind() as pb:

            # Render the traced geometry
            color = self.color_for_layer(layer)
            pb.composite(layer, color)

            # Render any viapairs on this layer
            for via_pair in self.project.stackup.via_pairs:
                if self.vp_is_visible(via_pair):
                    pb.composite(via_pair, (255, 0, 255))

            # Render the line-art
            side = self.project.stackup.side_for_layer(layer)
            pb.composite(("LINEART", side), (255, 255, 255))

            # Multilayer through-holes
            pb.composite("MULTI", (255, 255, 255))

            # And the tool overlay
            pb.composite("OVERLAY", (255, 255, 255))

    def render(self) -> None:
        with Timer() as t_render:
            # Update the layer-visible check
            self.__layer_visible_lut = [self._layer_visible(i) for i in self.project.stackup.layers]

            # zero accuum buffers for restarts
            self.trace_renderer.restart()
            self.poly_renderer.restart()

            # Update the Compositor
            self.compositor.restart()

            # Fill colors
            self.compositor.set_color_table(
                [
                    (255, 255, 255, 255),  # Color 0 is always the Layer current color (ignored)
                    (255, 255, 255, 255),  # Color of Text
                    (255, 255, 255, 255),  # Color of Selection
                    (128, 128, 128, 255),  # Color of Vias
                    (128, 128, 0, 255),  # Color of Airwires
                    (190, 190, 0, 255),
                ]
            )

            self.__render_top_half()

            if self.boardViewState.render_mode == MODE_CAD:
                # Render layer stack bottom to top
                self.render_mode_cad()

            elif self.boardViewState.render_mode == MODE_TRACE:
                # Render a single layer for maximum contrast
                self.render_mode_trace()

            self.debug_renderer.render()
Exemplo n.º 6
0
class BoardViewWidget(BaseViewWidget):
    def __init__(self, project):
        BaseViewWidget.__init__(self)
        self.project = project

        self.image_view_cache = { }

        self.pad_renderer = PadRender(self)
        self.dip_renderer = DIPRender(self)
        self.smd_renderer = SMDRender(self)
        self.trace_renderer = TraceRender(self)
        self.via_renderer = THRenderer(self)
        self.text_batch = TextBatcher(self.gls.text)
        self.poly_renderer = CachedPolygonRenderer(self)
        self.hairline_renderer = HairlineRenderer(self)
        self.passive_renderer = PassiveRender(self)

        # Initial view is a normalized 1-1-1 area.
        # Shift to be 10cm max
        self.viewState.transform = translate(-0.9, -0.9).dot(scale(1./100000))



    def text_color(self):
        return [1,1,1]

    def current_layer_hack(self):
        return self.viewState.current_layer

    def color_for_pad(self, pad):
        if pad.th_diam != 0:
            return [0.5, 0.5, 0.5]

        return self.color_for_layer(pad.layer)

    def color_for_trace(self, trace):
        return self.color_for_layer(trace.layer)

    def color_for_layer(self, layer):
        return list(layer.color)

    def sel_colormod(self, t, oldcolor):
        if t:
            return [1,1,1,1]
        return oldcolor



    def image_view_cache_load(self, il):
        key = id(il)
        if key not in self.image_view_cache:
            iv = ImageView(il)
            iv.initGL(self.gls)
            self.image_view_cache[key] = iv

        return self.image_view_cache[key]

    def reinit(self):
        self.pad_renderer.initializeGL(self, self.gls)
        self.dip_renderer.initializeGL(self.gls)
        self.smd_renderer.initializeGL(self.gls)
        self.trace_renderer.initializeGL(self.gls)
        self.via_renderer.initializeGL(self.gls)
        self.text_batch.initializeGL()
        self.poly_renderer.initializeGL()
        self.hairline_renderer.initializeGL()

        for i in list(self.image_view_cache.values()):
            i.initGL()

    def getVisibleArtwork(self):
        objects = []
        objects += self.project.artwork.vias
        objects += self.project.artwork.traces
        objects += self.project.artwork.polygons
        objects += self.project.artwork.airwires
        return objects


    def render_component(self, mat, cmp, render_mode=RENDER_STANDARD, render_hint=RENDER_HINT_NORMAL):
        if not self.layer_visible_m(cmp.on_layers()):
            return

        if isinstance(cmp, DIPComponent):
            self.dip_renderer.render(mat, cmp, render_mode, render_hint)
        elif isinstance(cmp, SMD4Component):
            self.smd_renderer.render(mat, cmp, render_mode, render_hint)
        elif isinstance(cmp, PassiveComponent):
            self.passive_renderer.render(mat, cmp, render_mode, render_hint)
        else:
            pass
            #raise TypeError("Can't render %s" % cmp)

        cm = mat.dot(cmp.matrix)

        for pad in cmp.get_pads():
            pad_render_mode = render_mode
            if not pad.is_through() and not self.layer_visible(pad.layer):
                continue

            if pad in self.selectionList:
                pad_render_mode |= RENDER_SELECTED
            self.pad_renderer.render(cm, pad, pad_render_mode, render_hint)


    def layer_visible(self, l):
        return l is self.viewState.current_layer or self.viewState.draw_other_layers

    def layer_visible_m(self, l):
        return self.viewState.current_layer in l or self.viewState.draw_other_layers

    def render(self):
        t_render_start = time.time()

        # zero accuum buffers for restarts
        self.trace_renderer.restart()
        self.via_renderer.restart()
        self.text_batch.restart()
        self.poly_renderer.restart()
        self.hairline_renderer.restart()

        stackup_layer = self.viewState.current_layer
        if stackup_layer is None:
            return

        # Render all images down onto the layer
        with Timer() as il_timer:
            if self.viewState.show_images:
                images = list(stackup_layer.imagelayers)
                i = self.viewState.layer_permute % len(images)
                images_cycled = images[i:] + images[:i]
                for l in images_cycled:
                    self.image_view_cache_load(l).render(self.viewState.glMatrix)

        # Now render features
        self.lt = time.time()


        GL.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA)

        artwork = self.getVisibleArtwork()

        # Build rendering batches
        with Timer() as t_aw:
            for i in artwork:
                rs = RENDER_SELECTED if i in self.selectionList else 0

                if isinstance(i, Trace):
                    if self.layer_visible(i.layer):
                        self.trace_renderer.deferred(i, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Via):
                    if self.layer_visible_m(i.viapair.all_layers):
                        self.via_renderer.deferred(i.pt, i.r, 0, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Polygon):
                    if self.layer_visible(i.layer):
                        self.poly_renderer.deferred(i, rs, RENDER_HINT_NORMAL)
                elif isinstance(i, Airwire):
                    self.hairline_renderer.deferred(i.p0, i.p1, AIRWIRE_COLOR, None, RENDER_HINT_NORMAL)
                else:
                    raise NotImplementedError()


        with Timer() as cmp_timer:
            for cmp in self.project.artwork.components:
                render_state = 0
                if cmp in self.selectionList:
                    render_state |= RENDER_SELECTED
                self.render_component(self.viewState.glMatrix, cmp, render_state)

        with Timer() as other_timer:
            super(BoardViewWidget, self).render()

        def ly_order_func(layer):
            # We always draw the current layer last
            if layer is self.viewState.current_layer:
                return 1

            return -layer.order

        with Timer() as t:
            # Draw all the layers
            layers = sorted(self.project.stackup.layers, key=ly_order_func)
            for layer in layers:
                self.trace_renderer.render_deferred_layer(self.viewState.glMatrix, layer)
                self.poly_renderer.render(self.viewState.glMatrix, layer)
                self.text_batch.render(key=layer)
                self.hairline_renderer.render_group(self.viewState.glMatrix, layer)

            self.via_renderer.render(self.viewState.glMatrix)

            # Render the non-layer text
            self.text_batch.render()
            self.hairline_renderer.render_group(self.viewState.glMatrix, None)

            self.hairline_renderer.render_group(self.viewState.glWMatrix, "OVERLAY_VS")
            #self.hairline_renderer.render_group(self.viewState.glMatrix, "OVERLAY_VS")

            GL.glFinish()

        all_time = time.time() - t_render_start
        print("Render time all: %f ot: %f cmp: %f aw: %f gl: %f" % (all_time, other_timer.interval, cmp_timer.interval, t_aw.interval, t.interval))