Unable to draw a filled shape with view.draw

I know I’m missing something pretty basic. Essentially, in my custom tool, I want to take the vertices of a selected face and fill it with a color/texture.

points = face.vertices
                    geom_pts = []
                    print points
                    points.each do |pt|
                    puts 'These are geom pts>>'
                    print geom_pts
                    view.draw(GL_LINE_LOOP, geom_pts, texture:$texture_id, uvs:$uvs)

I’ve already set the view drawing color and width which also does not seem to work and also tried other open gl enums

model = Sketchup.active_model
                view = model.active_view
                view.drawing_color = 'purple'
                view.line_width = 10
                texture_path = File.join(__dir__, "/icons/dsp_texture.jpg")
                image_rep = Sketchup::ImageRep.new(texture_path)
                @texture_id = view.load_texture(image_rep)
                @uvs = [ [0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0] ]

I did not cheeked otherwise, but you are using a global variables - which is anyway a very bad practice - in a view.draw method ($texture_id, $uvs ), but you defined instance variables @texture_id, @uvs (I guess that is located before your firstly posted snippet in your original code…)

1 Like

It isn’t clear what else goes with this snippet, but I hope you realize that view#draw can only be called from within a Tool’s draw callback.

Yes, I cannot see any changes to the model even when I’m calling it from within the draw callback of my tool. On a side note, essentially I’m storing multiple Point3D arrays in global $points variable right now and calling it from draw. If it’s not the most performant, what is the way to feed a parameter to the draw callback?

             def draw(view)
                puts 'Entering drawing mode!'
                view.drawing_color = 'green'
                view.line_width = 10
                $points.each do |pts|
                    view.draw(GL_POLYGON, pts)

A Tool is an instance of a class you define. You can store your points in an instance variable. That is the normal way to pass values between Tool callbacks because you can’t control when they are called and can’t modify their argument lists.

Are you also defining get_extents? Without that your polygons could be getting clipped.

I do not think you want to use GL_LINE_LOOP as it relies upon view.line_width= and view.line_stipple= which have defaults (likely 1 and solid respectively.) But the line width is scaled according to the user’s display scaling on the monitor that SketchUp is running on.

Basically, if the face has 4 vertices I would say use GL_QUADS and if more than GL_POLYGON. These are not affected by display scaled line widths.

You are. Either as Steve said you need to define a getExtents callback within your tool and inside this create a bounding box object that encompasses the @points.
So, somewhere in the tool wherever you determine the face, you get its vertices, their positions (@points) and create the tool’s bounding box immediately …

@bb = Geom::BoundingBox.new.add(@points)

… then your getExtents callback just returns that bounds whenever SketchUp’s graphics engine wants it.

def getExtents

Whenever your tool needs to expand the bounds, it need only add more points to @bb.

Another way is to simply have getExtents return its model’s bounds. Ie, a tool instance belongs to the toolstack collection for individual models. It is common to pass the active model instance reference into a tool’s constructor. …

model = Sketchup.active_model

In you tool …

def initialize(model)
  @model = model
  # ... other initialization ...

… and your tool’s getExtents callback …

def getExtents

… okay?

Now the other basic thing is that in order for the SketchUp graphics engine to draw to the view, … for either a tool or an overlay, … the view must be invalidated with view.invalidate.

  • Do not call view.invalidate from within your tool’s (or overlay’s) draw callback.
    It will crash SketchUp or lock it up within an endless loop.

Normally a tool will call view.invalidate at strategic places like after a certain mouse click on a desired object (a face in your case) and also likely in the activate, suspend, resume and deactivate callbacks.

NO … I third what the others have said. Global variables are a no-no. You would assign the face’s vertices positions to a @points instance variable, likely within your tool’s onLButtonUp callback.

There is no good reason for any of your code or data to exist or evaluate outside your top-level namespace module and each of your extensions should be in a submodule of your namespace module. The tool class would be wrapped inside your extension submodule.

Lastly, the indentation in your snippets is extreme. Ruby uses 2 space indents by convention.
It’s best to set your code editor to replace TABs with 2 spaces so that when pasted into the forum, we are not forced to scroll horizontally to read the snippets.