[WIP; by request] Road cutter

Continuing the discussion from Dezmo's ex*-tension*:

for @mihai.s

Just indication yet, there are still more to do before I can share the alpha code… deadline can not be offered. :blush:
road_cutter

Some conditions:
The path should be a curve, in model editing context.
The “road” to where the path will projected to and cut, should be a group containing the edges and faces. Should be that wide to hold the all projection.
“Flat” road, parallel to Z plane. (Perhaps might work with surface)

A cut line will contains more edges - than need to connect the “road cross” edges - according to the curve.

3 Likes

Hello Dezmo,

An idea/request that exceeded “something simple” and you found a solution and created the plugin very quickly. Through this plugin, you offer the possibility of replacing the current method that involves the use of three or four plugins/extensions. Very useful for urban planners and landscapers.

Thank you for your interest and effort!

1 Like

second attempt.
Still far away to be called as extension… no error handling, just a bunch of lines after other…, some restriction:

  • The path should be a curve, in model editing context. and above the road.
  • The “road” to where the path will projected to and cut, should be a group containing the edges and faces . (The faeces helps to determine the side edges, so currently it is mandatory)
    These faces are deleted during the process, but can be recreated…
  • Path should be that wide to hold the all projection, otherwise will unexpected result.
  • Works on SketchUp 2020.0 or newer (I guess I can do it for older version too)

You can play with it, copy-paste, the code to Ruby console, select a curve and the road group, then type:

cut_road

You can UNDO it.

BACKUP before you try!
No warranties!
Use at your own risk!

Code (Hidden, because it not very nice yet ;-)
def cut_road
  model = Sketchup.active_model
  entities = model.active_entities
  sel = model.selection
  curve = sel.grep(Sketchup::Edge).first.curve
  curve_pts = curve.vertices.map(&:position)
  road = sel.grep(Sketchup::Group).first
  road_tr = road.transformation
  road_edges = road.entities.grep(Sketchup::Edge)
  sides = road_edges.select{|e|
    e.faces.size == 1 &&
    !(e.start.edges.size == 2 && e.end.edges.size == 2)
  }
  return if sides.size < 4
  spokes = road_edges - sides
  fe = sides.find{|edge|
    (edge.end.edges.select{|e| sides.include?(e) }.size == 1 ||
    edge.start.edges.select{|e| sides.include?(e) }.size == 1)
  }
  side_one = [fe]
  edown = sides - [fe]
  until side_one.size == sides.size / 2
    new = edown.find{ |e| side_one.last.vertices.any?{ |v| v == e.start || v == e.end } }
    side_one << edown.delete(new)
  end
  side_two = sides - side_one
  lowest_pt_z = spokes.flat_map(&:vertices).map{|v|
    v.position.transform(road_tr).z
  }.min
  zb = lowest_pt_z - 1.0

  model.start_operation('_Test', true)

  knives = entities.add_group
  spokes.each{|e|
    p1 = e.start.position.transform(road_tr)
    p2 = e.end.position.transform(road_tr)
    p3 = p2.offset(Z_AXIS)
    p4 = p1.offset(Z_AXIS)
    knives.entities.add_face(p1, p2, p3, p4)
  }
  cuttter = entities.add_group
  curve_pts.each_with_index{ |pt, i| 
    next if i == 0
    p1 = curve_pts[i].clone
    p2 = curve_pts[i-1].clone
    p3 = p2.clone
    p3.z = zb
    p4 = p1.clone
    p4.z = zb
    cuttter.entities.add_face(p1, p2, p3, p4)
  }

  cut = cuttter.entities.intersect_with(false, 
                                        cuttter.transformation,
                                        road.entities,
                                        road.transformation,
                                        false,
                                        knives.entities.to_a)

  cuttter.erase!
  knives.erase!
  sel.clear
  model.active_path = [road]
  sel.add side_one
  result = UI.messagebox("Delete this side?\n\nYES to delete selected side\nNO to delete the other side.", MB_YESNO)
  sel.clear
  to_erase = (result == IDYES) ? side_one : side_two
  to_erase = to_erase.flat_map(&:vertices).uniq.flat_map(&:edges).uniq
  road.entities.erase_entities(to_erase)
  to_erase = road.entities.grep(Sketchup::Edge).select{|e| e.line[1].parallel?(Z_AXIS)}
  road.entities.erase_entities(to_erase)
  points = []
  road.entities.grep(Sketchup::Edge){|e| 
    points << e.start.position if e.start.edges.size == 1
    points << e.end.position if e.end.edges.size == 1 
  }
  bb = Geom::BoundingBox.new
  bb.add(*points)
  pt = bb.min
  pts = []
  while points.length > 0
    hash = {} 
    points.each{|p| hash[p] = pt.distance(p) }
    sorted = hash.sort{ |a, b| a[1] <=> b[1] }.map { |n| n[0] }
    pts << sorted.first
    pt = sorted.first
    points.delete(sorted.first)
  end
  pts.each_with_index{ |pt, i| 
    next if i == 0
    edge = road.entities.add_line( pts[i-1], pt )
  }
  model.active_path = nil
  
  model.commit_operation
  
end

road_cutterb

You can post an errors from the Ruby console and the situation description here.
That’s it for now :wink:

2 Likes

:slight_smile:

I tested and your code created the necessary geometry to generate the quads.
You managed to create a very useful plugin in a very short time.
Thank you, Dezmo!

1 Like

Perhaps you already see, but I thought a short summary of how it works might be useful, how to place the road to be cut and the curve. (Currently, that it doesn’t become a disturbing factor, it’s best if you make an empty model … which only has the curve and the road.

A brief summary of how it works:

After determining both side edges and the spokes of the road, broadly speaking, the following happens:

Creating a group with faces generated from the spokes of the road up (1 inch height), paralel to Z_AXIS

Creating a group with faces generated from the curve segments 1 inch down below the road, paralel to Z_AXIS

Initiate intersect entities of the above groups, and the intersection lines to appear in a road group, so these edges can cut the road spokes.

Using the facts, the cutting edges are vertical, and the new vertices of cut edges is connected to the user selected side edges, this edges are deleted:

Borrowing the technique from JHS Connect Guide Points to line up and connect the points formed by the ends of the exposed edges, is the last step.

I plan to turn it into a “regular” plugin at some point (rbz) and put into the first post. ( I can’t promise any deadline… :blush: )

Do you prefer a menu or a toolbar, context-menu or combination of any of these, to execute?

In the meantime, you can test with other curves and roads to see if you find any problems…

Hi Dezmo,

and thank you for detailing how the code works!

If it’s not more complicated, maybe you’ll add the plugin to your SketchUcation account to be easily discovered by everyone, it’s a very useful plugin.

Regarding the date when you will turn it into a plugin, I read your message when I was on the subway and I wanted to write to you then that, taking into account how fast you worked to create it, it will probably be ready to be published by the time I get home. :stuck_out_tongue_winking_eye:

The fact that the code and the steps to follow are already published and working is enough, but because you will turn it into a plugin, and seeing the requirements of people in the forum regarding plugins/extensions… menu, toolbar, shortcut will probably be useful.

As it is preferable to have a simple interface for SketchUp and not have to follow the whole screen with the mouse to click on an icon,

the most important thing is to be able to enter a shortcut to activate the plugin. Right click context menu, if you have many plugins, it would probably be more difficult to find/use.

The plugin is so useful that it allows you to create a mountain road more easily, even if it has ‘rest/overtaking areas’ (I think the correct name in English could be slow vehicle bays/lanes) on both sides of the road.

sbl-02

I did another test and, just like yesterday, I found that it is useful for the path to exceed a little at the ends. If it is exactly the length of the road, two triangles appear at the ends instead of four sides (even this is easily solved).

Thank you!

Thanks for feedback!
If the curve is exactly the length of the road, for some reason the last vertical cut line does not created. (There is a reported bug in Intersect Faces - intersect_with method in Ruby - when in some occasion this can happen. Perhaps, this case also involved… I will check later.)
So make you path longer … :wink:

Until - perhaps between the big Christmas visit to relatives and after some stuffed cabbage and wine - I make a plugin, just a small advise.

You do not need to copy-paste the code all time. Once you hit the enter it is loading for that season of SU. So you just need to type in the command cut_road after selection and hit Enter.

Moreover, if you click on a console input area and hit the up arrow the last commands is retrieved in a reverse order how its entered.
So click on a console input area hit then Enter

advise

1 Like

Thanks for the info!
There’s no rush, Dezmo. Even the code itself because you created and published it (very quickly) is excellent, taking into account that it exceeds a simple code. The fact that you will turn it into a plugin is not a rush.

1 Like

Small update:
I just made it compatible with SU2017 - but, I did not changed others in a code and I don’t think I will update it anytime soon. :blush:
I’ll be honest - because I think it’s right, even if it casts a bad light on me - I somehow lost my intention/interest and can’t bring myself to think about this extension any further… sorry!
… but- at least - you will have e a dirty plugin to be able to install as “normal” rbz.

You will get a menu item in Extension menu “Dezmo Cut Road Quads”, therefore you can assign a shortcut.
Should work same as before. That’s it for now. :peace_symbol: :beer:

Dezmo_Cut_Road_Quads.rbz (1.3 KB)

As I wrote before, you have already done a lot, the current addition is more than enough for this extension.
Thank you, @dezmo!

2 Likes