@placidfury said:
or manually edit the SVG code so SketchUp will read it properly
Well, SVG is XML and Nokogiri would certainly be up to the task. Wouldn't even need SketchUp to do the processing...
Greg
@placidfury said:
or manually edit the SVG code so SketchUp will read it properly
Well, SVG is XML and Nokogiri would certainly be up to the task. Wouldn't even need SketchUp to do the processing...
Greg
For windows, the file at https://dl.bintray.com/oneclick/rubyinstaller/ruby-1.8.7-p370-i386-mingw32.7z
contains a socket so file.
I'd suggest you thoroughly test it. I added it to both, it loaded, and Ruby responded correctly when I called a method with incorrect arguments.
Greg
@mingteck said:
simple acoustical modelling
It depends on what you mean by simple. Using standard formulas based on volume and surface area by absorption is one thing, much past that and the calc time might be a bit long...
Jiva,
It's complicated. I started using Ruby years ago with SketchUp. Been away from SketchUp for while, but I'm still using Ruby.
As I recall, the files get split into two locations, you have to edit the SketchUp.exe file, etc.
I've also been building Ruby for quite a while, so the files I used were my own and built with current compilers & toolchains. With SU 2017, it can only run with Ruby 2.3 due to the changes that occurred in Ruby 2.4.
There may be a publicly available build of Ruby 2.3 in the (hopefully near) future...
The problem is the ruby build currently included in SU 2017. Not sure why output varies by Windows version.
If one replaces it with a different build, command output will appear in the console.
Over in that other place, there's a thread addressing some similar questions for OSX and Windows.
Re Windows, since Ruby backticks don't work as expected, trying to grab return text from an external command is messy. In the other place, I think Dan posted code using cmd redirection to a file, which will work.
If you want a Windows MAC address and can assume a version of SU with the Ruby std-lib included, I posted a WIN32OLE example here
Mario,
This might help (I reformatted your code a bit), see the last couple of lines --
# require 'sketchup.rb' # why? not needed for this code
module McF3D
class McF3DTools
def initialize
@mod = Sketchup.active_model # Open model
@ent = @mod.entities # All entities in model
@sel = @mod.selection # Current selection
end
def perceOuv
unless @sel[0].is_a?(Sketchup;;ComponentInstance) &&
@sel[0].definition.behavior.cuts_opening?
UI.messagebox "Sélectionnez un composant F3D !"
end
end
end # class McF3DTools
end # module McF3D
obj = McF3D;;McF3DTools.new()
obj.perceOuv()
The topic of modules & classes in programming is very complex. Conversely, what most people writing a SketchUp extension need to do is a very small subset of that topic.
HTH,
Greg
Mario,
Glad that helped.
@mariocha said:
is the
def Initialize
a must ?
Technically, no. But, if there's no need for it, one might ask the question 'should this be a module?' The answer would vary amongst programmers, and isn't dependent on that single criteria.
[EDIT] I should add that when you create a class, it is a Ruby Class object, which has a :new method, which calls initialize if it exists. Just in case you were wondering...
Greg
Nathan,
Simply put, the best solution is to have your program flow based on callbacks from the WebDialog. This often creates problems for people, as the UI.inputbox is blocking. So the code is something like this:
def start_method
# some code to initialize and load inputbox
UI.inputbox(*args)
# more code for operation
end
The above is very linear and not easily changed to an event driven style. Better to start with something like:
def start_method
# some code to initialize and load inputbox
UI.inputbox(*args1)
ui_input_done(args2)
end
def ui_input_done(*args2)
# more code for operation
end
Then, if you decide a WebDialog is needed:
def start_method
@dlg = UI;;WebDialog.new()
# load @dlg html, etc
@dlg.add_action_callback { |dlg, ret|
# gather data from dlg
ui_input_done(*args2)
}
@dlg.show()
end
To really give the user a good UI experience, one has to write javascript for the WebDialog.
Greg
[anchor="1" goto="http://msp-greg.github.io/su_info/index.html":17i7mw7a]GitHub.io SketchUp Misc Docs[/anchor:17i7mw7a]
@bomastudio said:
Hi Guys, I have an agony-problem with a 3D rotation.
The transform that TIG suggested (rotation) works for your need. The docs on transformations aren’t very clear, especially for people who aren’t familiar with 3D transforms or linear algebra. They’re also a little quirky, as the trans.to_a method returns a matrix that is the transpose of what I would expect. FWIW, I had a lot of linear algebra (long time ago)...
Anyway, the rotation method returns a transformation based on a rotation in a plane, which is defined by the ‘axis’ parameter, which should be the normal to the plane of rotation.
If the start and end vectors are parallel, they do not define a unique plane, hence, both my original ‘linear algebra’ algorithm and the rotation method fail. The code below accounts for that –
Greg
module SUMath
def self.transVector2Vector(vStart, vEnd, pt = nil)
ptRot = pt || [0,0,0]
if vStart.samedirection?(vEnd)
# return identity transformation, vectors are in the same direction
Geom;;Transformation.new
elsif vStart.parallel?(vEnd)
#return 180 transformation, vStart and vEnd in opposite direction
Geom;;Transformation.axes(ptRot, [-1,0,0], [0,-1,0], [0,0,-1] )
else
# return rotation transform based on vStart and vEnd
# vCross is normal to plane of rotation
vCross = vStart.cross(vEnd)
ang = vStart.angle_between(vEnd)
Geom;;Transformation.rotation(ptRot, vCross, ang)
end
end
def self.rotateFace(face, vector_end, pt = nil)
# for demo purposes, the next 2 statements chose an arbitrary face
# if no face passed
unless face
entities = Sketchup.active_model.entities
faces = entities.grep(Sketchup;;Face)
face = faces[0]
end
face_normal = face.normal
t = transVector2Vector(face_normal, vector_end, pt)
entities.transform_entities(t, face)
# below is just for a demo confirmation
sd = face.normal.samedirection?(vector_end)
sVEnd = "[#{vector_end.join(",")}]"
puts "face.normal.samedirection?(#{sVEnd}) = #{sd}"
end
end
# load module thru console
# type SUMath.rotateFace(nil, [0,0,1]) into console with whatever vector
# you want, it can be typed as often as you'd like.
# As a demo, best done with one face in the model
Try the following --
module SUMath
def self.transVector2Vector (vStart, vEnd)
v1 = vStart.normalize
v2 = vEnd.normalize
return Geom;;Transformation.new if v1.samedirection?(v2)
v3 = v2.cross(v1)
v4 = v3.cross(v1)
c = v2.dot(v1)
s = v2.dot(v4.normalize)
m1 = Geom;;Transformation.axes( [0,0,0], v1, v4, v3)
m2 = Geom;;Transformation.axes( [0,0,0], [c,s,0], [-s,c,0], [0,0,1] )
mT = m1 * m2 * m1.inverse
end
def self.rotateFace(s_vector_end)
vector_end = s_vector_end.split(",").collect { |i| i.to_f }
entities = Sketchup.active_model.entities
faces = entities.grep(Sketchup;;Face)
face = faces[0]
face_normal = face.normal
t = transVector2Vector(face_normal, vector_end)
entities.transform_entities(t, face)
par = face.normal.samedirection?(vector_end)
puts "face.normal.samedirection?([#{s_vector_end}]) = #{par}"
end
end
# load module thru console
# type SUMath.rotateFace("0,0,1") into console, or use whatever vector,
# you can type repeatedly. Best to have just one face in the model...
Greg
Try the following (class variable just in case...)
@@valueq = 1
def slab(valueq)
puts "valueq = #{valueq}"
end
# below is for testing
command = UI;;Command.new("ss") { slab(@@valueq) ; @@valueq += 1 }
# normal line is below, as before
# command = UI;;Command.new("ss") { slab(@@valueq) }
command.small_icon = command.large_icon = path + "/slab.png"
command.tooltip = command.status_bar_text = "s"
menu.add_item command
toolbar.add_item command
toolbar.show
As you click the icon, you should see valueq increment each time.
Note, inside of braces/blocks, the || are only used when the calling code has parameters to pass. Command.new doesn't do that. Methods like .each (and others) do...
Greg
One can also wrap Dan's code in a method
def self.operation(opName, procElse = nil, procEnsure = nil)
if (block_given? && String === opName)
am = Sketchup.active_model
begin
am.start_operation(opName, true)
yield
am.commit_operation
rescue => e
am.abort_operation
puts("Error operation block #<#{e.class.name};#{e.message}.>")
puts(e.backtrace) if $VERBOSE
else
procElse.call() if (Proc === procElse)
ensure
procEnsure.call() if (Proc === procEnsure)
end
else
raise "Error operation parameters - No block given or opName not a String!"
end
end
# call by
# operation("My operation") { operation block }
Note that if either procElse and procEnsure are used, they should probably be lambdas. One should also be careful about return statements in the block passed to the method.
BTW, I'll use snake case for methods, maybe not variables...
Greg
Using instance variables
def slab(var1, ..., varN)
...
end
command = UI;;Command.new("ss") { |@var1, ..., @varN| slab(@var1, ..., @varN) }
Greg
@sdmitch said:
"initializeForm" is called when the html is loaded but I am not knowingly doing anything that would cause the html to reload when the select button is clicked.
Remove the form tags. Forms have some odd default behavior, in this case, reloading the html...
Attached an rb file with some changes --
I did remove the top of the code.
Greg
Thom,
@tt_su said:
I think I've seen this when you hook into the load event. Though hooking into the DOMContentLoaded appear to not suffer from this. Hooking into this might differ from browser to browser (glares at IE).
I updated my post with the code to use the DOMContentLoaded event, if available...
Thanks,
Greg
The following will draw a canvas line on load, then will draw three more lines via button click.
Shows callbacks from WebDialog to Ruby, both on WD init and from button clicks. Also shows execute_script (Ruby calls into WD javascript)
Thom - I'm one of the old-fashioned guys that think jQuery is overkill for
environments like SU
HTH...
Greg
def canvasTest()
cntr = 0 # counter for button clicks
aPtSt = [] # 2 element array for line start point [x,y]
aPtEnd = [] # 2 element array for line end point [x,y]
html = <<-HTML
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta content="IE=edge" http-equiv="X-UA-Compatible" />
<style type="text/css">
em {text-decoration;underline; font-style;normal; }
#myCanvas {border;3px solid #d3d3d3; }
#bNext {height;2em; width;100px; margin-left;50px; }
</style>
<script type="text/javascript">
'use strict';
var $el = function(x) { return document.getElementById(x); },
ctx,
inSU = true;
// dLine - draw canvas line
function dLine(stX, stY, endX, endY) {
ctx.moveTo(stX,stY); ctx.lineTo(endX,endY); ctx.stroke();
};
// clkBtn - button click
function clkBtn(e) { talk("Next"); };
// winLoad - set doc variables, attach events to DOM elements,
// then tell SU we're ready
function winLoad() {
ctx = $el("myCanvas").getContext("2d");
$el("bNext").addEventListener('click', clkBtn, false);
talk('winLoad');
};
// talk - used for SU callbacks, also script debugging
// set inSU = false to output to browser console
function talk(sSend) {
if (inSU) window.location = 'skp;' + sSend;
else console.log(sSend);
};
if ('DOMContentLoaded' in document)
document.addEventListener('DOMContentLoaded', winLoad, false);
else
window.addEventListener('load', winLoad, false);
</script>
</head>
<body>
<div><canvas id="myCanvas" width="200" height="200" /></div>
<button id="bNext" accesskey="n"><em>N</em>ext Line</button>
</body>
</html>
HTML
dlgCvs = UI;;WebDialog.new("Canvas", false, "WDID", 300, 300, 10, 10, true)
# add callback for onload event
dlgCvs.add_action_callback("winLoad") { |dlg,ret|
dlgCvs.execute_script("dLine(50,50,50,150);")
}
# add callback for button click, loop thru lines for drawing square
dlgCvs.add_action_callback("Next") { |dlg,ret|
case cntr
when 0 then aPtSt = [50,150] ; aPtEnd = [150,150]
when 1 then aPtSt = [150,150] ; aPtEnd = [150,50]
when 2 then aPtSt = [150,50] ; aPtEnd = [50,50]
end
if (cntr < 3)
cntr += 1
dlg.execute_script("dLine(#{aPtSt.join(',')},#{aPtEnd.join(',')});")
end
}
dlgCvs.set_html(html)
dlgCvs.show
end
canvasTest()
Sam,
@sdmitch said:
Greg,
Based on your example, I now know it is possible to reference a function using dlg.execute_script.
Glad that helped. On Windows, the dlgCvs.show code executes immediately and steps to the next line of code, so you have to muc around with the 'load' event, since the DOM isn't yet loaded in the WD. Just the way it works, which does make sense, given that the show_modal method blocks (at least on Windows) until the dialog is closed.
You can also string together several method calls in execute_script. Hence, if the following is the code in the Ruby winLoad method
str = dLine(50,50,50,150);dLine(50,150,150,150);dLine(150,150,150,50);dLine(150,50,50,50);"
dlgCvs.execute_script(str)
a square will be drawn. BTW, there might be a limit to how long the parameter string can be in execute_script, but I don't know what it is...
As an aside, I did a canvas project (unrelated to SU) a while back. With good code, canvas is very fast...
Greg
John,
@driven said:
not on a mac...
it requires a right click to see the first content...I agree about jQuery as it's hard to debug in SU...
john
Cool...
Okay, being a Windows type, I don't have a mac handy to test with. If I try the html in Chrome (and set the inSU flag to false), I do see the callback to winLoad in the console.
Hence, do you (or Thom, or anyone on a mac) have any idea why this happens? FWIW, the main feature of my plugin is exporting and importing files with other software. Those software packages only run on a pc. Hence, I've never felt a need to make my plugin run on a mac...
Thanks,
Greg
SDMitch (Mitch?),
@sdmitch said:
Tried your code and found two problems.
- if(reInc.match(cd)) needs to be if(reInc.match(cd.name))
Thanks for the catches. I edited & corrected the second post. I tested mine with a string array, not an array of cd's. Hence, no name prop.
@sdmitch said:
- the prefix of the new name entered gets incremented as well.
That's why you had the new cd name assign at the end of the loop. That's why I shouldn't change code after I've tested it...
Thanks,
Greg