Example #1
0
def binpack(nodes, max_bin_height, spacing=0):
    ''' Add nodes to the bins of given max bin height and spacing '''
    if nodes:
        debug("There are %d nodes to bin pack" % (len(nodes)))
        for node in nodes:
            if node == None:
                debug("WARNING: a None node in the spawned nodes???")
    else:
        debug("WARNING: there are no nodes to bin pack!!!")
        return []

    scale = 1.0 / get_dpi_factor()  # dpi adjustment scale
    items = [NodeItem(node.dimensions.x * scale, node.dimensions.y * scale, node.bl_idname, node) for node in nodes]
    items = sorted(items, key=lambda item: item.height, reverse=True)

    bins = []
    for item in items:
        # try to fit the next item into the first bin that is not yet full
        for n, bin in enumerate(bins):  # check all the bins created so far
            if bin.height + len(bin.items) * spacing + item.height <= max_bin_height:
                # bin not full ? => add item
                debug("ADDING node <%s> to bin #%d" % (item.name, n))
                bin.append(item)
                break  # proceed to the next item
        else:  # item didn't fit into any bin ? => add it to a new bin
            debug('ADDING node <%s> to new bin' % (item.name))
            bin = Bin()
            bin.append(item)
            bins.append(bin)

    return bins
Example #2
0
def get_text_drawing_location(node):
    ''' Get the text drawing location relative to the node location '''
    location = Vector((node.absolute_location))
    location = location + Vector(
        (node.width + 20, -20))  # shift right of the node
    location = location + Vector((
        10 * node.text_offset_x,  # apply user offset
        10 * node.text_offset_y))

    location = location * get_dpi_factor()  # dpi scale adjustment

    return list(location)
def get_preferences():
    from sverchok.settings import get_dpi_factor
    return get_dpi_factor()
Example #4
0
    def draw_statistics(self, names, values):
        """ Draw the statistics in the node editor

        The statistics data can be either single or vectorized.
        * single:
        [ [sum], [avg], ... [[b1, b2, .. bN]] ]
        * vectorized:
        [ [sum1... sumM], [avg1... avgM], ... [[b11... b1N]... [bM1... bMN]] ]

        Q: how can we tell statistics are simple or vectorized?
        A: if the values[0] is a list with length > 1 then it's vectorized
        """
        nvBGL.callback_disable(node_id(self))  # clear drawing

        if len(values) == 0:
            return

        if self.show_statistics:
            max_width = max(len(name)
                            for name in names)  # used for text alignment

            info = []

            # for now let's treat single and vectorized separately (optimize later)
            is_vectorized = len(values[0]) > 1

            if is_vectorized:
                for n, v in zip(names, values):
                    if n in ["Histogram", "HIS"]:
                        line_format = "{0:>{x}} : [{1}]"
                        histogram_lines = []
                        for a in v:  # for each histogram set
                            if self.mode == "FLOAT":
                                value_format = "{:.{p}f}"
                            else:
                                value_format = "{}"
                            histogram_values = ", ".join([
                                value_format.format(aa, p=self.precision)
                                for aa in a
                            ])
                            histogram_lines.append(
                                "[{}]".format(histogram_values))
                        line = line_format.format(n,
                                                  ", ".join(histogram_lines),
                                                  x=max_width)
                        info.append(line)

                    else:
                        line_format = "{0:>{x}} : [{1}]"
                        if n in ["Count", "CNT"]:
                            value_format = "{}"
                        else:
                            if self.mode == "FLOAT":
                                value_format = "{:.{p}f}"
                            else:
                                value_format = "{}"

                        value_line = ", ".join([
                            value_format.format(vv, p=self.precision)
                            for vv in v
                        ])
                        line = line_format.format(n, value_line, x=max_width)

                        info.append(line)
            else:  # single values
                for n, v in zip(names, values):
                    if n in ["Histogram", "HIS"]:
                        # print("drawing histogram")
                        line_format = "{0:>{x}} : [{1}]"
                        if self.normalize:
                            value_format = "{:.{p}f}"
                        else:
                            value_format = "{}"
                        histogram_values = ", ".join([
                            value_format.format(a, p=self.precision)
                            for a in v[0]
                        ])
                        line = line_format.format(n,
                                                  histogram_values,
                                                  x=max_width)
                        info.append(line)
                    else:
                        if n in ["Count", "CNT"]:
                            line_format = "{0:>{x}} : {1}"
                        else:
                            if self.mode == "FLOAT":
                                line_format = "{0:>{x}} : {1:.{p}f}"
                            else:
                                line_format = "{0:>{x}} : {1}"

                        line = line_format.format(n,
                                                  v[0],
                                                  x=max_width,
                                                  p=self.precision)

                        info.append(line)

            draw_data = {
                'tree_name': self.id_data.name[:],
                'node_name': self.name[:],
                'content': info,
                'location': get_text_drawing_location,
                'color': self.text_color[:],
                'scale': self.text_scale * get_dpi_factor(),
                'font_id': int(self.selected_font_id())
            }

            nvBGL.callback_enable(node_id(self), draw_data)
Example #5
0
    def arrange_nodes(self, context):
        ''' Arrange the nodes in current category (using bin-packing) '''
        try:
            nodes = get_spawned_nodes()

            debug("ARRANGING %d nodes constrained by %s" % (len(nodes), self.constrain_layout))

            scale = 1.0 / get_dpi_factor()  # dpi adjustment scale

            max_node_width = max([node.dimensions.x * scale for node in nodes])

            if self.constrain_layout == "HEIGHT":
                # prioritize the layout height to fit all nodes => variable width
                bins = binpack(nodes, self.grid_height, self.grid_y_space)

            elif self.constrain_layout == "WIDTH":
                # find the height that gives the desired width
                max_tries = 11
                num_steps = 0
                min_h = 0
                max_h = 2 * (sum([node.dimensions.y * scale for node in nodes]) + (len(nodes)-1)*self.grid_height)
                while num_steps < max_tries:
                    num_steps = num_steps + 1

                    bin_height = 0.5 * (min_h + max_h)  # middle of the height interval

                    # get the packed bins for the next bin height
                    bins = binpack(nodes, bin_height, self.grid_y_space)

                    # find the total width for current bin layout
                    totalWidth = sum([bin.width for bin in bins])
                    # add the spacing between bins
                    totalWidth = totalWidth + self.grid_x_space * (len(bins)-1)

                    debug("{0} : min_h = {1:.2f} : max_h = {2:.2f}".format(num_steps, min_h, max_h))
                    debug("For bin height = %d total width = %d (%d bins)" % (bin_height, totalWidth, len(bins)))

                    delta = abs((self.grid_width - totalWidth)/self.grid_width)

                    debug("{0} : target = {1:.2f} : current = {2:.2f} : delta % = {3:.2f}".format(
                        num_steps,  self.grid_width, totalWidth,  delta))

                    if delta < 0.1:  # converged ?
                        break

                    else:  # not found ? => binary search
                        if self.grid_width < totalWidth:  # W < w (make h bigger)
                            min_h = bin_height
                        else:  # W > w (make h smaller)
                            max_h = bin_height

                debug("*** FOUND solution in %d steps" % num_steps)
                debug("* {} bins of height {} : width {} : space {} ".format(len(bins),
                                                                             int(bin_height),
                                                                             int(totalWidth),
                                                                             (len(bins)-1)*self.grid_x_space
                                                                             ))

            else:  # self.constrain_layout == "ASPECT"
                # find the height and width closest to the grid aspect ratio
                target_aspect = self.grid_width / self.grid_height

                max_tries = 11
                num_steps = 0
                min_h = 0
                max_h = 2 * sum([node.dimensions.y * scale for node in nodes])
                while num_steps < max_tries:
                    num_steps = num_steps + 1

                    bin_height = 0.5 * (min_h + max_h)  # middle of the height interval

                    # get the packed bins for the next bin height
                    bins = binpack(nodes, bin_height, self.grid_y_space)

                    # find the max width for current layout
                    totalWidth = sum([bin.width for bin in bins])
                    # add the spacing between bins
                    totalWidth = totalWidth + self.grid_x_space * (len(bins)-1)

                    debug("{0} : min_h = {1:.2f} : max_h = {2:.2f}".format(num_steps, min_h, max_h))
                    debug("For bin height = %d total width = %d" % (bin_height, totalWidth))

                    current_aspect = totalWidth / bin_height

                    delta_aspect = abs(current_aspect - target_aspect)

                    debug("{0} : target = {1:.2f} : current = {2:.2f} : delta = {3:.2f}".format(
                        num_steps, target_aspect, current_aspect, delta_aspect))

                    if delta_aspect < 0.1:  # converged ?
                        break

                    else:  # not found ? => binary search
                        if target_aspect < current_aspect:  # W/H < w/h (make h bigger)
                            min_h = bin_height
                        else:  # W/H > w/h (make h smaller)
                            max_h = bin_height

                debug("*** FOUND solution in %d steps" % num_steps)
                debug("* {} bins of height {} : width {} : space {} ".format(len(bins),
                                                                             int(bin_height),
                                                                             int(totalWidth),
                                                                             (len(bins)-1)*self.grid_x_space
                                                                             ))

            max_idname = max([len(node.bl_idname) for node in nodes])

            # ARRANGE the nodes in the bins on the grid
            x = 0
            for bin in bins:
                y = 0
                for item in bin.items:
                    node_width = item.width
                    node_height = item.height
                    node_name = item.name
                    node = item.node
                    if self.node_alignment == "LEFT":
                        node.location[0] = x
                    elif self.node_alignment == "RIGHT":
                        node.location[0] = x + (bin.width - node_width)
                    else:  # CENTER
                        node.location[0] = x + 0.5 * (bin.width - node_width)
                    node.location[1] = y

                    debug("node = {0:>{x}} : W, H ({1:.1f}, {2:.1f})  &  X, Y ({3:.1f}, {4:.1f})".format(
                        node_name,
                        node.dimensions.x * scale, node.dimensions.y * scale,
                        node.location.x, node.location.y,
                        x=max_idname))

                    y = y - (item.height + self.grid_y_space)
                x = x + (bin.width + self.grid_x_space)

        except Exception as e:
            print('EXCEPTION: arranging nodes failed:', str(e))