Variable Space Between lines

Thanks Dan,

I started to follow this step by step and you make the instance in def make_geometry so there should be the “LenX” attribute. but where should the redraw be placed? after the spacings.each {|interval| part? because that didn’t worked for me. This is the very last thing i need to know about this code but i can’t find were it goes wrong.

def make_component(cname = "New Component Name")
  model = Sketchup.active_model
  definitions = model.definitions
  cdef = definitions.load ( "Z:/Algemeen 2016/Systeemvloeren/Tekeningen/Sketchup/C-profiel 3.skp" ) 
  return cdef
end

def make_geometry( cdef, trans )
  aents = Sketchup.active_model.active_entities
  aents.add_instance( cdef, trans )
  aents.set_attribute( 'dynamic_attributes', '_lenx_formula', "100" )
end

@start_point = Geom::Point3d::new( 0, 0, 0)

@model.start_operation( "C-profiel" )
    # Make the component definition:
    comp_def = make_component("C-profiel")
    # Create a transformation set to the start point:
    trans = Geom::Transformation.new( @start_point )     
    
    spacings.each {|interval|
    # Create a x-axis vector for this interval:
      vec = Geom::Vector3d::new( interval, 0, 0 )
    # Apply this interval vector to the running transform:
      trans = trans * Geom::Transformation::new( vec )
    # Call a method that uses the transformation:
      t = Geom::Transformation.scaling @start_point, interval/3.937, 1 , 1 
      make_geometry( comp_def, trans*t )
    }

   dcs = $dc_observers.get_latest_class
   dcs.redraw_with_undo(aents)

  @model.commit_operation

These lines need to be inside the method where you create the instance, AND where the reference aents is valid. References created inside the scope of a method, do not exist outside that method. They are destroyed when the call to that method ends, and recreated when the method is called again.


BUT, if all of the instances are going to be the same, then you can change the definition’s default LenX, instead of changing every instance.

Ok,

Maybe there is an easier way to this but i never done this with methodes.

In the picture below an example from what i need. The C-profiel is white and uses the interval. So the instance is the same only the LenX is variable. In this picture is the first one 4.000 mm and the second one 5.000 mm. So this length should be updated so i can use them to generate a report.

You are the programmer. You decide what to do.

Isn’t it obvious that you would need to pass the interval into the make_geometry() method so you could use it to set the instance’s length or use the interval to apply a scaling to the instance ?

You’ll have to do one or the other. It is up to you.

I prefer using interval in the make_geometry() method to set each length because i don’t like scaling a component.

But i have tried in my opinion everything but i don’t get it to work as it should

The most error i seen in the console is

Error: #<NoMethodError: undefined method `transform!' for #<Sketchup::ComponentDefinition:0x000176066b4e50>>

You have your redraw at the wrong place. Try this:

def make_component(cname = "New Component Name")
  model = Sketchup.active_model
  definitions = model.definitions
  cdef = definitions.load ( "Z:/Algemeen 2016/Systeemvloeren/Tekeningen/Sketchup/C-profiel 3.skp" ) 
  return cdef
end

def make_geometry( cdef, trans )
  aents = Sketchup.active_model.active_entities
  aents.add_instance( cdef, trans )
  aents.set_attribute( 'dynamic_attributes', '_lenx_formula', "100" )
   dcs = $dc_observers.get_latest_class
   dcs.redraw_with_undo(aents)
end

@start_point = Geom::Point3d::new( 0, 0, 0)

@model.start_operation( "C-profiel" )
    # Make the component definition:
    comp_def = make_component("C-profiel")
    # Create a transformation set to the start point:
    trans = Geom::Transformation.new( @start_point )     
    
    spacings.each {|interval|
    # Create a x-axis vector for this interval:
      vec = Geom::Vector3d::new( interval, 0, 0 )
    # Apply this interval vector to the running transform:
      trans = trans * Geom::Transformation::new( vec )
    # Call a method that uses the transformation:
      t = Geom::Transformation.scaling @start_point, interval/3.937, 1 , 1 
      make_geometry( comp_def, trans*t )
    }

  @model.commit_operation

One problem with calling dcs.redraw_with_undo(aents), is that I think it breaks your operation. You should check to make sure.

Hi Neil,

Thanks for helping. I also tried it your way as you should do normally after the instance part. But the code is breaking up as you said and you get the transform error.

Really don’t know how to solve this

It’s a little more work up front, but I don’t use dynamic components anymore. I draw everything from scratch as needed. Once you have the code in place it is very fast to simply clear the contents of a component and redraw it to the new specs.

The idea is this:

  1. Main def to create components and figure out where to put them.
  2. Main def then determines the specs for the component and calls another def to do the drawing. draw_box(cinst,10,20,30)
def draw_box(cinst, width = 1,depth = 1, height = 1)
  if cinst
    cdef = cinst.definition
    cdef.entities.clear!
    f = cdef.entities.add_face([0,0,0],[width,0,0],[width,depth,0],[0,depth,0])
    f.pushpull -height
  end
end

Many times faster than a DC. Window.rb (10.0 KB)

[quote=“vanlion, post:28, topic:33248”][quote=“Neil_Burkholder, post:27, topic:33248”]
One problem with calling dcs.redraw_with_undo(aents), is that I think it breaks your operation. You should check to make sure.
[/quote]

I also tried it your way as you should do normally after the instance part. But the code is breaking up as you said and you get the transform error.
[/quote]

Try simply:

def make_geometry( cdef, trans, interval )
  aents = Sketchup.active_model.active_entities
  aents.add_instance( cdef, trans )
  aents.set_attribute( 'dynamic_attributes', '_lenx_formula', interval.to_s )
  dcs = $dc_observers.get_latest_class
  progressbar = false
  dcs.redraw( aents, progressbar )
end

?

The only other option is to queue up the new instances to be redrawn, and then iterate the array afterward calling the redraw_with_undo():

def make_geometry( cdef, trans, interval )
  aents = Sketchup.active_model.active_entities
  aents.add_instance( cdef, trans )
  aents.set_attribute(
    'dynamic_attributes', '_lenx_formula', interval.to_s 
  )
  return aents # return a reference to the new instance
end

@start_point = Geom::Point3d::new( 0, 0, 0)

@insts_to_redraw = [] # redraw queue

@model.start_operation( "C-profiel" )
    # Make the component definition:
    comp_def = make_component("C-profiel")
    # Create a transformation set to the start point:
    trans = Geom::Transformation.new( @start_point )     
    
    spacings.each {|interval|
    # Create a x-axis vector for this interval:
      vec = Geom::Vector3d::new( interval, 0, 0 )
    # Apply this interval vector to the running transform:
      trans = trans * Geom::Transformation::new( vec )
    # Call a method that uses the transformation:
      t = Geom::Transformation.scaling @start_point, interval, 1 , 1 
      @insts_to_redraw << make_geometry( comp_def, trans*t, interval )
    }

@model.commit_operation

dcs = $dc_observers.get_latest_class

for i in @insts_to_redraw
  dcs.redraw_with_undo(i)
end

Thanks Guys,

I first tried the codes Dan gave me.

The first one gives me the error:

Error: #<ArgumentError: wrong number of arguments (2 for 3)>
(eval):69:in `make_geometry'
(eval):86:in `activate'
(eval):108:in `select_tool'
(eval):108:in `block (2 levels) in initialize'
SketchUp:1:in `call'

The second code gives me this error:

Error: #<NoMethodError: undefined method `set_attribute' for #<Sketchup::Entities:0x00025e63c799a8>>
(eval):72:in `make_geometry'
(eval):95:in `block in activate'
(eval):88:in `each'
(eval):88:in `activate'
(eval):108:in `select_tool'
(eval):108:in `block (2 levels) in initialize'
SketchUp:1:in `call'

This would suggest a transcription error or typo. Take a look at the line where you invoke make_geometry. You have omitted one of the three arguments.

I believe this is due to a typo in the code Dan gave. I think it should have been

def make_geometry( cdef, trans, interval )
  aents = Sketchup.active_model.active_entities
  newinst = aents.add_instance( cdef, trans )
  newinst.set_attribute(
    'dynamic_attributes', '_lenx_formula', interval.to_s 
  )
  return newinst # return a reference to the new instance
end

Thanks this is working :slight_smile: i only had to change:

t = Geom::Transformation.scaling @start_point, interval, 1 , 1
into:
t = Geom::Transformation.scaling @start_point, interval/3.937, 1 , 1

to get it in mm.

Also my selfmade cutout material is working and updating it as it should without doing a “scale definition” (see picture)

The second thing i saw is that the meassure is correct in “generate report” but in the component options LenX stays on
100 mm. But this is not a big issue.

Vanlion did the booboo in post 22.
Prior to that, there was no assignment of an attribute.
After that Neil and I both just copied his mistake.
Spank us all! :hand_splayed: (whack)

That looks better!

…and, yes, I added the interval reference to the parameters for the make_geometry() method.

… and I told you that was poor practice (I think in another thread.)
That is why I removed it from the example.

You only convert to the user’s model units in the inputbox,
and use the SketchUp Length class internally in code.

Please read Thomas Thomassen’s blog:

Thank you Dan i going to take a good look how Thom this is doing with model units.

I have now everything to make my construction builder. Thank you all for the support :slight_smile: i’m learning
Much from this.

Thanks for this tip that is working great. Only in my code it’s not working. Thoms example is working with like this:

prompts = ['Width', 'Height']
defaults = [5.m, 2.m]
input = UI.inputbox( prompts, defaults, 'Create Square' )
# User enters Width: 300cm, Height 4
p input
# => ["My Own Square", 118.110236220472, 157.48031496063]
p input.map { |n| n.class }
# => [String, Length, Length]
p input.map { |n| n.to_s }
# => ["My Own Square", "3000mm", "4000mm"]

But in my example i use it in an other way and than we are going to split the values.

defaults = [2x5.m,2x2.m]
input = UI.inputbox( prompts, defaults, 'Create Square' )
# User enters Width: 300cm, Height 4
p input
# => ["My Own Square", 118.110236220472, 157.48031496063]
p input.map { |n| n.class }
# => [String, Length, Length]
p input.map { |n| n.to_s }
# => ["My Own Square", "3000mm", "4000mm"]

But you get an error “Cannot convert ``2x5000mm” to Length``` because I have to use 2x5.m is there a way to fix this?

Why can’t you use 5.m here? And if you can’t do without the multiplication sign, in Ruby, as in most computer languages, multiply is * not x.

John asks a valid question: the expression “2x5.m” is a fixed “manifest” constant, why not just write that constant?

I can understand why you say * instead of x
But i use the ‘x’ to split values as you can see in the code above. That is the Reason its not working i think

[quote=“vanlion, post:37, topic:33248”]
defaults = [2x5.m,2x2.m]
[/quote] Consider…
defaults = ["2x5.m", "2x2.m"]
Then split those strings at ‘x’ ?

However, your thinking seems muddled.
‘input’ should be an array from the two prompts which would return two lengths.
How your p input returns a array with a string and two floats is not explained ??

Why must the user input in this odd way - why not 4 inputs for lengths and numbers ?

Why not default your lengths sensibly - e.g. 5.m and 2.m ??
And while you are at it consider 5000.mm and 2000.mm as you seem to be using mm anyway ?

Why must you have 2x5.m ?
Wouldn’t 10.m be more logical.
Why do you need to have the x ??
Why split values at all ??

width, height = input

Automatically formatted as a length as the user types in…