Ruby API split edge issue

  1. how to split an edge by multiple points at once?
    as I split A edge into B edge and C edge. A → B + C
    will edge A be deleted?
    what should I do to continue to split edge A?

  2. how to split a curve?
    I get out of the an edge which makes up the curve, and applies a .split(p3d) on it, however, only the edge (straight line) is split, the curve didn’t change.

I will be so glad if you can help. ^v^

fortunately, I solved it. I just leave the code here, in case someone needs it.

def sort_index(arg, arr)
  indices = 0
  arr.each do |ar|
    if arg < ar
      indices -= 1
    else
      indices += 1
    end
  end
  return indices
end

def sort_sequence(arr)
  indices = Hash.new
  arr.each do |arg|
    indices[sort_index(arg, arr)] = arg
  end
  _index = Array.new
  indices.sort.each do |ar|
    _index << ar[1]
  end
  return _index
end

def advanced_split(ed, p3ds)
  it = sort_sequence(p3ds).reverse
  it.each do |p3d|
    ed.split(p3d)
  end
end

What does this code have to do with splitting edges or curves and the associated questions posed in the initial post ?

EDIT: Never mind the split iterator method was hidden by the scroll.

edge = Sketchup.active_model.entities.add_line([0,0,0],[100,100,0])
puts "edge id: #{edge.object_id}"
new_edge = edge.split(0.5)
puts "edge id: #{edge.object_id}"
puts "new_edge id: #{new_edge.object_id}"

… proves that no, edge A is not deleted, so you are actually splitting edge A into edge A + edge B.

Simply continue calling #split upon edge A.

edge = Sketchup.active_model.entities.add_line([0,0,0],[100,0,0])
#<Sketchup::Edge:0x000001a8b40fbdc0>
edge.split([10,0,0],[20,0,0],[30,0,0])
#=> Error: #<ArgumentError: wrong number of arguments (given 3, expected 1)>

… proves that the method only takes 1 argument.

And so you needed to write your own iterator method (as you’ve shown above) that accepts multiple point arguments.

Here is my concept for a single method that would take multiple point arguments.
It wraps the splitting in an undo operation. Fully commented.
(Tested with a simple 100 inch edge lying along the x axis.)

  # Split an edge given multiple points. Returns an array of the new edges
  # along with the original now shorter edge (in no particular order.)
  # @param edge [Sketchup::Edge] the edge to split.
  # @param points [Array<Geom::Point3d,Array<Float,Float,Float>]
  # @return [Array<Sketchup::Edge>] an array of the edges with the original.
  def split_multiple(edge,*points)
    # Convert any 3 element Float arrays to a Geom::Point3d:
    points.map! do |pt|
      next pt if pt.is_a?(Geom::Point3d)
      next Geom::Point3d.new(pt[0..2]) if pt.is_a?(Array)
      nil
    end
    points.compact! # Remove any nils
    # Remove any points not on the edge's line:
    eline = edge.line
    points.delete_if {|pt|
      next true if !pt.on_line?(eline)
      Geom::point_in_polygon_2D(
        pt, [edge.start.position, edge.end.position], true
      ) ? false : true
    }
    # Sort the points using the API's < method which is based upon ORIGIN
    # Use a transformation to the ORIGIN within the sort block:
    t = edge.start.position.vector_to(ORIGIN)
    points.sort! do |a,b|
      a = a.transform(t)
      b = b.transform(t)
      a == b ? 0 : a < b ? -1 : 1
    end
    unless points.empty?
      # Take an array snapshot of the existing entities edges:
      before = edge.parent.entities.grep(Sketchup::Edge)
      # Split the edge using the sorted points in reverse order:
      Sketchup.active_model.start_operation("MultiSplit Edge",true)
        #
        points.reverse.each do |point|
          edge.split(point)
        end
        #
      Sketchup.active_model.commit_operation
      # Remove the target edge's reference from the before array:
      before.delete(edge)
      # Return an array of the edge and it's split off edges: 
      edge.parent.entities.grep(Sketchup::Edge) - before
    else
      # Just return an array with the original edge:
      [edge]
    end
  end

And here is a slightly modified edition that returns the edge array in edge joined order (the now shortened original edge first.)

  # Split an edge given multiple points. Returns an array of the new edges
  # along with the original now shorter edge first (in edge joined order.)
  # @param edge [Sketchup::Edge] the edge to split.
  # @param points [Array<Geom::Point3d,Array<Float,Float,Float>]
  # @return [Array<Sketchup::Edge>] an array of the edges with the original.
  def split_multiple_ordered(edge,*points)
    # Convert any 3 element Float arrays to a Geom::Point3d:
    points.map! do |pt|
      next pt if pt.is_a?(Geom::Point3d)
      next Geom::Point3d.new(pt[0..2]) if pt.is_a?(Array)
      nil
    end
    points.compact! # Remove any nils
    # Remove any points not on the edge's line:
    eline = edge.line
    points.delete_if {|pt|
      next true if !pt.on_line?(eline)
      Geom::point_in_polygon_2D(
        pt, [edge.start.position, edge.end.position], true
      ) ? false : true
    }
    # Sort the points using the API's < method which is based upon ORIGIN
    # Use a transformation to the ORIGIN within the sort block:
    t = edge.start.position.vector_to(ORIGIN)
    points.sort! do |a,b|
      a = a.transform(t)
      b = b.transform(t)
      a == b ? 0 : a < b ? -1 : 1
    end
    unless points.empty?
      # The edge array to return:
      edges = []
      # Split the edge using the sorted points in reverse order:
      Sketchup.active_model.start_operation("MultiSplit Edge",true)
        #
        points.reverse.each do |point|
          edges << edge.split(point)
        end
        #
      Sketchup.active_model.commit_operation
      # Return an array of the edge and it's split off edges: 
      edges << edge
      # Reverse and remove any nils returned by edge.split
      edges.reverse.compact
    else
      # Just return an array with the original edge:
      [edge]
    end
  end

Retested and edited several times to deal with issues, like when the args result in no valid points, the undo operation needs to be skipped, and when the split method returns nil these need to be removed from the result array.

1 Like

Thank you so much for providing this dedicate answer. ^v^
What I am coding, is an extension that connects all stray edges || curves || connected-edges by only one click. the aim is to facilitate users during importing an imperfect pattern with many tiny edges missing.
Therefore, I have developed an extension that add edges to the entities, but intersection appears to be a problem. The edges generated by the extension: if it’s crossed by another edge, it won’t automatically split.

You may wonder why I don’t simply apply
Sketchup.break_edges = true
as adding edges, I can’t do this because there are some special circumstances in user’s expectation. In other word, I want the user to be able to decide what to do with outcome edges, and delete or split the ones they don’t want. To make the code more flexible. ^v^

Now, what I have already gained is a hash, which the keys are single edge each, that has been proved to be crossed. Meanwhile the values are an array each, consists of one or more p3d arguments which represents intersections.

To split each of the edges, I thought I could do “split(*p3ds)” to split one edge at multiple points, however, it failed as split only takes one arguments. v_v

I work out there are two ways to split the same edge:
ed = #<Sketchup::Edge:0x0000021d25b1edd0>
if p3d1<p3d2

  1. ed.split(p3d1).split(p3d2)
  2. ed.split(p3d2) ed.split(p3d1)

Method 2 is perfect in my circumstance, what I needed is a sort function that helps me to determine which p3d is closer to the origin.
in the my code above, as I already have
hash = {edge=>[*p3ds], edge2=>[*p3ds]}
simply call
advance_split(ed, p3ds)
it resolves all the problem. ^v^

I can remember some of my previous questions are answered by you.
I appreciate your passion and contribution.
cheers

2 Likes