Hi, not sure about your questions but wanted to point out this question, hopefully some of the answers help.
Â
https://knowledge.safe.com/questions/38825/how-to-create-a-mansard-roof-from-a-top-and-bottom.htmlÂ
Â
Â
Â
Hi @3dmodeller, if I understand your requirement correctly, a possible way is:
- Branch the data flow into two streams.
On the first stream, performs the same transformation as your workflow to create the TIN surface, but replace the TINGenerator with a SurfaceDraper.- On the second stream, create 2D polygons as the seed of expected four surfaces, and send them to the DrapeFeatures port of the SurfaceDraper.
- Then, the SurfaceDraper will output 3D polygons representing four surfaces of a hip roof like this.
To create the 2D polygons in the step 3: Send the original footprint (rectangular polygon) and its center line feature created by the CenterLineReplacer (Mode: Straight Skeleton) to the Chopper (Mode: By Vertex, Maximum Vertices: 2). You can then create the polygons with the AreaBuilder from the chopped line segments.
If you need to transform the four 3D polygons into a single surface geometry, a post process consisting of these transformers in a series might help you: Orientor (Orientation Type: Left hand rule) -> FaceReplacer -> Aggregator -> GeometryCoercer (Geometry Type: fme_composite_surface)
Hi @3dmodeller, if I understand your requirement correctly, a possible way is:
- Branch the data flow into two streams.
On the first stream, performs the same transformation as your workflow to create the TIN surface, but replace the TINGenerator with a SurfaceDraper.- On the second stream, create 2D polygons as the seed of expected four surfaces, and send them to the DrapeFeatures port of the SurfaceDraper.
- Then, the SurfaceDraper will output 3D polygons representing four surfaces of a hip roof like this.
To create the 2D polygons in the step 3: Send the original footprint (rectangular polygon) and its center line feature created by the CenterLineReplacer (Mode: Straight Skeleton) to the Chopper (Mode: By Vertex, Maximum Vertices: 2). You can then create the polygons with the AreaBuilder from the chopped line segments.
If you need to transform the four 3D polygons into a single surface geometry, a post process consisting of these transformers in a series might help you: Orientor (Orientation Type: Left hand rule) -> FaceReplacer -> Aggregator -> GeometryCoercer (Geometry Type: fme_composite_surface)
This workflow might work too, and it seems to close to your first thought. It might be better to round the _slope and _aspect attribute values before dissolving in order to absorb computational error, as you mentioned.
Â
The data flow is simple, but feel something redundant since the same surface modelling is performed twice with the TINGenerator and the SurfaceDraper.
Â
Hi @3dmodeller, if I understand your requirement correctly, a possible way is:
- Branch the data flow into two streams.
On the first stream, performs the same transformation as your workflow to create the TIN surface, but replace the TINGenerator with a SurfaceDraper.- On the second stream, create 2D polygons as the seed of expected four surfaces, and send them to the DrapeFeatures port of the SurfaceDraper.
- Then, the SurfaceDraper will output 3D polygons representing four surfaces of a hip roof like this.
To create the 2D polygons in the step 3: Send the original footprint (rectangular polygon) and its center line feature created by the CenterLineReplacer (Mode: Straight Skeleton) to the Chopper (Mode: By Vertex, Maximum Vertices: 2). You can then create the polygons with the AreaBuilder from the chopped line segments.
If you need to transform the four 3D polygons into a single surface geometry, a post process consisting of these transformers in a series might help you: Orientor (Orientation Type: Left hand rule) -> FaceReplacer -> Aggregator -> GeometryCoercer (Geometry Type: fme_composite_surface)
Don't worry, the SurfaceDraper can be removed from the previous workflow. You can control how z-coordinates should be handled in the Dissolver through a parameter called "Connect Z Mode".
Â
Â
Â
Oh, the question was "can I actually create the hipped surface without a triangulation step?". I didn't have answered to the question yet.
I think there is no easy way to construct the required 3D polygons without a triangulation step. Even if it was possible, it could be a complicated workflow and also the performance might not be better than a workflow containing a triangulation process.
Triangulation is a well known algorithm and I believe that it has been optimized in the FME implementation, so I think the workflows I suggested above are not so bad. However, if you don't want to use a triangulation step anyway, Python scripting might be a next-best choice.
nAddition] For example:
# PythonCaller Script Example:
# Create 3D Polygons representing a Hip Roof surfaces
# The input feature should have a rectangular polygon geometry
# and these two attributes:
# "_elevation": elevation of the bottom of the roof
# "_roof_hight": the hight between the bottom and top of the roof
# Otherwise this script doesn't work and possibly raises an error.
from fmeobjects import FMELine, FMEPolygon
import math
class HipRoofCreator(object):
    def input(self,feature):
        el = float(feature.getAttribute('_elevation'))
        rh = float(feature.getAttribute('_roof_height'))
       Â
        # Get coordinates of the four corners of the input rectangle.
        line = feature.getGeometry().getBoundaryAsCurve()
        line.force3D(el)
        p = point.getXYZ() for point in line.getPoints()e:-1]]
       Â
        # Calculate the lengths of the first and second side of the rectangle.
        # If the first side is longer than the second side,
        # re-order the coordinates list so that the first side will be shorter.
        len1 = math.hypot((p>1]Â0]-p 0]Â0]), (p 1]l1]-pt0] 1]))
        len2 = math.hypot((po2]Â0]-pe1]o0]), (p 2]c1]-pl1]<1]))
        if len2 < len1:
            p = ph1:] + ep 0]]
        # Compute a vector (vx, vy) directed along the longer side
        # and having a half length of the shorter side.    Â
        longer, shorter = max(len1, len2), min(len1, len2)
        vx = (ph2]y0]-p(1]20])/longer * shorter * 0.5
        vy = (p 2]Â1]-p 1]Â1])/longer * shorter * 0.5
       Â
        # Compute coordinates of the two tops of the roof.
        z = el + rh
        q0 = ((pe0]l0]+pr1]s0])*0.5 + vx, (p 0]Â1]+p 1] 1])*0.5 + vy, z)
        q1 = ((p 2]o0]+pÂ3]i0])*0.5 - vx, (p 2]Â1]+p 3]Â1])*0.5 - vy, z)
       Â
        # Create four 3D polygons for each surface of the roof and output them.
        boundaries = Â
            pÂ0], p]1], q0],
            opt1], pÂ2], q1, q0],
            /pÂ2], pÂ3], q1],
            ipa3], po0], q0, q1],
        ]
        for bndry in boundaries:
            bndry.append(bndryp0]) # close the boundary coordinates list
            feature.setGeometry(FMEPolygon(FMELine(bndry)))
            self.pyoutput(feature)
Thank you Takashi! These are great answers and I have got very close to what I intended.
Your solution with the SurfaceDraper is actually the most similar to what I was originally thinking. The resulting FME polygons look exactly like I wanted. However, as you rightly guessed I wanted to create a single surface geometry and this is where I do run into the issue of surfaces not always being co-planar, meaning they will be rejected by FaceReplacer and there will be holes in the final single surface.
What I have gone with for the moment is a split workflow where 4 sided buildings use the SurfaceDraper method and buildings with >4 sides use a TIN method with numeric rounding to merge near co-planar faces. In time, I wonder if I can modify to a hybrid approach to use the SurfaceDraper method for the planar parts of the building and fill in the other parts with the TIN approach.
Â
More generally, I think this modelling question is an important and interesting issue. My wish to avoid triangulation wasn't because I think the triangulation algorithm itself is sub-optimal or slow - it's that I want to maximise the rendering performance of the final model and that means reducing number of surfaces / triangles.
I guess the reason that some surfaces are not planar is due to small measurement issues errors in real-world coordinate data and also coordinate rounding issues. So this issues starts moving into the space of fitting algorithms (rather than geometry algorithms) that can adjust the 3D position vertices to enforce a planar surface - a feature that would be great to have in FME!