Thanks for all the replies!
I ended up using the method that both Ruts and fredo6 explained. There's a special circumstance when the arbitrary line runs along a face parallell to the line that I had to consider in the code (the skip_intersection part). Here's the code I ended up with:
def solid_contains_point?(solid, point, include_shell = true)
return false unless solid.manifold?
return false unless solid.bounds.contains? point
point = point.transform(solid.transformation.inverse)
line_vector = Geom;;Vector3d.new(1, 0, 0)
line = [point, line_vector]
previous_intersection_point = previous_classify_result = nil
intersection_points = Array.new
solid.definition.entities.find_all { |e| e.instance_of? Sketchup;;Face }.each { |face|
intersection_point = Geom.intersect_line_plane(line, face.plane)
if intersection_point and intersection_point != previous_intersection_point
classify_result = face.classify_point(intersection_point)
point_on_face = [Sketchup;;Face;;PointInside, Sketchup;;Face;;PointOnVertex, Sketchup;;Face;;PointOnEdge].include? classify_result
angle = line_vector.dot(point.vector_to(intersection_point))
if point_on_face and ((angle >= 0 and include_shell) or (angle > 0 and not include_shell))
intersection_points.push [intersection_point, classify_result]
previous_intersection_point = intersection_point
end
end
}
previous_skip_intersection = false
intersections = 0
intersection_points.sort! { |a,b| point.vector_to(a[0]).length <=> point.vector_to(b[0]).length }.each { |array|
intersection_point = array[0]
classify_result = array[1]
# skip intersection point if there's another face that contains both the current
# intersection point and the previous intersection point
skip_intersection = false
if previous_classify_result and previous_classify_result != Sketchup;;Face;;PointInside
solid.definition.entities.find_all { |e| e.instance_of? Sketchup;;Face }.each { |e|
if classify_result == Sketchup;;Face;;PointOnVertex
if e.vertices.find_all { |vertex| vertex.position == intersection_point or vertex.position == previous_intersection_point }.size == 2
skip_intersection = true
break
end
elsif classify_result == Sketchup;;Face;;PointOnEdge
if e.edges.find_all { |edge| intersection_point.on_line?(edge.line) or previous_intersection_point.on_line?(edge.line) }.size == 2
skip_intersection = true
break
end
end
}
end
# count all intersection points connected by face parallell to and on line as one intersection
intersections += 1 if previous_skip_intersection and !skip_intersection
previous_skip_intersection = skip_intersection
next if skip_intersection
intersections += 1
previous_intersection_point = intersection_point
previous_classify_result = classify_result
}
intersections.odd?
end