Adding or subtracting boundary from hatch after the hatch has been made

Hi there!

What’s the quickest way of removing a boundary from a hatch? My current workflow is like so:

Now for a simple case this is fine. But if adding or removing from some more complex shapes this becomes quite cumbersome. Especially if the original hatch is surrounding an area that is not hatched, in those cases AllRegions can’t be used.

Hello - In the current state of things, what you’re doing is it…You can try this plug-in for now and see if it helps - it is not as clean as just trimming but probably better than the ‘by hand’ work around.

TrimHatch.rhp (14.5 KB)

Unblock the plug-in (rhp file) in Windows Explorer (right click > Properties > Unblock (lower right) and Apply) then drag and drop it onto Rhino for TrimHatch.

Select the hatch, then the cutters then trim the hatch - it is lame but maybe helpful.
https://mcneel.myjetbrains.com/youtrack/issue/RH-48707
-Pascal

Much obliged, this works quite well, thanks!

My greedy self now wants a similar tool for adding to the boundary :smiley:

Hi there, I’ve been using TrimHatch.rhp. But I’ve noticed that it sometimes resets the HatchBase and mirrors the hatch:

Is there any small adjustment that can be made to the script to avoid this happening?

Hello - can you send me a file with that hatch in it?

-Pascal

Sure! But it happens with all the hatches, it’s only really noticeable with hatches with a “baseline”
20190904 HatchTrim - Pattern and File.3dm (148.1 KB)

Thanks, I should be able to fix that.
@rheinason - I updated the rhp above - should work better now…

BTW, in V7/WIP this all just works with Trim - no need for any stinkin’ plug-in.

-Pascal

1 Like

Hi Pascal

Sorry for being so slow in getting back to you, the new update does some weirdness as well, it seems it translates the hatch quite a bit aways:

These are all hatches originally created with the VisualARQ plugin, is there some hidden transformation information hidden that’s messing this up?

hello, any solution until now to simly trim any 2d hatches???

Hey guys!

This is my approach on the subject, let me know what you think. Constructive criticism is welcomed :slight_smile:

The script asks for a hatch and existing curves or blocks containing curves, then just like using CurveBoolean you can extend, trim or cut hole in the hatch by selecting the desired region.

Paste this into a button and experiment:

! _-RunPythonScript (
#HatchEdit
#This script updates an existing hatch to fill a new region defined by a selection of boundary curves or blocks containing curves
#Krausz Zsolt 20200418

import rhinoscriptsyntax as rs
import Rhino
import System.Guid
import sys
import scriptcontext as sc
import rhinoscript.utility as rhutil
 
string1 = "Select boundary curves or blocks"
string2 = "Select hatch to edit"


def DuplicateHatchBorder(hatch_id, outer=True):
    """Create curves that duplicate a hatch inner, outer or both borders
    Parameters:
      hatch_id (guid): identifier of a hatch
      outer (boolean, optional): the border curves to return
         True=exterior
         False=interior
    Returns:
      list(guid, ...): list of curve ids on success
      None: on error
    """
    hatch = rhutil.coercegeometry(hatch_id, True)
    curves = Rhino.Geometry.Hatch.Get3dCurves(hatch, outer)
    
    if curves is None: return sc.errorhandler()
    tolerance = sc.doc.ModelAbsoluteTolerance * 2.1
    curves = Rhino.Geometry.Curve.JoinCurves(curves, tolerance)
    if curves is None: return sc.errorhandler()
    
    rc = [sc.doc.Objects.AddCurve(c) for c in curves]
    sc.doc.Views.Redraw()
    return rc


class Hatch:
    def __init__(self, id):
        self.id = id
        self.StoreAttributes()
        rs.LockObject(self.id)

    def StoreAttributes(self):
        # store attributes of input hatch
        self.pattern = rs.HatchPattern(self.id)
        self.rotation = rs.HatchRotation(self.id)
        self.scale = rs.HatchScale(self.id)
        #store inner and outer boundaries of input hatch
        self.innerBorder = DuplicateHatchBorder(self.id, False)
        self.outerBorder = DuplicateHatchBorder(self.id, True)

    def GetNewBoundary(self, crvs):
        rs.UnselectAllObjects()
        rs.SelectObjects(crvs + self.outerBorder + self.innerBorder )
        if rs.Command("_-CurveBoolean", echo=True):
            self.newBorder = rs.LastCreatedObjects(select=False) #replace new border with the selected region
        else: self.newBorder = self.outerBorder + self.innerBorder # in case no region is selected maintain old border
        rs.UnselectAllObjects()

    def RefreshHatch(self):
        sn = sc.doc.BeginUndoRecord("HatchEdit")
        newHatch = rs.AddHatch(self.newBorder, self.pattern, self.scale, self.rotation) # create a hatch with new boundary, same pattern attributes
        rs.MatchObjectAttributes(newHatch, source_id=self.id) # match new hatch attributes with old one
        rs.UnlockObjects([self.id, newHatch]) 
        rs.DeleteObject(self.id) # delete original hatch
        if (sn > 0):
            sc.doc.EndUndoRecord(sn)

    def DeleteTemporaryGeometry(self):
        rs.DeleteObject(self.outerBorder)
        rs.DeleteObjects([crv for crv in self.newBorder])
        rs.DeleteObjects([crv for crv in self.innerBorder])

# ask user to select the hatch to edit
id = rs.GetObject(message=string2, filter=65536, preselect=True, select=True)

#test if selection is not empty
if id: 
    hatchObj = Hatch(id) 
    cutBoundaries = rs.GetObjects(message=string1, filter=4|4096, preselect=False, select=True)
    if cutBoundaries: 
        hatchObj.GetNewBoundary(cutBoundaries)
        hatchObj.RefreshHatch()
        hatchObj.DeleteTemporaryGeometry()
    else: 
        rs.UnlockObject(hatchObj.id)

del hatchObj
rs.Redraw()

)
1 Like

Very cool, and I like the elegant solution. I have a couple of comments.

  1. It doesn’t respect the hatch base when cutting a single hatch into two:
  2. It changes the hatch layer from the original layer to the current layer. This is pretty easy to adjust for though so this is not a major deal.

Thx! Fixed the issues you pointed out. Find the updated script below:

! _-RunPythonScript (
#HatchBoolean
#This script updates an existing hatch to fill a new region defined by a selection of boundary curves or blocks containing curves
#Krausz Zsolt 2020042

#Error log
#maintains original layer for all pieces
#hatch base implemented


import rhinoscriptsyntax as rs
import Rhino
import System.Guid
import sys
import scriptcontext as sc
import rhinoscript.utility as rhutil

string1 = "Select boundary curves or blocks"
string2 = "Select hatch to edit"

def HatchBasePoint(hatch_ids, base_point=None):
    #sets or returns a hatch basepoint
    # test if user specified one id only
    if type(hatch_ids) is System.Guid:
        hatch_ids = [hatch_ids]
    # initialise return values
    r_count = 0
    r_list = []
    # go through the list
    for hatch_id in hatch_ids:
        rh_hatch_obj = sc.doc.Objects.Find(hatch_id)
        if base_point == None:
            # if no point was specified, return existing basepoint of the hatch
            base_point = rh_hatch_obj.Geometry.BasePoint
            base_point = rs.coerce3dpoint(base_point)
            r_list.append(base_point)
        else:
            # if a point was specified, set it as the new basepoint of the hatch, and increase count
            rh_hatch_obj.Geometry.BasePoint = rhutil.coerce3dpoint(base_point)
            rh_hatch_obj.CommitChanges()
            r_count = +1
    #return the ids of the basepoints or the number of hatches modified
    if r_count == 0 and len(r_list)>1:
        return r_list
    elif r_count == 0 and len(r_list) == 1:
        return r_list[0]
    else:
        return r_count


def DuplicateHatchBorder(hatch_id, outer=True):
    """Create curves that duplicate a hatch inner, outer or both borders
    Parameters:
      hatch_id (guid): identifier of a hatch
      outer (boolean, optional): the border curves to return
         True=exterior
         False=interior
    Returns:
      list(guid, ...): list of curve ids on success
      None: on error
    """
    hatch = rhutil.coercegeometry(hatch_id, True)
    curves = Rhino.Geometry.Hatch.Get3dCurves(hatch, outer)

    if curves is None: return sc.errorhandler()
    tolerance = sc.doc.ModelAbsoluteTolerance * 2.1
    curves = Rhino.Geometry.Curve.JoinCurves(curves, tolerance)
    if curves is None: return sc.errorhandler()

    rc = [sc.doc.Objects.AddCurve(c) for c in curves]
    sc.doc.Views.Redraw()
    return rc


class Hatch:
    def __init__(self, id):
        self.id = id
        self.StoreAttributes()
        rs.LockObject(self.id)

    def StoreAttributes(self):
        # store hatch base point
        self.base_point = HatchBasePoint(self.id)
        # store attributes of input hatch
        self.pattern = rs.HatchPattern(self.id)
        self.rotation = rs.HatchRotation(self.id)
        self.scale = rs.HatchScale(self.id)
        #store inner and outer boundaries of input hatch
        self.innerBorder = DuplicateHatchBorder(self.id, False)
        self.outerBorder = DuplicateHatchBorder(self.id, True)

    def GetNewBoundary(self, crvs):
        rs.UnselectAllObjects()
        rs.SelectObjects(crvs + self.outerBorder + self.innerBorder )
        if rs.Command("_-CurveBoolean", echo=True):
            self.newBorder = rs.LastCreatedObjects(select=False) #replace new border with the selected region
        else: self.newBorder = self.outerBorder + self.innerBorder # in case no region is selected maintain old border
        rs.UnselectAllObjects()

    def RefreshHatch(self):
        sn = sc.doc.BeginUndoRecord("HatchEdit")
        # create a hatch with new boundary, same pattern, scale and rotation
        newHatch = rs.AddHatches(self.newBorder, self.pattern, self.scale, self.rotation)
        HatchBasePoint(newHatch, self.base_point)
        r = rs.MatchObjectAttributes(newHatch, source_id=self.id) # match new hatch attributes with old one
        rs.UnlockObjects([self.id]+newHatch)
        rs.DeleteObject(self.id) # delete original hatch
        if (sn > 0):
            sc.doc.EndUndoRecord(sn)

    def DeleteTemporaryGeometry(self):
        rs.DeleteObject(self.outerBorder)
        rs.DeleteObjects([crv for crv in self.newBorder])
        rs.DeleteObjects([crv for crv in self.innerBorder])

# ask user to select the hatch to edit
id = rs.GetObject(message=string2, filter=65536, preselect=True, select=True)

#test if selection is not empty
if id:
    hatchObj = Hatch(id)
    cutBoundaries = rs.GetObjects(message=string1, filter=4|4096, preselect=False, select=True)
    if cutBoundaries:
        hatchObj.GetNewBoundary(cutBoundaries)
        hatchObj.RefreshHatch()
        hatchObj.DeleteTemporaryGeometry()
    else:
        rs.UnlockObject(hatchObj.id)

del hatchObj
rs.Redraw()


)