Difference between revisions of "Exploring Livarot"

From Inkscape Wiki
Jump to navigation Jump to search
 
(8 intermediate revisions by 2 users not shown)
Line 1: Line 1:
===Why is it being used?===
===Why is it being used?===
# Tweak Tool
# Tweak Tool (Shape->MakeTweak())
# Flood Tool (Shape->MakeOffset())
# Path Offsetting
# Path Offsetting
#* Inset/Outset (Livarot)
#* Dynamic Offset (Livarot)
#* Linked Offset (Livarot)
#* SPOffset (dynamic/linked offset) (Livarot)
#* LPEOffset (Livarot to flatten, half_outline())
# Boolean Operations
# Boolean Operations
# Flowing Text
# Flowing Text (Shape->Scan(), Shape->Transform())
# Path Simplification
# Path Simplification (Path->Coalesce() or Path->ConvertEvenLines() + path->Simplify())
# Path Flattening (lpe-offset.pp, elsewhere?)


===Detailed List===
 
# Tweak Tool
Much of the code that uses Livarot to offset is duplicate three times:
#* <code>sp_tweak_dilate_recursive()</code> in ''src/ui/tools/tweak-tool.cpp'' uses <code>shape->MakeTweak</code> for tweak modes such as <code>TWEAK_MODE_SHRINK_GROW</code>, <code>TWEAK_MODE_ATTRACT_REPEL</code>, <code>TWEAK_MODE_PUSH</code> and <code>TWEAK_MODE_ROUGHEN</code>.
* sp_selected_path_do_offset()
# Path Offsetting
* sp_selected_path_create_offset_object() (This might not actually be needed as presumably the code in SPOffset::set_shape is what is displayed.)
#*
* SPOffset::set_shape()
The common code should be made into a single function.
 
===How's it typically used?===
# Creating a livarot <code>Path</code> object by using Inkscape’s functions such as <code>Path_for_item</code> or <code>Path_for_pathvector</code>. These functions just store path descriptions inside the <code>Path</code> object. Such as <code>moveTo</code>,<code>lineTo</code>, <code>cubicTo</code>, etc. These are just instructions.
# Calling <code>path-&gt;Convert</code> or <code>path-&gt;ConvertWithBackData</code>. Given a threshold value, these functions create a polygon that approximates the path descriptions given in step 1. The smaller the threshold value, the better the approximation. This approximation is the fundamental tool of livarot that gives it the ability to do things like path boolean operations.
# Calling <code>path-&gt;Fill(shape, ...)</code> . This creates a directed graph structure from the approximated polygon. A directed graph just has points connected by edges.
# Calling <code>shape-&gt;ConvertToShape(shape_input, ...)</code>. This function converts the graph created earlier, to a polygon without any self-intersections or duplicate points. It is one of the most important functions of livarot. It also takes a fill rule as the argument.
# Here whatever actual function you wanna perform is called. If you’re tweaking, you would call <code>shape-&gt;MakeTweak</code>, if you’re performing outset/inset, you’d call <code>shape-&gt;MakeOffset</code>. Similar functions exist for boolean operations.
# Calling <code>shape-&gt;ConvertToForme(path)</code>. This converts the shape object back to a path whose SVG description you can grab and put in the XML tree.
 
===Use in Dynamic Offset===
 
The Dynamic and Linked offset code produces better results than the Inset and Outset code. They also have the advantage of always using the reference path as the starting point. Repeatedly applying Inset and Offset will eventually fail (typically after 9 or 10 times) due to fitting errors.
 
This is the process that is used by the Dynamic and Linked offset code:
 
====Part 1====
 
This process breaks a path that crosses itself into subpaths that do not cross and that flow in a counter-clockwise direction(?).
 
Creation of dynamic offset (sp_selected_path_create_offset_object()):
# Create 'Path' (orig) using Path_for_item(). This uses Path->LoadPathVector using SPShape::_curve set by SPShape::setCurveInsync() from, for example SPRect::set_shape(), via a way too long chain of function calls.
# Convert 'Path' using Path->convertWithBackData(1.0).
# Fill 'Shape' (theShape) using Path->Fill(theShape, 0). Where 0 is path id (index).
# Create a new 'Shape' (theRes) using Shape->ConvertToShape(theShape, fill_rule).
# Fill a new 'Path' (res) using Shape->ConvertToForme(res, 1, &orig). 1 is the number of paths in orig[].
 
====Part 2====
 
This part takes a path, offsets it and then removes any intersections.
 
Changing the "radius" of a dynamic offset (SPOffset::set_shape()) using the 'fast' branch:
# Make copy (orig) of the stored 'Path' (originalPath).
# Offset the path by "radius" using orig->OutsideOutline().
# Convert 'Path' using Path->ConvertWithBackData(x) where x is 1.0 or radius if less than 1.0.
# Fill 'Shape (theShape) using theShape->Fill (theShape, 0). Where 0 is path id (index).
# Fill 'Shape (theRes) using theRes->ConvertToShape (theShape, fill_positive).
# Fill 'Path' (orig) using theRes->ConvertToForme (orig, 1, &res). 1 is number of paths in res[].
# Coalesce (simplify) path using orig->Coalesce(size * 0.001) where size is related to the bounding box.
# Set _curve to 'Path' output via orig->svg_dump_path().
 
===Source===
 
Livarot is available at SourceForge. There is a newer, C++ based version.[https://sourceforge.net/projects/livarot/]

Latest revision as of 11:42, 29 March 2020

Why is it being used?

  1. Tweak Tool (Shape->MakeTweak())
  2. Flood Tool (Shape->MakeOffset())
  3. Path Offsetting
    • Inset/Outset (Livarot)
    • Dynamic Offset (Livarot)
    • Linked Offset (Livarot)
    • SPOffset (dynamic/linked offset) (Livarot)
    • LPEOffset (Livarot to flatten, half_outline())
  4. Boolean Operations
  5. Flowing Text (Shape->Scan(), Shape->Transform())
  6. Path Simplification (Path->Coalesce() or Path->ConvertEvenLines() + path->Simplify())
  7. Path Flattening (lpe-offset.pp, elsewhere?)


Much of the code that uses Livarot to offset is duplicate three times:

  • sp_selected_path_do_offset()
  • sp_selected_path_create_offset_object() (This might not actually be needed as presumably the code in SPOffset::set_shape is what is displayed.)
  • SPOffset::set_shape()

The common code should be made into a single function.

How's it typically used?

  1. Creating a livarot Path object by using Inkscape’s functions such as Path_for_item or Path_for_pathvector. These functions just store path descriptions inside the Path object. Such as moveTo,lineTo, cubicTo, etc. These are just instructions.
  2. Calling path->Convert or path->ConvertWithBackData. Given a threshold value, these functions create a polygon that approximates the path descriptions given in step 1. The smaller the threshold value, the better the approximation. This approximation is the fundamental tool of livarot that gives it the ability to do things like path boolean operations.
  3. Calling path->Fill(shape, ...) . This creates a directed graph structure from the approximated polygon. A directed graph just has points connected by edges.
  4. Calling shape->ConvertToShape(shape_input, ...). This function converts the graph created earlier, to a polygon without any self-intersections or duplicate points. It is one of the most important functions of livarot. It also takes a fill rule as the argument.
  5. Here whatever actual function you wanna perform is called. If you’re tweaking, you would call shape->MakeTweak, if you’re performing outset/inset, you’d call shape->MakeOffset. Similar functions exist for boolean operations.
  6. Calling shape->ConvertToForme(path). This converts the shape object back to a path whose SVG description you can grab and put in the XML tree.

Use in Dynamic Offset

The Dynamic and Linked offset code produces better results than the Inset and Outset code. They also have the advantage of always using the reference path as the starting point. Repeatedly applying Inset and Offset will eventually fail (typically after 9 or 10 times) due to fitting errors.

This is the process that is used by the Dynamic and Linked offset code:

Part 1

This process breaks a path that crosses itself into subpaths that do not cross and that flow in a counter-clockwise direction(?).

Creation of dynamic offset (sp_selected_path_create_offset_object()):

  1. Create 'Path' (orig) using Path_for_item(). This uses Path->LoadPathVector using SPShape::_curve set by SPShape::setCurveInsync() from, for example SPRect::set_shape(), via a way too long chain of function calls.
  2. Convert 'Path' using Path->convertWithBackData(1.0).
  3. Fill 'Shape' (theShape) using Path->Fill(theShape, 0). Where 0 is path id (index).
  4. Create a new 'Shape' (theRes) using Shape->ConvertToShape(theShape, fill_rule).
  5. Fill a new 'Path' (res) using Shape->ConvertToForme(res, 1, &orig). 1 is the number of paths in orig[].

Part 2

This part takes a path, offsets it and then removes any intersections.

Changing the "radius" of a dynamic offset (SPOffset::set_shape()) using the 'fast' branch:

  1. Make copy (orig) of the stored 'Path' (originalPath).
  2. Offset the path by "radius" using orig->OutsideOutline().
  3. Convert 'Path' using Path->ConvertWithBackData(x) where x is 1.0 or radius if less than 1.0.
  4. Fill 'Shape (theShape) using theShape->Fill (theShape, 0). Where 0 is path id (index).
  5. Fill 'Shape (theRes) using theRes->ConvertToShape (theShape, fill_positive).
  6. Fill 'Path' (orig) using theRes->ConvertToForme (orig, 1, &res). 1 is number of paths in res[].
  7. Coalesce (simplify) path using orig->Coalesce(size * 0.001) where size is related to the bounding box.
  8. Set _curve to 'Path' output via orig->svg_dump_path().

Source

Livarot is available at SourceForge. There is a newer, C++ based version.[1]