Giter Site home page Giter Site logo

occult's Introduction

occult

vpype plug-in to remove lines occulted by polygons from SVG files.

Examples

Basic usage

Draw a line, then a square:

vpype line 0 0 5cm 5cm rect 2cm 2cm 1cm 1cm show

Same drawing, after applying occult:

vpype line 0 0 5cm 5cm rect 2cm 2cm 1cm 1cm occult show

Order of path is important: occult will consider the last geometry in a SVG file to be "on top" of all other geometries, the last but one is above every other geometries except the last one. For instance, using vpype rect 2cm 2cm 1cm 1cm occult show will not modify geometries.

Working with multiple layers

By default, occult performs occlusion layer by layer. For instance, applying occlusion on the image below will not change anything:

occult -i ignores layers, so that occlusion is performed on all objects, regardless of their layer. Geometries in layers with a larger ID number are considered to be "on top" of geometries in layers with a smaller ID number.

  • Without -i flag

  • With -i flag

occult -a only performs occlusions across layers, ignoring occlusions that occur within a layer. As in occult -i, geometries in layers with a larger ID number are considered to be "on top" of geometries in layers with a smaller ID number. This option overrides -i.

  • Without -a or -i flags

  • With -i flag

  • With -a flag

Save occulted lines

occult -k keeps occulted lines in a separate layers.

  • Without -k flag

  • With -k flag

Using vpype's viewer (show command), you can visualize occulted lines and remaining lines separately.

Using occult with Vsketch

occult can be invoked from a Vksetch sketch, using vsk.vpype("occult"). When using the GUI, calling occult within the sketch draw() method will display occulted geometries at each code save / seed change. For sketches with lots of geometries, occlusion can take a significant amount of time. Invoke occult within the finalize() method of a sketch to perform occlusion only when saving a specific output.

import vsketch

class Sketch(vsketch.SketchClass):
    def draw(self, vsk: vsketch.Vsketch):
        vsk.size('10x10cm')
        vsk.scale('mm')
        
        vsk.line(-5, -5, 5, 5)
        vsk.circle(0, 0, 3)

        # Uncomment to perform occlusion at every GUI reload
        # vsk.vpype("occult")
  
    def finalize(self, vsk: vsketch.Vsketch) -> None:
        # Occlusion (and other vpype commands) invoked only when saving
        vsk.vpype("linesimplify occult linemerge linesort")


if __name__ == "__main__":
    Sketch.display()

Installation

See the installation instructions for information on how to install vpype.

Existing vpype installation

If vpype was installed using pipx, use the following command:

$ pipx inject vpype vpype-occult

If vpype was installed using pip in a virtual environment, activate the virtual environment and use the following command:

$ pip install vpype-occult

Check that your install is successful:

$ vpype --help
Usage: vpype [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  -v, --verbose
  -I, --include PATH  Load commands from a command file.
  --help              Show this message and exit.

Commands:
[...]
  Plugins:
    occult
[...]

Stand-alone installation

Use this method if you need to edit this project. First, clone the project:

$ git clone https://github.com/LoicGoulefert/occult.git
$ cd occult

Create a virtual environment:

$ python3 -m venv venv
$ source venv/bin/activate
$ pip install --upgrade pip

Install occult and its dependencies (including vpype):

$ pip install -e .

Check that your install is successful:

$ vpype --help
Usage: vpype [OPTIONS] COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]...

Options:
  -v, --verbose
  -I, --include PATH  Load commands from a command file.
  --help              Show this message and exit.

Commands:
[...]
  Plugins:
    occult
[...]

Documentation

The complete plug-in documentation is available directly in the CLI help:

$ vpype occult --help

License

See the LICENSE file for details.

occult's People

Contributors

abey79 avatar dwymark avatar loicgoulefert avatar st0rmingbr4in avatar very-meanly avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

occult's Issues

Does not run on Windows 10 with Python 3.9

Hi,

trying to execute occult on Windows 10 but it does not work for me. I installed vpype using pip3 (Python3.9) pip install vpype and then pip install git+https://github.com/abey79/occult.git#egg=occult

installation works without errros. vpype executes without any hassle. But as soon as applying "occult" command to vpype it fails:

C:\Users\tomate\Downloads>vpype read drawing.svg occult

Warning: entry point could not be loaded. Contact its author for help.


Traceback (most recent call last):
  File "c:\users\tomate\appdata\local\programs\python\python39\lib\site-packages\click_plugins\core.py", line 37, in decorator
    group.add_command(entry_point.load())
  File "c:\users\tomate\appdata\local\programs\python\python39\lib\site-packages\pkg_resources\__init__.py", line 2450, in load
    return self.resolve()
  File "c:\users\tomate\appdata\local\programs\python\python39\lib\site-packages\pkg_resources\__init__.py", line 2456, in resolve
    module = __import__(self.module_name, fromlist=['__name__'], level=0)
  File "c:\users\tomate\appdata\local\programs\python\python39\lib\site-packages\occult\occult.py", line 7, in <module>
    import pygeos
  File "c:\users\tomate\appdata\local\programs\python\python39\lib\site-packages\pygeos\__init__.py", line 1, in <module>
    from .lib import GEOSException  # NOQA
ImportError: DLL load failed while importing lib: Die angegebene Prozedur wurde nicht gefunden.

could it be missing GEOS bindings? i checked https://www.gisinternals.com/query.html?content=filelist&file=release-1900-gdal-3-0-2-mapserver-7-4-2.zip and i cannot find sth for Python 3.9. At least some help about external requirements or python version would be helpful.

thanks for your work and time :-)

API usage

Hi, thanks for providing this super useful plugin.
If you like, you may add a section to your README.md on how to use your plugin when using vsketch/ vpype as a regular python package in scripts. Actually rather trivial, but maybe it will help someone.

MWE:

import vsketch


class Sketch(vsketch.Vsketch):
       
    def draw(self):
        
        self.stroke(1)
        self.penWidth(8)
        
        self.size('10cm', '10cm')
        self.scale('1cm')
        
        self.line(0,0,5,5)
        self.rect(2,2,1,1)
        
        # other more complicated geometry objects..
      
  
    def occult(self) -> None:
        self.vpype("occult") 
  
    def finalize(self) -> None:
        self.vpype("linemerge linesimplify reloop linesort")
        

        

if __name__ =='__main__':
    v = Sketch()
    v.draw()
    v.occult()
    v.display(mode='matplotlib',
                   paper=False)
    
    
    v.save('test.svg')

Occult does not apply for some graphics

Hi,
sometimes occult does nothing on a SVG file. I attached a cardbox (https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/TuckboxExtension/) which has multiple lines over each other but they still exist after running occult. Do you have an idea what could be the reason?

i already checked if all points are snapped to each other. I played with different tolerance values from tiny to large. no difference.

before running occult i have 28 elements
grafik

after running occult i have 30 elements
grafik

i attached the sample file here:
drawing.zip

regards, Mario

ValueError: zero-size array to reduction operation minimum which has no identity

Using occult sometimes produces the following error:

Traceback (most recent call last):
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vsketch_cli/threads.py", line 25, in run
    sketch = self._sketch_class.execute(seed=self._seed, finalize=False)
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vsketch/sketch_class.py", line 78, in execute
    bounds = vsk.document.bounds()
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vpype/model.py", line 680, in bounds
    [
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vpype/model.py", line 681, in <listcomp>
    self._layers[vid].bounds()
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vpype/model.py", line 359, in bounds
    raise e
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/vpype/model.py", line 353, in bounds
    line.real.min()
  File "~/.pyenv/versions/3.9.5/envs/creative-coding/lib/python3.9/site-packages/numpy/core/_methods.py", line 43, in _amin
    return umr_minimum(a, axis, None, out, keepdims, initial, where)
ValueError: zero-size array to reduction operation minimum which has no identity

This happens when a LineString doesn't have any items, so the resulting Numpy array has zero items. I have attached a sketch that demonstrates the issue - if it doesn't reproduce on your machine, try randomizing the seed - you should eventually trigger the error. Requires vype (obviously) and vsketch to run - can't provide a requirements.txt since it's installed from a git clone (sorry) but I am using vpype 1.7.0.

cvety.zip

Handle GEOSException: IllegalArgumentException: Invalid number of points in LinearRing found 3 - must be 0 or >= 4

Sometimes, linesimplify introduces ill formed LinearRings. Find a way to either:

  • convert these ill-formed linear rings into valid ones
  • ignore them
  • delete them
GEOSException                             Traceback (most recent call last)
<ipython-input-80-8f14df048e4b> in <module>
     49         vsk.geometry(rh.intersection(bundle_lines))
     50 
---> 51 vsk.vpype("linesimplify occult crop 1cm 1cm 26.2cm 19cm reloop linemerge multipass linesort")
     52 vsk.display(mode="matplotlib")

~/Documents/python/vsketch/vsketch/vsketch.py in vpype(self, pipeline)
   1229 
   1230         args = "vsketchinput " + pipeline + " vsketchoutput"
-> 1231         vpype_cli.cli.main(prog_name="vpype", args=shlex.split(args), standalone_mode=False)
   1232 
   1233     def display(

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/vpype_cli/cli.py in main(self, args, **extra)
     72         if args is None:
     73             args = get_os_args()
---> 74         return super().main(args=preprocess_argument_list(args), **extra)
     75 
     76 

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/click/core.py in main(self, args, prog_name, complete_var, standalone_mode, **extra)
    780             try:
    781                 with self.make_context(prog_name, args, **extra) as ctx:
--> 782                     rv = self.invoke(ctx)
    783                     if not standalone_mode:
    784                         return rv

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/click/core.py in invoke(self, ctx)
   1288                 with sub_ctx:
   1289                     rv.append(sub_ctx.command.invoke(sub_ctx))
-> 1290             return _process_result(rv)
   1291 
   1292     def resolve_command(self, ctx, args):

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/click/core.py in _process_result(value)
   1222         def _process_result(value):
   1223             if self.result_callback is not None:
-> 1224                 value = ctx.invoke(self.result_callback, value, **ctx.params)
   1225             return value
   1226 

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/click/core.py in invoke(*args, **kwargs)
    608         with augment_usage_errors(self):
    609             with ctx:
--> 610                 return callback(*args, **kwargs)
    611 
    612     def forward(*args, **kwargs):  # noqa: B902

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/vpype_cli/cli.py in process_pipeline(processors, verbose, include, history, seed, config)
    126 @cli.resultcallback()
    127 def process_pipeline(processors, verbose, include, history, seed, config):
--> 128     execute_processors(processors)
    129 
    130 

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/vpype_cli/cli.py in execute_processors(processors)
    210     state = vp.VpypeState()
    211     for proc in outer_processors:
--> 212         state = proc(state)
    213     return state
    214 

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/vpype/decorators.py in layer_processor(state)
     83                 start = datetime.datetime.now()
     84                 with state.current():
---> 85                     state.document[lid] = f(state.document[lid], *args, **kwargs)
     86                 stop = datetime.datetime.now()
     87 

~/Documents/python/occult/occult/occult.py in occult(lines, tolerance)
     44         if math.hypot(coords[-1, 0] - coords[0, 0], coords[-1, 1] - coords[0, 1]) < tolerance:
     45             tree = pygeos.STRtree(line_arr[:i])
---> 46             p = pygeos.polygons(coords)
     47             geom_idx = tree.query(p, predicate="intersects")
     48             line_arr[geom_idx] = pygeos.set_operations.difference(line_arr[geom_idx], p)

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/pygeos/creation.py in polygons(shells, holes)
     85     shells = np.asarray(shells)
     86     if not isinstance(shells, Geometry) and np.issubdtype(shells.dtype, np.number):
---> 87         shells = linearrings(shells)
     88 
     89     if holes is None:

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/pygeos/creation.py in linearrings(coords, y, z)
     69     z : array_like
     70     """
---> 71     return _wrap_construct_ufunc(lib.linearrings, coords, y, z)
     72 
     73 

~/Documents/python/vsketch/venv/lib/python3.6/site-packages/pygeos/creation.py in _wrap_construct_ufunc(func, coords, y, z)
     18 def _wrap_construct_ufunc(func, coords, y=None, z=None):
     19     if y is None:
---> 20         return func(coords)
     21     x = coords
     22     if z is None:

GEOSException: IllegalArgumentException: Invalid number of points in LinearRing found 3 - must be 0 or >= 4

Line Removal Failing When Using -k flag

I'm using Vpype version 1.14.0 and Occult version 0.4.0

Using the Demo.svg file attached, I'm looking to use occult with the -k flag to keep the lines within the mountain shape.

When simply performing Occult across layers, it removes the lines inside the mountain shape; using this command:

vpype read -a id --no-crop 'Demo.svg' occult -i write 'Demo_occult.svg'

But when the -k flag is added, it fails to remove any lines at all, using this command:

vpype read -a id --no-crop 'Demo.svg' occult -i -k write 'Demo_occult.svg'

Demo

trim on page edges

Hello, I came to your library from the mention in evil-mad/axidraw#100
Is there a combination of arguments that would simply trim the excess paths that go over a page?
(Context: creating the SVG from Processing will generate SVG files with paths that go beyond the page border)

Idea: Option to use geometry of overlapping shapes to make closed polygons on overlapped shapes

It may be useful for people using commands like fill from here (which may become a feature in vpype depending on whenever that happens) or efill from the embroidery plugin, to be able to make overlapping shapes be cropped properly, but have the resulting design use closed polygon loops, which these functions need to define their bounds. Not a massive necessity and i've not poked about your source code to see how feasible this would be, but I think it could be a great addition to this plugin. Threw a rough vis image together to show what this may look like. (please ignore that i was too lazy to reauthor the svg to make it have a filled shape, so the filled lines is just copy paste job :') )
idea_pic

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.