Imprint write_image-images with a text in the bottom

I create images with write_image that is exported (automatically by our extension) to another system.
I want to imprint the images with a text in the bottom of the image.

I had a solution using add_note, however I was using scale_factor to get it to the size I wanted it, and discovered that scale_factor affects other things, like how edges are drawn, so that didn’t work out.

I have now made a solution using add_3d_text, and place it the text in a position close to the camera so it renders in the bottom of the view.
I’m using pickray to find the place in the model for its, and transform_by_vectors to “stretch” the text in order to compensate for the perspective (so the text appear as it was rendered in 2d onto the image).

      model = Sketchup.active_model
      view = model.active_view
      
      textDistanceFromCamera = 5
      textHeightAsPartOfView = 0.05

      bottomLeft = view.pickray(0,view.vpheight)
      bottomLeft = bottomLeft[0].offset(bottomLeft[1],textDistanceFromCamera)
      topLeft = view.pickray(0,(1-textHeightAsPartOfView)*view.vpheight)
      topLeft = topLeft[0].offset(topLeft[1],textDistanceFromCamera)
      topRight = view.pickray(view.vpwidth,(1-textHeightAsPartOfView)*view.vpheight)
      topRight = topRight[0].offset(topRight[1],textDistanceFromCamera)
      bottomRight = view.pickray(view.vpwidth,view.vpheight)
      bottomRight = bottomRight[0].offset(bottomRight[1],textDistanceFromCamera)

      bottomTextWidth = bottomLeft.distance(bottomRight)
      topTextWidth = topLeft.distance(topRight)
      textHeight = bottomLeft.distance_to_line([topLeft, topRight])

      textgroup = model.entities.add_group
      textgroup.entities.add_3d_text(
        "Hello world ÅHg", 
        TextAlignLeft,
        "arial",
        false, #is_bold
        false, #is_italic
        textHeight*0.65, #letter_height
        0.0, #tolerance
        0.0, #z
        true, #is_filled
        0.0 #extrusion
      )
      
      vertices = textgroup.entities.select{|e|e.is_a?(Sketchup::Edge)}.map(&:explode_curve).flat_map{|edge|[edge.start, edge.end]}.uniq
      vectors = vertices.map{|vertex| [vertex.position.y/textHeight * (vertex.position.x-bottomTextWidth/2)/bottomTextWidth * (topTextWidth-bottomTextWidth), 0, 0]}
      textgroup.entities.transform_by_vectors(vertices,vectors)
      
      textgroup.transform!(Geom::Transformation.new(bottomLeft,(bottomRight-bottomLeft),(topLeft-bottomLeft)))

This seems to work for my application, but it’s kind of a “workaround solution”.

So what is my question ?
In general, is there some obvious better/less complicated way of doing this ?

Also, I’m assuming pickray returns a ray originating from the camera eye (/“camera plane” for parallell projection), is this a reasonable assumption?

The pickray method is used to retrieve a ray passing through a given screen position in the viewing direction.

You are giving an x,y coordinates of the screen as a parameter, so the ray(line) is not “originating from the camera eye” but passing through a given screen position, and the vector (the second item in a returned array) is exactly same as a view.camera.direction` vector.

__
I don’t know if it is better, worse, less complicated…whatever, but this is another way that “suddenly” occurred to me.
Using the write_image dump framebuffer capabilities, you can capture “everything” - including you draw in screen space. (which can only be called from within the Tool#draw method of a tool that you implement in Ruby…)

This is a concept code, you need to “fine tune” it… :wink:

module Dezmo
module TestWriteFb
  extend self
  
  class WriteToScreenTest
    def initialize(view, text = "test")
      @text = text
      @text_size = 30
      @text_options = {
        :font => "Arial",
        :color => [255,0,0],
        :size => @text_size,
        :bold => false,
        :align => TextAlignLeft,
        :vertical_align => TextVerticalAlignBaseline
      }
      _x,@y = view.corner( 2 )
      view.invalidate
    end
    
    def draw(view)
      view.draw_text([20, @y - (@text_size + 4)],
                      @text, 
                      @text_options )
    end
  end

  def writte_fb(text = "text missing...")
     options = {
      :filename => File.join(Sketchup.temp_dir, 'example.png'),
      :source => :framebuffer,
      :compression => 0.9,
    }
    model = Sketchup.active_model
    view = model.active_view
    @write_text = WriteToScreenTest.new(view, text)
    model.tools.push_tool(@write_text)
    UI.start_timer(0, false){
      view.write_image(options)
    }
    UI.start_timer(0.2, false){
      model.tools.pop_tool
      view.invalidate
    }
  end

end
end
Dezmo::TestWriteFb.writte_fb("Hello world test1")

1 Like

Ah, this is better.
I found draw_text but quickly disregarded it because I read it can only be invoked from within a tool. Didn’t know it was so easy to make a tool.
Thanks.

Making a simple tool is easy. There are more complicated ones. :wink:
It is also important how you activate and deactivate this one so that you do not “disturb” the user while using their current tool.

1 Like