def load_svg(path): """ """ paths, attributes = svg2paths(path) result = ddd.group2() for k, v in enumerate(attributes): #print(v) # v['d'] # print d-string of k-th path in SVG # Ex svgpath = 'M10 10 C 20 20, 40 20, 50 10Z' mpl_path = parse_path(v['d']) ''' import matplotlib.pyplot as plt fig = plt.figure(figsize=(200, 200)) ax = fig.add_subplot(111) ax.axis([0, 0, 200, 200]) collection = matplotlib.collections.PathCollection([mpl_path]) collection.set_transform(ax.transData) #patch = matplotlib.patches.PathPatch(mpl_path, facecolor="red", lw=2) ax.add_artist(collection) #ax.add_patch(patch) ax.set_xlim([0, 200]) ax.set_ylim([200, 0]) plt.show() ''' coords = mpl_path.to_polygons(closed_only=True) item = ddd.polygon(coords[0]).clean() #.convex_hull() for c in coords[1:]: ng = ddd.polygon(c).clean() #.convex_hull() #ng.show() #print (ng.geom.is_valid) #if not ng.geom.is_valid: continue if item.contains(ng): item = item.subtract(ng) else: item = item.union(ng) #result = ddd.group([ddd.polygon(c) for c in coords], empty=2) result.append(item) #result = result.scale([1.0 / (48 * 64), -1.0 / (48 * 64)]) #result = result.simplify(0.005) # #result.show() result = result.union().scale([1, -1]).clean(0) xmin, ymin, xmax, ymax = result.bounds() result = result.translate([0, -(ymin + ymax)]) #result = ddd.align.anchor(result, ddd.ANCHOR_CENTER) return result
def reed(height=None, leaves=None): if leaves is None: leaves = random.randint(4, 7) if height is None: height = random.uniform(1.3, 1.9) reed = ddd.group3(name="Reed") for i in range(leaves): lh = height + random.uniform(-0.4, 0.4) lb = 0.02 * lh item = ddd.polygon([[-lb, 0.0], [lb, 0.0], [0, lh]], name="Reed leaf").triangulate( twosided=True).material(ddd.mats.treetop) item = item.rotate(ddd.ROT_FLOOR_TO_FRONT) item = ddd.group([item, item.rotate(ddd.ROT_TOP_CW)], name="Reed leaf") item = ddd.uv.map_cubic(item, scale=(0.2, 2.0)) item = item.rotate([0, 0, random.uniform(0, math.pi)]) item = item.rotate([random.uniform(0.1, 0.3), 0, 0]) torsion_advance = math.pi / 4 item = item.rotate([0, 0, torsion_advance]) rt = 0.15 item = item.translate( [random.uniform(-rt, rt), random.uniform(-rt, rt), 0]) reed.append(item) reed = ddd.align.polar(reed, 0.1, rotate=True) reed = reed.combine() return reed
def generate_areas_2d_ways_interiors(self, union): """ Generates interways areas. """ result = ddd.group2() if not union.geom: return result for c in ([union.geom] if union.geom.type == "Polygon" else union.geom): if c.type == "LineString": logger.warning( "Interways areas union resulted in LineString geometry. Skipping." ) continue if len(c.interiors) == 0: continue logger.info("Generating %d interiors.", len(c.interiors)) for interior in c.interiors: area = ddd.polygon(interior.coords, name="Interways area") if area: area = area.subtract(union) area = area.clean(eps=0.01) #area = area.clean() if area.geom: area.set('ddd:area:interways', True) result.append(area) else: logger.warn("Invalid interways area.") return result
def cart_wheel_axis(height_to_axis=0.1, wheel_radius=0.075, width=0.06, thick_interior=0.032): """ """ #wheel_radius = height_to_axis * 0.75 #height_to_axis = wheel_radius * 1.25 # 0.10 item = ddd.point().line_to([0, -height_to_axis]).line_to( [width, -height_to_axis - wheel_radius * 0.5]).line_to([width, 0]) item = ddd.polygon(item.geom.coords) item = item.translate([-width * 0.5, 0]).triangulate().twosided() item = item.rotate(ddd.ROT_FLOOR_TO_FRONT).rotate(ddd.ROT_TOP_CW) side1 = item.copy().translate([-thick_interior, 0, 0]) side2 = item.copy().translate([thick_interior, 0, 0]) item = side1.append(side2) item = item.combine() item = item.material(ddd.mats.steel) item = ddd.uv.map_cubic(item) item.set('ddd:connector:axis', [0, 0, -height_to_axis]) return item
def childrens_playground_slide(length=4.5, height=None, width=0.60): slide_thick = 0.03 side_thick = 0.06 if height is None: height = length * 0.45 side_mat = random.choice([ddd.mats.metal_paint_red, ddd.mats.metal_paint_green, ddd.mats.metal_paint_yellow]) slideline = ddd.point([0, 0], name="Slide").line_to([0.5, 0]).line_to([3, 1.5]).line_to([3.5, 1.5]) # TODO: slideline.interpolate_cubic(), or slideline.smooth() or similar slideprofile = slideline.buffer(slide_thick / 2, cap_style=ddd.CAP_FLAT) slide = slideprofile.scale([1 / 4.5 * length, 1 / 2.0 * height]) slide = slide.extrude(width - side_thick, center=True).rotate(ddd.ROT_FLOOR_TO_FRONT) slide = slide.material(ddd.mats.steel) slide = ddd.uv.map_cubic(slide) slidesideprofile = slideline.line_to([3.5, 1.7]).line_to([3, 1.7]).line_to([0.5, 0.2]).line_to([0, 0.2]) slidesideprofile = ddd.polygon(list(slidesideprofile.geom.coords), name="Slide profile") stairssideprofile = ddd.polygon([[3.5, 1.5], [3.5, 2], [4, 2], [4, 1.5], [4.5, 0], [4.0, 0], [3.5, 1.5]]) stairssideprofile = stairssideprofile.union(ddd.point([3.75, 2]).buffer(0.25, cap_style=ddd.CAP_ROUND)) stairssideprofile = stairssideprofile.subtract(ddd.point([3.75, 2]).buffer(0.15, cap_style=ddd.CAP_ROUND, resolution=2)) # Hole stairssideprofile = stairssideprofile.translate([-0.25, 0]) slidesideprofile = slidesideprofile.union(stairssideprofile) slidesideprofile = slidesideprofile.scale([1 / 4.5 * length, 1 / 2.0 * height]) slidesideprofile = slidesideprofile.extrude(side_thick, center=True).rotate(ddd.ROT_FLOOR_TO_FRONT) slidesideprofile = slidesideprofile.material(side_mat) slidesideprofile = ddd.uv.map_cubic(slidesideprofile) slidesideprofile1 = slidesideprofile.translate([0, width / 2, 0]) slidesideprofile2 = slidesideprofile.translate([0, -width / 2, 0]) item = ddd.group([slide, slidesideprofile1, slidesideprofile2]) numsteps = int((height - 1) / 0.3) + 1 for i in range(numsteps): step = ddd.box([-0.1, -((width - side_thick) / 2), 0, 0.1, ((width - side_thick) / 2), 0.05], name="Slide Step") step = step.translate([4 - (i + 1) * (0.5 / (numsteps + 1)), 0, (i + 1) * 0.3]).material(ddd.mats.steel) step = ddd.uv.map_cubic(step) item.append(step) item = item.translate([-4.5/2, 0, 0]).rotate(ddd.ROT_TOP_CCW) item = ddd.meshops.batch_by_material(item).clean(remove_degenerate=False) return item
def features_points_voronoi(pipeline, root, logger): points = root.find("/Features2/Points") vor = Voronoi(points.extra['points_coords']) #lines = [ddd.line(vor.vertices[line]) for line in vor.ridge_vertices if -1 not in line] #lines = ddd.group2(lines) #[[], [-1, 0], [-1, 1], [1, -1, 0], [3, -1, 2], [-1, 3], [-1, 2], [0, 1, 3, 2], [2, -1, 0], [3, -1, 1]] #print(vor.regions) regions = [ddd.polygon( [vor.vertices[i] for i in r ] ) for r in vor.regions if -1 not in r] regions = ddd.group2(regions, name="Regions") root.find("/Features2").append(regions)
def process_node(prefix, node): node_path = prefix + "/" + node.name if (node.type == "Polygon2D"): #print(node['polygon']) #print(node['polygon'].__class__.__name__) coords = godot_vector2array(node['polygon'].args) position = godot_vector2(node['position'].args) if ( 'position' in node.properties) else [0, 0] #scale = godot_vector2(node['scale'].args) if ('scale' in node.properties) else [1, 1] rotation = node['rotation'] if ('rotation' in node.properties) else 0.0 visible = node['visible'] if ('visible' in node.properties) else True feat = ddd.polygon(coords, name=node.name) feat = feat.rotate(rotation) feat = feat.translate(position) # Transform should be maintained #feat = feat.scale([0.6, 0.6]) #T TODO: support scale somehow (ideally through godot hierarchy, but at least in metadata) feat.extra['godot:node:path'] = node_path feat.extra['godot:visible'] = visible #print(node_path) process_node_meta(feat, node) features.append(feat) elif (node.type == "Line2D"): coords = godot_vector2array(node['points'].args) position = godot_vector2(node['position'].args) if ( 'position' in node.properties) else [0, 0] visible = node['visible'] if ('visible' in node.properties) else True feat = ddd.line(coords, name=node.name) feat = feat.translate(position) feat.extra['godot:node:path'] = node_path feat.extra['godot:visible'] = visible process_node_meta(feat, node) features.append(feat) for c in node.get_children(): process_node(prefix + "/" + node.name, c)
def georaster_coverage(self): covermap = ddd.group2(name="DDD GeoRaster Coverage") tiles_config = settings.DDD_GEO_DEM_TILES transformers = {} for tc in tiles_config: logger.info("Inspecting DEM file: %s", tc['path']) crs = tc['crs'].lower() transformer = transformers.get(crs, None) if not transformer: transformer = pyproj.Transformer.from_proj(crs, 'epsg:4326', always_xy=True) transformers[crs] = transformer projected_point_x0y0 = transformer.transform(tc['bounds'][0], tc['bounds'][1]) projected_point_x0y1 = transformer.transform(tc['bounds'][0], tc['bounds'][3]) projected_point_x1y0 = transformer.transform(tc['bounds'][2], tc['bounds'][1]) projected_point_x1y1 = transformer.transform(tc['bounds'][2], tc['bounds'][3]) # Generate polygon in wgs84 tile = ddd.polygon([projected_point_x0y0, projected_point_x1y0, projected_point_x1y1, projected_point_x0y1]) tile.name = "Tile: %s" % tc['path'] tile.extra.update(tc) file_size = None try: file_size = os.path.getsize(tc['path']) except FileNotFoundError as e: pass tile.extra['file_size'] = file_size tile.extra['file_available'] = file_size is not None covermap.append(tile) #print(tc) #map.show() filename = "/tmp/ddd-georaster-coverage.geojson" logger.info("Saving map to: %s", filename) covermap.save(filename)
def tennis_net(width, net_height_center=0.914, net_height_post=1.07): """ Tennis net, on XY along the Y axis (since playground fields are seen x: length, y: width). """ post1 = urban.post(net_height_post + 0.15).translate( [0, -width * 0.5, 0]).material(ddd.mats.steel) post2 = urban.post(net_height_post + 0.15).translate( [0, +width * 0.5, 0]).material(ddd.mats.steel) net = ddd.polygon( [[-width * 0.5, 0], [width * 0.5, 0], [width * 0.5, net_height_post], [0, net_height_center], [-width * 0.5, net_height_post]], name="Tennis net") net = net.triangulate(twosided=True).material(ddd.mats.fence) net = net.rotate(ddd.ROT_FLOOR_TO_FRONT).rotate(ddd.ROT_TOP_CCW) net = ddd.uv.map_cubic(net) item = ddd.group3([post1, post2, net]) return item
def post_arm_angled(height=6.0, length=5.0, lamp=None): """ A lamppost arm, with a central anchor point lying _below_ the XY plane. Lamp is copied to the lamp anchor position. TODO: Support anchor points instead (should support assigment/cloning). """ r = 0.5 # Distance to inner point width = 0.3 height_raise = 1.0 shape = ddd.polygon([(0, 0), (0, height - height_raise), (length, height), (r, height - height_raise - r)], name="Lamppost Arm Angled") arm = shape.extrude(width).rotate(ddd.ROT_FLOOR_TO_FRONT) arm = arm.material(ddd.mats.steel) arm = ddd.uv.map_cubic(arm) arm = arm.translate([0, width / 2, height_raise - height]) if lamp: arm.append(lamp.copy().translate([length - 0.3, 0, height_raise - 0.3])) return arm
def pipeline_test_vertex_order_align_snap(pipeline, root): """ Tests geometric operations. """ # Test polygon subtract, and 2D convex hull coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] obj = ddd.polygon(coords).subtract(ddd.rect([1, 1, 2, 2])) ref = obj.convex_hull().material(ddd.MAT_HIGHLIGHT) # Test vertex reordering obj = ddd.geomops.vertex_order_align_snap(obj, ref) result = ddd.group([ obj, ref, ddd.point(obj.geom.exterior.coords[0]).buffer(0.1), ddd.point(ref.geom.exterior.coords[0]).buffer(0.2).material( ddd.MAT_HIGHLIGHT), ]) result.show() root.append(result)
def char(self, ch): def tuple_to_imag(t): return t[0] + t[1] * 1j #face = Face('/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf') face = Face(ddd.DATA_DIR + '/fonts/OpenSansEmoji.ttf') face.set_char_size(self.char_size) face.load_char(ch) #kerning = face.get_kerning(ch, 'x') # or from previous, actually? #print(kerning) outline = face.glyph.outline y = [t[1] for t in outline.points] # flip the points outline_points = [(p[0], max(y) - p[1]) for p in outline.points] start, end = 0, 0 paths = [] for i in range(len(outline.contours)): end = outline.contours[i] points = outline_points[start:end + 1] points.append(points[0]) tags = outline.tags[start:end + 1] tags.append(tags[0]) segments = [ [ points[0], ], ] for j in range(1, len(points)): segments[-1].append(points[j]) if tags[j] and j < (len(points) - 1): segments.append([ points[j], ]) for segment in segments: if len(segment) == 2: paths.append( Line(start=tuple_to_imag(segment[0]), end=tuple_to_imag(segment[1]))) elif len(segment) == 3: paths.append( QuadraticBezier(start=tuple_to_imag(segment[0]), control=tuple_to_imag(segment[1]), end=tuple_to_imag(segment[2]))) elif len(segment) == 4: paths.append( CubicBezier(start=tuple_to_imag(segment[0]), control1=tuple_to_imag(segment[1]), control2=tuple_to_imag(segment[2]), end=tuple_to_imag(segment[3]))) #C = ((segment[1][0] + segment[2][0]) / 2.0, # (segment[1][1] + segment[2][1]) / 2.0) #paths.append(QuadraticBezier(start=tuple_to_imag(segment[0]), # control=tuple_to_imag(segment[1]), # end=tuple_to_imag(C))) #paths.append(QuadraticBezier(start=tuple_to_imag(C), # control=tuple_to_imag(segment[2]), # end=tuple_to_imag(segment[3]))) start = end + 1 path = Path(*paths) #wsvg(path, filename="/tmp/test.svg") path_d = path.d() # https://gis.stackexchange.com/questions/301605/how-to-create-shape-in-shapely-from-an-svg-path-element # This page also has info about SVG reading! #svgpath = 'M10 10 C 20 20, 40 20, 50 10Z' mpl_path = parse_path(path_d) coords = mpl_path.to_polygons(closed_only=True) item = None for c in coords: # coords[1:]: if len(c) < 3: continue ng = ddd.polygon(c) #.clean(eps=char_size / 100) #.convex_hull() #ng.show() if item is None: item = ng elif item.contains(ng): item = item.subtract(ng) else: item = item.union(ng) item = item.clean( eps=self.char_size / 200) # Note that this is effectively limiting resolution #result = ddd.group([ddd.polygon(c) for c in coords], empty=2) result = item result = result.scale([1.0 / self.char_size, -1.0 / self.char_size]) result = result.simplify( 0.005) # Note that this is effectively limiting resolution return (result, face)
area = ddd.polygon([[0, 0], [10, 0], [10, 5], [0, 5]]) lines = sports.football_field_lines_area(area).translate([0, 0, 0.05]) area = area.triangulate().material(ddd.mats.pitch) item = ddd.group2([area, lines]) items.append(item) item.show() ''' functions = (sports.handball_field_lines, sports.basketball_field_lines, sports.tennis_field_lines, sports.football_field_lines) count = 3 for m in functions: items = ddd.group3() for i in range(count): area = ddd.polygon([[0, 0], [10, 0], [10, 5], [0, 5]]).scale(1 + i * 3).rotate(i * 2 * math.pi / count) lines = sports.field_lines_area(area, m).translate([0, 0, 0.05]) area = area.triangulate().material(ddd.mats.pitch) item = ddd.group2([area, lines]) items.append(item) items.show() area = ddd.regularpolygon(7, r=random.uniform(12, 20)).scale([random.uniform(0.5, 1.5), random.uniform(0.5, 1.5)]).rotate(random.uniform(0, math.pi * 2)) lines = sports.field_lines_area(area, m).translate([0, 0, 0.05]) area = area.triangulate().material(ddd.mats.pitch) item = ddd.group2([area, lines]) item.show()
def pipeline_start(pipeline, root): """ Tests subdivision on several geometries (check wireframe). """ items = ddd.group3() # Subdivision to grid fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.rect([-3, -1, -1, 1]) figh = fig1.subtract(fig2) fig = figh.extrude_step(figh, 1.0, base=False, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(figh.buffer(-0.25), 1.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.material(ddd.mats.logo) fig = ddd.uv.map_cubic(fig) fig = ddd.meshops.subdivide_to_grid(fig, 0.5) fig.show() items.append(fig) # Test slicing with plane figa = ddd.meshops.slice_plane(fig, [-1, -1, -1], [0.3, 0.3, 0.3]) #figa = ddd.uv.map_cubic(figa) # This should not be needed, slice_plane should do this figb = ddd.meshops.slice_plane(fig, [1, 1, 1], [0.3, 0.3, 0.3]).material(ddd.MAT_HIGHLIGHT) ddd.group([figa, figb]).show() # Subdivide to grid coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] ref = ddd.polygon(coords).subtract(ddd.rect([1, 1, 2, 2])) obj = ref.triangulate() obj = ddd.meshops.subdivide_to_grid(obj, 2.0) #obj= obj.subdivide_to_size(2.0) #ddd.group3([obj, ref.triangulate().material(ddd.MAT_HIGHLIGHT).translate([0, 0, -1])]).show() items.append(obj.scale([0.1, 0.1, 1]).translate([0, 0, 1])) # Subdivide to grid (cube) obj = ddd.cube(d=2) obj = obj.material(ddd.mats.dirt) obj = ddd.uv.map_cubic(obj) obj = ddd.meshops.subdivide_to_grid(obj, 0.5) #obj.show() items.append(obj) # Subdivide fig1 = ddd.rect().extrude(1) fig1 = fig1.subdivide_to_size(0.5) items.append(fig1) #fig1.show() fig1 = ddd.rect([1, 3]).extrude(1) fig1 = fig1.subdivide_to_size(0.5) items.append(fig1) # Pointy end fig = ddd.point().buffer(0.5, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(ddd.point(), 2) fig = fig.subdivide_to_size(0.5) items.append(fig) # Remove bottom test fig = ddd.cube(d=2) fig = fig.subdivide_to_size(1.0) fig = ddd.meshops.remove_faces_pointing(fig, ddd.VECTOR_DOWN) items.append(fig) # All items items = ddd.align.grid(items, space=10.0) items.append(ddd.helper.all()) items.show() root.append(items)
penetrate=0.5).material(ddd.mats.highlight) fig = ddd.group([point, obj, result]) fig.buffer(0.1).triangulate().show() # Snap point inside with penetration point = ddd.point([4.5, 0]) obj = ddd.point([5, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) result = ddd.snap.project(point, obj, penetrate=0.5).material(ddd.mats.highlight) fig = ddd.group([point, obj, result]) fig.buffer(0.1).triangulate().show() # Irregular object fig = ddd.group2() coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] obj = ddd.polygon(coords) fig.append(obj) for i in range(10): point = ddd.point([random.uniform(-10, 10), random.uniform(-10, 10)]) result = ddd.snap.project(point, obj, penetrate=0).material(ddd.mats.highlight) fig.append(point) fig.append(result) fig.buffer(0.1).triangulate().show() # Irregular object fig = ddd.group2() coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] obj = ddd.polygon(coords) fig.append(obj) for i in range(10):
def pipeline_start(pipeline, root): """ Generate different geometric objects. """ items = ddd.group3() # Remember to use JOIN_ROUND so resolution is applied when buffering points fig = ddd.point([0, 0]).buffer(1.0, resolution=2, join_style=ddd.JOIN_ROUND, cap_style=ddd.CAP_ROUND).triangulate() items.append(fig) fig = ddd.point([0, 0]).buffer(1.0, resolution=3, join_style=ddd.JOIN_ROUND, cap_style=ddd.CAP_ROUND).triangulate() items.append(fig) fig = ddd.point([0, 0]).buffer(1.0, resolution=4, join_style=ddd.JOIN_ROUND, cap_style=ddd.CAP_ROUND).triangulate() items.append(fig) # Extrusion with optional caps fig = ddd.disc().extrude(5) items.append(fig) fig = ddd.disc().extrude(5, base=False) items.append(fig) fig = ddd.disc().extrude(5, cap=False) items.append(fig) fig = ddd.disc().extrude(5, cap=False, base=False) items.append(fig) # Extrude line (to faces, not volume) fig1 = ddd.line([[-2, 0], [0, 0], [2, 2]]) fig = fig1.extrude(2.0).twosided() items.append(fig) # Extrusion to line (explicit) fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.line([[-4, 0], [4, 0]]) fig = fig1.extrude_step(fig2, 1.0) items.append(fig) # Extrusion to line (explicit) fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.line([[-3, 0], [3, 0]]) fig = fig1.extrude_step(fig2, 1.0) items.append(fig) # Extrusion to line (explicit, method subtract) fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.line([[-3, 0], [3, 0]]) fig = fig1.extrude_step(fig2, 1.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) # TODO: this currently fails but should be fixed items.append(fig) # Extrusion to line with vertical (explicit) for skillion roofs fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.line([[-4, 2], [4, 2]]) fig = fig1.extrude_step(fig2, 1.0) # TODO: this currently fails but should be fixed items.append(fig) # Extrusion to line (axis middle) fig1 = ddd.rect([-4, -2, 4, 2]) #.rotate(math.pi * 1.5) axis_major, axis_minor, axis_angle = ddd.geomops.oriented_axis(fig1) fig = fig1.extrude_step(axis_minor, 1.0) items.append(fig) # Extrusion to line (axis middle) fig1 = ddd.rect([-4, -2, 4, 2]) #.rotate(math.pi * 1.5) axis_major, axis_minor, axis_angle = ddd.geomops.oriented_axis(fig1) fig = fig1.extrude_step(axis_major, 1.0) items.append(fig) # Extrusion to line (buffered geometry) - currently fails (shapely does not return the reduced polygon linestring) fig1 = ddd.rect([-4, -2, 4, 2]) fig = fig1.extrude_step(fig1.buffer(-2.5), 1.0) items.append(fig) # Extrusion to line (buffered geometry) and back (fails, extrusion from point to shape) fig1 = ddd.rect([-4, -2, 4, 2]) fig = fig1.extrude_step(fig1.buffer(-2.5), 1.0) fig = fig.extrude_step(fig1, 1.0) items.append(fig) # Triangulation with hole fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.rect([-3, -1, -1, 1]) fig = fig1.subtract(fig2).triangulate() items.append(fig) # Extrusion with hole fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.rect([-3, -1, -1, 1]) fig = fig1.subtract(fig2).extrude(1.0) items.append(fig) # Extrusion with steps with hole fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.rect([-3, -1, -1, 1]) figh = fig1.subtract(fig2) fig = figh.extrude_step(figh, 1.0, base=False) fig = fig.extrude_step(figh.buffer(-0.25), 1.0) items.append(fig) # Extrusion with steps with hole 2 fig1 = ddd.rect([-4, -2, 4, 2]) fig2 = ddd.rect([-3, -1, -1, 1]) figh = fig1.subtract(fig2) fig = figh.extrude_step(figh, 1.0, base=False, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(figh.buffer(-0.25), 1.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Simple extrusion fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND).extrude(5.0) items.append(fig) # Simple extrusion fig = ddd.regularpolygon(5).extrude(5.0) items.append(fig) # Simple extrusion no caps fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig, 5.0, base=False, cap=False) items.append(fig) # Extrusion between shapes fig1 = ddd.point([0, 0]).buffer(1.0) fig2 = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig3 = ddd.point([0, 0]).buffer(1.0) fig = fig1.extrude_step(fig2, 3.0).extrude_step(fig3, 2.0) items.append(fig) # Extrusion fig = ddd.point([0, 0]).buffer(1.0) for i in range(10): fign = ddd.point([0, 0]).buffer(1.0).rotate(math.pi / 12 * i) fig = fig.extrude_step(fign, 0.5) items.append(fig) # Pointy end fig = ddd.point().buffer(2.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(ddd.point(), 5.0) items.append(fig) # Convex shapes (this fails) coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] #coords.reverse() fig = ddd.polygon(coords).scale(0.25) fig = fig.extrude_step(fig.buffer(-0.5), 1) items.append(fig) # Convex shapes - subtract method (works) coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] #coords.reverse() fig = ddd.polygon(coords).scale(0.25) fig = fig.extrude_step(fig.buffer(-0.5), 1, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude-subtract to bigger fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.buffer(1.0), 5.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude-subtract downwards shape = ddd.disc().scale([3, 2]) fig = shape.extrude_step(shape.buffer(-0.5), -1.0, base=False, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(shape.buffer(-1.0), -0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude-subtract vertical case fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig, 5.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Convex shapes with holes - subtract method fig = ddd.group3() text = Text3D.quick_text("86A").scale(2.0) for f in text.children: #f.replace(f.subtract(f.buffer(-0.2))) fe = f.extrude_step(f.buffer(-0.05), 0.2, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig.append(fe) items.append(fig) # Extrude to point fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.centroid(), 2.0) items.append(fig) """ fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.centroid(), 2.0, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) """ # Extrude to empty fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.buffer(-2.0), 2.0) items.append(fig) fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.buffer(-2.0), 2.0, base=False, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude with division fig1 = ddd.disc().translate([1.5, 0]).union(ddd.disc()) fig = fig1.extrude_step(fig1.buffer(-0.2), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(fig1.buffer(-0.5), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude multiple with empty geometry fig1 = ddd.point([0, 0]).buffer(2.0, cap_style=ddd.CAP_ROUND) fig = fig1.extrude_step(fig1.buffer(-0.5), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(fig1.buffer(-1.5), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(fig1.buffer(-2.5), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) fig = fig.extrude_step(fig1.buffer(-2.5), 0.5, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Triangulate/Extrude with colinear segments fig1 = ddd.polygon([[0, 0], [1, 0], [2, 0], [2, 1], [1, 1], [0, 1]]) #fig = fig1.triangulate() fig = fig1.extrude(1.0) items.append(fig) # All items items = ddd.align.grid(items, space=10.0) #items.append(ddd.helper.all()) items.show() root.append(items)
def crane_vertical(): """ Large vertical crane (such as those seen in cargo ports). Inspired by: https://commons.wikimedia.org/wiki/File:Port_crane_of_Mammoet,_Schiedam-8054.jpg """ base_width = 7 base_length = 6 # Piers pier_height = 2.0 pier_width = 1.5 pier_length = base_length + 2 pier = ddd.rect( [-pier_width / 2, -pier_length / 2, pier_width / 2, pier_length / 2], name="Crane Pier") pier = pier.extrude(pier_height).material(ddd.mats.metal_paint_red) pier = ddd.uv.map_cubic(pier) pier_l = pier.translate([-base_width * (2 / 6), 0, 0]) pier_r = pier.translate([base_width * (2 / 6), 0, 0]) piers = ddd.group3([pier_l, pier_r], name="Crane Piers") # Base base_height = 1.5 base = ddd.rect([0, 0, base_width, base_length], name="Crane Base").recenter() base = base.extrude(base_height).translate([0, 0, pier_height]) base = base.material(ddd.mats.cement) base = ddd.uv.map_cubic(base) # Base Tower column_height = 8 column_radius_base = base_width * 0.85 * 0.5 column_radius = column_radius_base * 0.5 column_shape_base = ddd.regularpolygon(4, column_radius_base) column_shape_middle = ddd.regularpolygon(12, column_radius) column = column_shape_base.extrude_step(column_shape_middle, 1, base=False) column = column.extrude_step(column_shape_middle, column_height - 1, cap=False) column = column.translate([0, 0, pier_height + base_height ]).material(ddd.mats.metal_paint_red) column = ddd.uv.map_cubic(column) column.name = "Crane column" # Platform and railing platform_radius = column_radius + 0.85 platform_base_height = pier_height + base_height + column_height - 2.0 platform_shape = ddd.point(name="Crane platform").buffer( platform_radius, cap_style=ddd.CAP_ROUND) platform = platform_shape.triangulate(twosided=True) platform = platform.material(ddd.mats.metallic_grid) platform_fence = platform_shape.outline().extrude(1.2).material( ddd.mats.fence) platform = ddd.group([platform, platform_fence], name="Crane platform") platform = ddd.uv.map_cylindrical(platform) platform = platform.translate([0, 0, platform_base_height]) # WeightBlock block_width = base_width * 0.6 block_length = base_length * 1.25 block_base_height = pier_height + base_height + column_height block_height = 2.2 block = ddd.rect([-block_width / 2, 0, block_width / 2, block_length], name="Crane Weight") block = block.extrude(block_height).translate([0, -2.5, block_base_height ]).material(ddd.mats.cement) block = ddd.uv.map_cubic(block) # Cabin cabin_width = block_width * 0.6 cabin_length = 3 cabin_height = block_height cabin_shape = ddd.rect( [-block_width * 0.5, 0, block_width * 0.5, cabin_length]) cabin_shape_top = ddd.rect( [-block_width * 0.5, 1, block_width * 0.5, cabin_length]) cabin = cabin_shape.extrude_step(cabin_shape, 1) cabin = cabin.extrude_step(cabin_shape_top, cabin_height - 1) cabin = cabin.extrude_step(cabin_shape_top.buffer(-0.4), 0.3) cabin = cabin.material(ddd.mats.metal_paint_yellow) cabin = cabin.translate([0, -2.5 - cabin_length, block_base_height]) cabin = ddd.uv.map_cubic(cabin) mainsupport_width = 2 mainsupport_skew = 2 mainsupport_height = 10 mainsupport_base_height = block_base_height + block_height mainsupport = ddd.rect([0, 0, mainsupport_width, mainsupport_width], name="Main Support").recenter() mainsupport = mainsupport.extrude_step(mainsupport.translate( [0, -mainsupport_skew]), mainsupport_height, base=False) mainsupport = mainsupport.material(ddd.mats.metal_paint_red) mainsupport = mainsupport.translate([0, 0, mainsupport_base_height]) mainsupport = ddd.uv.map_cubic(mainsupport) secsupport_width = 1.5 secsupport_skew = 11 secsupport_height = 18 secsupport_base_height = block_base_height + block_height secsupport = ddd.rect([0, 0, secsupport_width, secsupport_width], name="Sec Support").recenter() secsupport = secsupport.extrude_step(secsupport.scale( [0.8, 0.7]).translate([0, -secsupport_skew]), secsupport_height, base=False) secsupport = secsupport.material(ddd.mats.metal_paint_red) secsupport = secsupport.translate([0, -1.5, secsupport_base_height]) secsupport = ddd.uv.map_cubic(secsupport) maincable1 = cable( [-block_width * 0.4, block_length - 3, mainsupport_base_height], [ -mainsupport_width * 0.2, -mainsupport_skew, mainsupport_base_height + mainsupport_height - 0.2 ]) maincable1 = maincable1.material(ddd.mats.cable_metal) maincable2 = cable( [block_width * 0.4, block_length - 3, mainsupport_base_height], [ mainsupport_width * 0.2, -mainsupport_skew, mainsupport_base_height + mainsupport_height - 0.2 ]) maincable2 = maincable2.material(ddd.mats.cable_metal) seccable1 = cable( [0, -mainsupport_skew, mainsupport_base_height + mainsupport_height], [ 0, -1.5 - secsupport_skew, mainsupport_base_height + secsupport_height - 0.2 ]) seccable1 = seccable1.material(ddd.mats.cable_metal) # Drag cable dragcable_length = 20 dragcable_point = [ 0, -1.5 - secsupport_skew, mainsupport_base_height + secsupport_height - 0.2 ] dragcable_endpoint = [ 0, -1.5 - secsupport_skew, mainsupport_base_height + secsupport_height - 0.2 - dragcable_length ] dragcable = cable(dragcable_endpoint, dragcable_point) dragcable = dragcable.material(ddd.mats.cable_metal) # Pulley block pulley_block_width = 0.5 pulley_block_thick = 0.3 pulley_block_height = 0.8 pulley_block_profile = ddd.polygon( [[-pulley_block_width * 0.25, 0], [pulley_block_width * 0.25, 0], [pulley_block_width / 2, pulley_block_height], [-pulley_block_width / 2, pulley_block_height]], name="Pulley block") pulley_block_profile = pulley_block_profile.buffer( 0.2, resolution=3, join_style=ddd.JOIN_ROUND) pulley_block = pulley_block_profile.extrude(pulley_block_thick).translate( [0, 0, -pulley_block_thick / 2]) pulley_block = pulley_block.rotate(ddd.ROT_FLOOR_TO_FRONT).rotate( ddd.ROT_TOP_CW) pulley_block = pulley_block.material(ddd.mats.metal_paint_yellow) pulley_block = ddd.uv.map_cubic(pulley_block) pulley_block = pulley_block.translate(dragcable_endpoint) # Hook hook_radius = 0.6 hook_radius_inner = 0.35 hook = ddd.sphere(r=hook_radius, name="Hook") hook = hook.scale([0.2, 1.0, 1.0]) hole = ddd.point().buffer(hook_radius_inner, resolution=3, cap_style=ddd.CAP_ROUND).extrude(4.0).translate( [0, 0, -2]) hole = hole.rotate(ddd.ROT_FLOOR_TO_FRONT).rotate(ddd.ROT_TOP_CW) hook = hook.subtract(hole) hook = hook.material(ddd.mats.steel) #hook = ddd.uv.map_cubic(hook) hook = hook.translate(dragcable_endpoint) item = ddd.group3([ piers, base, column, platform, block, cabin, mainsupport, maincable1, maincable2, secsupport, seccable1, dragcable, pulley_block, hook ], name="Crane Vertical") return item
# Extrusion fig = ddd.point([0, 0]).buffer(1.0) for i in range(10): fign = ddd.point([0, 0]).buffer(1.0).rotate(math.pi / 12 * i) fig = fig.extrude_step(fign, 0.5) items.append(fig) # Pointy end fig = ddd.point().buffer(2.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(ddd.point(), 5.0) items.append(fig) # Convex shapes (this fails) coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] #coords.reverse() fig = ddd.polygon(coords).scale(0.25) fig = fig.extrude_step(fig.buffer(-0.5), 1) items.append(fig) # Convex shapes - subtract method (works) coords = [[10, 10], [5, 9], [3, 12], [1, 5], [-8, 0], [10, 0]] #coords.reverse() fig = ddd.polygon(coords).scale(0.25) fig = fig.extrude_step(fig.buffer(-0.5), 1, method=ddd.EXTRUSION_METHOD_SUBTRACT) items.append(fig) # Extrude-subtract to bigger fig = ddd.point([0, 0]).buffer(1.0, cap_style=ddd.CAP_ROUND) fig = fig.extrude_step(fig.buffer(1.0),
def char(ch): def tuple_to_imag(t): return t[0] + t[1] * 1j from freetype import Face #face = Face('/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf') face = Face(ddd.DATA_DIR + '/fonts/OpenSansEmoji.ttf') face.set_char_size(48 * 64) face.load_char(ch) #kerning = face.get_kerning(ch, 'x') # or from previous, actually? #print(kerning) outline = face.glyph.outline y = [t[1] for t in outline.points] # flip the points outline_points = [(p[0], max(y) - p[1]) for p in outline.points] start, end = 0, 0 paths = [] for i in range(len(outline.contours)): end = outline.contours[i] points = outline_points[start:end + 1] points.append(points[0]) tags = outline.tags[start:end + 1] tags.append(tags[0]) segments = [ [ points[0], ], ] for j in range(1, len(points)): segments[-1].append(points[j]) if tags[j] and j < (len(points) - 1): segments.append([ points[j], ]) for segment in segments: if len(segment) == 2: paths.append( Line(start=tuple_to_imag(segment[0]), end=tuple_to_imag(segment[1]))) elif len(segment) == 3: paths.append( QuadraticBezier(start=tuple_to_imag(segment[0]), control=tuple_to_imag(segment[1]), end=tuple_to_imag(segment[2]))) elif len(segment) == 4: C = ((segment[1][0] + segment[2][0]) / 2.0, (segment[1][1] + segment[2][1]) / 2.0) paths.append( QuadraticBezier(start=tuple_to_imag(segment[0]), control=tuple_to_imag(segment[1]), end=tuple_to_imag(C))) paths.append( QuadraticBezier(start=tuple_to_imag(C), control=tuple_to_imag(segment[2]), end=tuple_to_imag(segment[3]))) start = end + 1 path = Path(*paths) #wsvg(path, filename="/tmp/test.svg") path_d = path.d() # https://gis.stackexchange.com/questions/301605/how-to-create-shape-in-shapely-from-an-svg-path-element # This page also has info about SVG reading! from svgpath2mpl import parse_path #svgpath = 'M10 10 C 20 20, 40 20, 50 10Z' mpl_path = parse_path(path_d) coords = mpl_path.to_polygons() # Add or subtract char_2d = ddd.polygon(coords[0]) for c in coords[1:]: ng = ddd.polygon(c) #print (ng.geom.is_valid) if not ng.geom.is_valid: continue if char_2d.contains(ng): char_2d = char_2d.subtract(ng) else: char_2d = char_2d.union(ng) #result = ddd.group([ddd.polygon(c) for c in coords], empty=2) result = char_2d result = result.scale([1.0 / (48 * 64), -1.0 / (48 * 64)]) result = result.simplify(0.005) # return result