3
$\begingroup$

I'm trying to write a chaining iterator for Freestyle to produce a single, fully-joined chain; mathematically, I believe this requires me to trace each line twice.

I have written a simple chaining iterator, inheriting from ChainingIterator, with a traverse function which keeps track of visited edges and allows each to be visited exactly twice. The problem is that when I use it with the built-in chain operator, I'm not seeing edges that have been visited at all. According to the docs, I should be able to ask for all edges, including the visited ones, by setting restrict_to_unvisited=False in my iterator; indeed, this works fine when I use the bidirectional_chain operator instead of chain but uh, this also has the effect of resulting in a bidirectional chain (with the result that I'll end up with some segments visited thrice and some not at all, which is not what I'm looking for aesthetically for this project).

I have the following code:

from freestyle.predicates import (
    QuantitativeInvisibilityUP1D,
    FalseUP1D,
    TrueUP1D
    )
from freestyle.shaders import (
    BezierCurveShader,
    pyDecreasingThicknessShader,
    IncreasingColorShader
    )
from freestyle.types import Operators

from freestyle.types import (
    AdjacencyIterator,
    ChainingIterator
)

class singleLineIterator(ChainingIterator):
    def __init__(self):
        ChainingIterator.__init__(self, restrict_to_selection=True, restrict_to_unvisited=False, begin=None, orientation=True)
        self.edgeDict = dict()

    def init(self):
        pass

    def traverse(self, iter):
        it = AdjacencyIterator(iter)
        bestScore = 100
        bestEdge = None
        # "ve" means "view edge" apparently!
        for ve in it:
            score = self.edgeDict.get((ve.id.first, ve.id.second), 0)
            if (score < bestScore):
                bestEdge = ve
                bestScore = score
        print(bestScore) # this should be printing numbers higher than 0!
        if bestEdge is None or bestScore >= 2:
            return None
        self.edgeDict[(bestEdge.id.first, bestEdge.id.second)] = bestScore+1
        return bestEdge


Operators.select(QuantitativeInvisibilityUP1D(0))

Operators.chain(singleLineIterator(), FalseUP1D())
# Operators.bidirectional_chain(singleLineIterator(), FalseUP1D())

shaders_list = [
    BezierCurveShader(3),
    pyDecreasingThicknessShader(1,40),
    IncreasingColorShader(0,0,0,1, 255,0,255,1),
    ]
Operators.create(TrueUP1D(), shaders_list)

(Setup: basic cube, rotated slightly so the occluded edge isn't lining up with the front edge, for readability; Cycles renderer; Freestyle enabled, set to Python Scripting Mode, and using the above code as the only style module. Here's a .blend file with all this.)

Running the above code, the printout from line 45 never prints a number higher than 0. If I comment out line 54 and comment in line 55 (using bidirectional_chain instead of chain), I correctly see printed numbers up to and including 2.

In the linked .blend, I also have an svg_export.py with the same iterator, since it's easier to see the path continuity in an svg path. I marked the beginnings and endings of lines with a circle and a triangle, respectively: Bidirectional_chain shows a single continuous path, but not all edges visited exactly twice. chain shows no edge visited more than once

So, how do I fix this? I think this means either:

  1. actually convincing chain to give me previously-visited edges, or
  2. hacking bidirectional_chain to be less bidirectional, which seems less elegant but maybe more doable.

P.S. Interestingly, restrict_to_selection is also not working as I expected when using chain: if restricted_to_unvisited=true, then both restrict_to_selection=true and restrict_to_selection=false have results that are restricted to the selection (i.e. not showing the occluded edge). restrict_to_selection=false only works as expected if restricted_to_unvisited is also false. This doesn't bother me as much for this project, but perhaps it's a clue as to what is going wrong?

P.P.S. Is there a way to use the built-in Freestyle SVG Exporter Add-on with my own Python Style Module here? It didn't seem to work (no svg export iff Freestyle was in Python Scripting Mode); it seemed easier to grab the code from github and integrate it than to actually debug it, but I'm idly curious.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.