draw.py use settings instead of direct member methods

This commit is contained in:
Thomas Krijnen
2026-05-09 21:19:55 +02:00
parent da5ebf172e
commit c3a10694ba
3 changed files with 106 additions and 35 deletions
+34
View File
@@ -141,6 +141,12 @@ inline namespace settings {
static constexpr const char* const description = "Draws a horizontal line at the height of building storeys in vertical drawings. Accepted values are none, full, and left.";
};
struct SvgProfileThreshold : public SettingBase<SvgProfileThreshold, int> {
static constexpr const char* const name = "profile-threshold";
static constexpr const char* const description = "Limits the number of projected wire profiles for non-wall and non-slab elements in SVG output. A negative value disables the limit.";
static constexpr int defaultvalue = -1;
};
struct SvgStoreyHeightLineLength : public SettingBase<SvgStoreyHeightLineLength, double> {
static constexpr const char* const name = "storey-height-line-length";
static constexpr const char* const description = "Length of the line when --draw-storey-heights=left.";
@@ -164,12 +170,23 @@ inline namespace settings {
static constexpr bool defaultvalue = false;
};
struct SvgUnifyInputs : public SettingBase<SvgUnifyInputs, bool> {
static constexpr const char* const name = "svg-unify-inputs";
static constexpr const char* const description = "Unify input shapes before SVG projection.";
static constexpr bool defaultvalue = false;
};
struct SvgSegmentProjection : public SettingBase<SvgSegmentProjection, bool> {
static constexpr const char* const name = "svg-segment-projection";
static constexpr const char* const description = "Segment result of projection with respect to original products.";
static constexpr bool defaultvalue = false;
};
struct SvgSubtractBefore : public SettingBase<SvgSubtractBefore, std::string> {
static constexpr const char* const name = "svg-subtract-before";
static constexpr const char* const description = "Controls which shapes are cut before SVG hidden-line projection. Accepted values are auto, slabs-and-walls, and always.";
};
struct SvgPolygonal : public SettingBase<SvgPolygonal, bool> {
static constexpr const char* const name = "svg-write-poly";
static constexpr const char* const description = "Approximate every curve as polygonal in SVG output.";
@@ -194,6 +211,18 @@ inline namespace settings {
static constexpr bool defaultvalue = false;
};
struct SvgMirrorY : public SettingBase<SvgMirrorY, bool> {
static constexpr const char* const name = "svg-mirror-y";
static constexpr const char* const description = "Mirror SVG output along the Y axis.";
static constexpr bool defaultvalue = false;
};
struct SvgMirrorX : public SettingBase<SvgMirrorX, bool> {
static constexpr const char* const name = "svg-mirror-x";
static constexpr const char* const description = "Mirror SVG output along the X axis.";
static constexpr bool defaultvalue = false;
};
struct SvgDoorArcs : public SettingBase<SvgDoorArcs, bool> {
static constexpr const char* const name = "door-arcs";
static constexpr const char* const description = "Draw door opening arcs for IfcDoor elements.";
@@ -251,15 +280,20 @@ class SerializerSettings : public SettingsContainer <
SvgAutoSection,
SvgAutoElevation,
SvgDrawStoreyHeights,
SvgProfileThreshold,
SvgStoreyHeightLineLength,
SvgUseNamespace,
SvgUseHlrPoly,
SvgUsePrefiltering,
SvgUnifyInputs,
SvgSegmentProjection,
SvgSubtractBefore,
SvgPolygonal,
SvgAlwaysProject,
SvgWithoutStoreys,
SvgNoCss,
SvgMirrorY,
SvgMirrorX,
SvgDoorArcs,
SvgSectionHeight,
SvgSectionHeightFromStoreys,
+27 -30
View File
@@ -159,52 +159,49 @@ def main(
# Initialize serializer
buffer = ifcopenshell.geom.serializers.buffer()
serialiser_settings = ifcopenshell.geom.serializer_settings()
sr = ifcopenshell.geom.serializers.svg(buffer, geom_settings, serialiser_settings)
sr.setFile(files[0])
if settings.auto_floorplan:
sr.setSectionHeightsFromStoreys()
serialiser_settings.set("section-height-from-storeys", True)
# setElevationRefGuid and setElevationRef are also mutually exclusive in C-code.
# elevation-ref-guid and elevation-ref are also mutually exclusive in C-code.
# Note that guid or object type are not checked anywhere to be valid,
# it's up to user to keep them valid for the provided projects.
if settings.drawing_guid or settings.drawing_object_type:
if settings.drawing_guid:
if not by_guid(settings.drawing_guid):
raise ValueError(f"Unable to find guid {settings.drawing_guid!r}")
sr.setElevationRefGuid(settings.drawing_guid)
serialiser_settings.set("elevation-ref-guid", settings.drawing_guid)
elif settings.drawing_object_type:
sr.setElevationRef(settings.drawing_object_type)
sr.setWithoutStoreys(True)
serialiser_settings.set("elevation-ref", settings.drawing_object_type)
serialiser_settings.set("svg-without-storeys", True)
# required for svgfill
sr.setPolygonal(True)
sr.setUseNamespace(True)
serialiser_settings.set("svg-write-poly", True)
serialiser_settings.set("svg-xmlns", True)
sr.setAlwaysProject(settings.include_projection)
sr.setProfileThreshold(settings.profile_threshold)
sr.setBoundingRectangle(settings.width, settings.height)
sr.setScale(settings.scale)
sr.setAutoElevation(settings.auto_elevation)
sr.setAutoSection(settings.auto_section)
sr.setPrintSpaceNames(settings.space_names)
sr.setPrintSpaceAreas(settings.space_areas)
sr.setDrawDoorArcs(settings.door_arcs)
sr.setNoCSS(not not settings.css)
serialiser_settings.set("svg-project", settings.include_projection)
serialiser_settings.set("profile-threshold", settings.profile_threshold)
serialiser_settings.set("bounds", f"{settings.width}x{settings.height}")
serialiser_settings.set("scale", str(settings.scale))
serialiser_settings.set("auto-elevation", settings.auto_elevation)
serialiser_settings.set("auto-section", settings.auto_section)
serialiser_settings.set("print-space-names", settings.space_names)
serialiser_settings.set("print-space-areas", settings.space_areas)
serialiser_settings.set("door-arcs", settings.door_arcs)
serialiser_settings.set("svg-no-css", bool(settings.css))
if settings.subtract_before_hlr:
sr.setSubtractionSettings(W.ALWAYS)
serialiser_settings.set("svg-subtract-before", "always")
sr.setUseHlrPoly(settings.hlr_poly)
sr.setUsePrefiltering(settings.prefilter)
sr.setUnifyInputs(settings.unify_inputs)
sr.setMirrorY(settings.mirror_y)
serialiser_settings.set("svg-poly", settings.hlr_poly)
serialiser_settings.set("svg-prefilter", settings.prefilter)
serialiser_settings.set("svg-unify-inputs", settings.unify_inputs)
serialiser_settings.set("svg-mirror-y", settings.mirror_y)
try:
sh = ["none", "full", "left"].index(settings.storey_heights)
sr.setDrawStoreyHeights(sh)
except:
if settings.storey_heights not in {"none", "full", "left"}:
raise ValueError("storey_heights should be one of {'none', 'full', 'left'}")
serialiser_settings.set("draw-storey-heights", settings.storey_heights)
sr = ifcopenshell.geom.serializers.svg(buffer, geom_settings, serialiser_settings)
sr.setFile(files[0])
"""
# It is also possible to add drawing planes manually
+45 -5
View File
@@ -97,10 +97,10 @@ namespace {
const double PI2 = M_PI * 2.;
std::pair<double, double> parse_svg_bounds(const std::string& value) {
unsigned int width = 0;
unsigned int height = 0;
if (std::sscanf(value.c_str(), "%ux%u", &width, &height) == 2 && width > 0 && height > 0) {
return { static_cast<double>(width), static_cast<double>(height) };
double width = 0.;
double height = 0.;
if (std::sscanf(value.c_str(), "%lfx%lf", &width, &height) == 2 && width > 0. && height > 0.) {
return { width, height };
}
throw std::runtime_error("Invalid use of --bounds");
}
@@ -123,9 +123,27 @@ double parse_svg_scale(const std::string& value) {
numerator > 0 && denominator > 0) {
return static_cast<double>(numerator) / denominator;
}
double scale = 0.;
if (std::sscanf(value.c_str(), "%lf", &scale) == 1 && scale > 0.) {
return scale;
}
throw std::runtime_error("Invalid use of --scale");
}
subtract_before_project parse_subtract_before_project(const std::string& value) {
const auto setting = boost::to_lower_copy(value);
if (setting == "auto" || setting == "slabs-at-floorplans" || setting == "slabs-at-floor-plans") {
return ON_SLABS_AT_FLOORPLANS;
}
if (setting == "slabs-and-walls") {
return ON_SLABS_AND_WALLS;
}
if (setting == "always") {
return ALWAYS;
}
throw std::runtime_error("Invalid use of --svg-subtract-before, expected auto|slabs-and-walls|always");
}
SvgSerializer::storey_height_display_types parse_storey_height_display(const std::string& value) {
const auto display = boost::to_lower_copy(value);
if (display == "none") {
@@ -163,7 +181,7 @@ void SvgSerializer::apply_settings() {
setDrawingCenter(center.first, center.second);
}
if (settings().get<SvgSectionHeightFromStoreys>().get()) {
if (settings().get<SvgSectionHeightFromStoreys>().get() && file) {
if (settings().get<SvgSectionHeight>().has()) {
setSectionHeightsFromStoreys(settings().get<SvgSectionHeight>().get());
} else {
@@ -181,11 +199,19 @@ void SvgSerializer::apply_settings() {
setUseNamespace(settings().get<SvgUseNamespace>().get());
setUseHlrPoly(settings().get<SvgUseHlrPoly>().get());
setUsePrefiltering(settings().get<SvgUsePrefiltering>().get());
setUnifyInputs(settings().get<SvgUnifyInputs>().get());
setSegmentProjection(settings().get<SvgSegmentProjection>().get());
setPolygonal(settings().get<SvgPolygonal>().get());
setAlwaysProject(settings().get<SvgAlwaysProject>().get());
setWithoutStoreys(settings().get<SvgWithoutStoreys>().get());
setNoCSS(settings().get<SvgNoCss>().get());
setMirrorY(settings().get<SvgMirrorY>().get());
setMirrorX(settings().get<SvgMirrorX>().get());
setProfileThreshold(settings().get<SvgProfileThreshold>().get());
if (settings().get<SvgSubtractBefore>().has()) {
setSubtractionSettings(parse_subtract_before_project(settings().get<SvgSubtractBefore>().get()));
}
if (settings().get<SvgDrawStoreyHeights>().has()) {
setDrawStoreyHeights(parse_storey_height_display(settings().get<SvgDrawStoreyHeights>().get()));
@@ -2425,7 +2451,18 @@ std::string SvgSerializer::nameElement(express::Base elem_) {
}
void SvgSerializer::setFile(ifcopenshell::file* f) {
using namespace ifcopenshell::geometry::settings;
file = f;
auto apply_section_heights_from_storeys = [&]() {
if (settings().get<SvgSectionHeightFromStoreys>().get()) {
if (settings().get<SvgSectionHeight>().has()) {
setSectionHeightsFromStoreys(settings().get<SvgSectionHeight>().get());
} else {
setSectionHeightsFromStoreys();
}
}
};
auto storeys = f->instances_by_type("IfcBuildingStorey");
if (storeys.empty()) {
@@ -2449,6 +2486,7 @@ void SvgSerializer::setFile(ifcopenshell::file* f) {
delete matrix;
#endif
logger::warning("No building storeys encountered, used for reference:", product);
apply_section_heights_from_storeys();
return;
}
}
@@ -2459,6 +2497,8 @@ void SvgSerializer::setFile(ifcopenshell::file* f) {
logger::warning("No building storeys encountered, output might be invalid or missing");
}
apply_section_heights_from_storeys();
}
void SvgSerializer::setSectionHeight(double h, express::Base storey) {