How Do I Script Pushing a Face To The Opposite Side of a Box?

Dear Friends,

In Ruby sketchup I have a face on one side of a box. Now I wish to pushpull it till other side of box. How can I do it? I don’t know how to find other side face. Thank you for your help in advance.

oh sorry for my mistake. I mean in Ruby sketchup.

I’ll move this to the right category and edit the title, then.

1 Like

In the spirit of the old “teach a man to fish”, I won’t give you a pre-written code answer, just textual outline of an approach. If you can’t figure it out from that, come back and show us what you tried and we will point out where you went astray.

Can I assume that you know how to invoke Face#pushpull? I.e. that the question isn’t how to do the pushpull, but rather identifying the end Face after the extrusion?

As usual when programming, there are multiple ways to proceed. A key factor is how “clean” was the Entities collection containing the start Face before the pushpull? Things get messier if those Entities already contained other Edges and Faces because you have to test more carefully to be certain you found the desired end Face rather than some other pre-existing Face.

If the start Face was the only thing in its Entities (e.g. if you created a new Group and added the Face to the Group’s Entities), then you just have to search that Entities collection for a new, different Face that is parallel to the start Face (i.e. their normal vectors are parallel). There will be only one such, and it will be the end Face.

If there were other Faces in the Entities before the pushpull then it is possible that there will be more than one Face parallel to the start Face. Then you have to search through them to find the right one. One way to test is to get the Vertices and compare their positions to those of the Vertices of the original Face. There must be the same number of vertices, and they must all be offset from the original Face’s Vertices by the distance used in pushpull and in the direction of the start Face’s normal.

Edit: BTW please correct your forum profile. It says you are running SketchUp Make (desktop) 2020, which does not exist. The last version of Make is 2017.

1 Like

I could create a face on one side of a box and also I can pushpull it. I don’t know how can I don’t know how can I calculate pushpull distance.
I corrected my profile as your advice.
Thank you.

The distance is one of the parameters to the pushpull method. You can’t invoke pushpull without a distance. It sounds like you are saying you don’t know how big you want the box to be?

It still says SketchUp Make 2020. There is no Make edition for 2020 !


Do you understand this ?

# Given a face referenced as: face1
pt1 = face1.outer_loop.vertices[0].position
# Fire a ray in the reverse direction of face1's normal vector:
result = model.raytest( [ pt1, face1.normal.reverse ] )

# Check the result. nil if failure:
if result
  # The point that the ray hit:
  pt2 = result.first
  # The face that the ray hit:
  if result.last[0].is_a?(Sketchup::Face)
    face2 = result.last[0]
    # Distance between pt1 and pt2 (on face2):
    dist  = pt1.distance(pt2)
    # PushPull in negative direction:
    face1.pushpull(-dist)
  end
end

REF:

Dear Dan,

Your codes works well. Also you explain it clearly. My problem solved. Thank you so much.

Thank you so much for your attention. Next time, I will ask more clear questions. My mistake.

if box is in a group than we can edit its face, answer of following if is false. Can you help me for it?

  if result.last[0].is_a?(Sketchup::Face)

Dear Dan,
You can see my problem in following example. If I delete last “if” it works.

UI.menu("Plugins").add_item("Box") {
Sketchup.active_model.select_tool(Box.new)
}
class Box
  def activate
    mod = Sketchup.active_model
#Make a group

    pt1 = [0, 0, 0]
    pt2 = [2, 0, 0]
    pt3 = [2, 10, 0]
    pt4 = [0, 10, 0]
    grp = mod.active_entities.add_group
    face1 = grp.entities.add_face [pt1, pt2, pt3, pt4]
    face1.pushpull -10
  end
  def deactivate(view)
    view.invalidate
  end
  def onLButtonDown(flags, x, y, view)
    mod = Sketchup.active_model
  # select face
    input = Sketchup::InputPoint.new
    input.pick view, x, y
    view.model.active_path = input.instance_path.valid? ? input.instance_path : nil
    face1 = input.face
    # Given a face referenced as: face1
    pt1 = face1.outer_loop.vertices[0].position
# Fire a ray in the reverse direction of face1's normal vector:
    result = mod.raytest( [ pt1, face1.normal.reverse ] )
# Check the result. nil if failure:
    if result
  # The point that the ray hit:
      pt2 = result.first
  # The face that the ray hit:
      if result.last[0].is_a?(Sketchup::Face)
        face2 = result.last[0]
    # Distance between pt1 and pt2 (on face2):
        dist  = pt1.distance(pt2)
    # PushPull in negative direction:
        face1.pushpull(-dist)
      end
    end
  end  
end

In your profile you mentioning you are using SU Make 2017.

But if you are using this code:

…In earlier version than SU 2020 you will get this error massage:

Error: #<NoMethodError: undefined method `active_path=' for #<Sketchup::Model:0x000001cb186baff0>>
<main>:24:in `onLButtonDown' 
  • If the code runs without error then you are using SU2020.
  • If you are using SU 2017 Make the code will not run properly.
    Which one is true?

Codes run without any problem. I try to follow friends advice.

So, you are using the SU 2020. Would you please correct your profile?

1 Like

I did it and next time I will tell others SU 2020 does exist.

It does.
But SU 2020 Make does not!

https://www.sketchup.com/download/all

1 Like

I hope my profile is right now. Thank you for your help.

2 Likes

Sorry, it was a “boo-boo” on my part. I did not know you would be inside a group.
When this is true, the instance path array returned by model.raytest has more than 1 member in it, so [0] cannot be used because it assumes only 1 member in the array.

This should explain it better …

# encoding: UTF-8

module Majid866
  module BoxTest

    class Box

      def activate
        @mod = Sketchup.active_model
        # Define 4 points:
        pts = [
          [0, 0, 0],
          [2, 0, 0],
          [2, 10, 0],
          [0, 10, 0]
        ]
        # Make a group
        grp = @mod.active_entities.add_group
        face1 = grp.entities.add_face(*pts)
        # Pushpull into 3D:
        face1.pushpull(-10)
        # Draw a face to select:
        pts = [
          [2, 2, 2],
          [2, 8, 2],
          [2, 8, 8],
          [2, 2, 8]
        ]
        face1 = grp.entities.add_face(*pts)
        instance_path = Sketchup::InstancePath.new([grp])
        @mod.active_path= instance_path
        Sketchup.status_text = "Choose a face ..."
        @state = 1
      end

      def deactivate(view)
        view.invalidate
        Sketchup.status_text = ""
      end

      def suspend(view)
        view.invalidate
        Sketchup.status_text = ""
      end

      def resume(view)
        view.invalidate
        Sketchup.status_text = "Choose a face ..." if @state == 1
      end

      def onLButtonDown(flags, x, y, view)
        # @mod is set as the active model
        # Select face
        input = Sketchup::InputPoint.new
        input.pick(view, x, y)
        #@model.active_path = input.instance_path.valid? ? input.instance_path : nil
        face1 = input.face
        if face1 && face1.is_a?(Sketchup::Face)
          Sketchup.status_text = ""
          @state = 2
          push_through(face1)
        end
      end ### onLButtonDown()

      def push_through(face1)
        # Given a face referenced as: face1
        pt1 = face1.outer_loop.vertices[0].position
        # Fire a ray in the reverse direction of face1's normal vector:
        result = @mod.raytest( [ pt1, face1.normal.reverse ] )
        # Check the result. nil if failure:
        if result
          # The point that the ray hit:
          pt2 = result.first
          instance_path = result.last
          # The face that the ray hit:
          puts "the result object: #{instance_path.last.inspect}"
          if instance_path.last.is_a?(Sketchup::Face)
            face2 = instance_path.last
            # Distance between pt1 and pt2 (on face2):
            dist = pt1.distance(pt2)
            # PushPull in negative direction:
            face1.pushpull(-dist)
          end
        end
      end ### push_through()

    end # class Box

    unless defined? @loaded
      @loaded = true
      UI.menu("Plugins").add_item("Box") {
        Sketchup.active_model.select_tool(Box.new)
      }
    end

  end # module BoxTest
end # module Majid866
1 Like

Thank you so much, it works well also your explanation help me so much to write my codes in right way. You are great!!!

Please stop quoting full posts from other members.
(You were told already, so you’ve earned your first flag.)