diff --git a/TerminalServer/TerminalServer.kicad_pcb b/TerminalServer/TerminalServer.kicad_pcb index 9f61eda..f205328 100644 --- a/TerminalServer/TerminalServer.kicad_pcb +++ b/TerminalServer/TerminalServer.kicad_pcb @@ -54494,164 +54494,316 @@ (uuid "bdb227a5-f7ab-4333-aea6-3cf2db6a672d") ) (segment - (start 75.2 92.013606) - (end 75.2 97.786394) - (width 0.2) + (start 81.224999 86.98) + (end 80.814999 87.39) + (width 0.15) (layer "F.Cu") (net 228) - (uuid "0dfac608-ccd4-4e9b-bfae-e00a14836b18") - ) - (segment - (start 94.735001 97.144999) - (end 95.78 96.1) - (width 0.2) - (layer "F.Cu") - (net 228) - (uuid "1b5c7f7c-6b53-4ec8-bd10-3ba05de94c8f") - ) - (segment - (start 93.118198 99.7) - (end 94.735001 98.083197) - (width 0.2) - (layer "F.Cu") - (net 228) - (uuid "3a9f0b2d-a9a2-4ae6-950d-a045821457aa") - ) - (segment - (start 75.2 97.786394) - (end 77.113606 99.7) - (width 0.2) - (layer "F.Cu") - (net 228) - (uuid "4b9bffa4-2932-4f13-9f83-8cd70a007174") - ) - (segment - (start 80.814999 87.39) - (end 79.823606 87.39) - (width 0.2) - (layer "F.Cu") - (net 228) - (uuid "5d718bfa-a23c-477c-b393-723c3d5e106b") - ) - (segment - (start 79.823606 87.39) - (end 75.2 92.013606) - (width 0.2) - (layer "F.Cu") - (net 228) - (uuid "69cf8735-80a1-4ff9-a07c-caa420582e29") + (uuid "153041d6-9208-4d0c-8b33-169957911f12") ) (segment (start 94.735001 98.083197) (end 94.735001 97.144999) - (width 0.2) + (width 0.15) (layer "F.Cu") (net 228) - (uuid "88c66fcf-e0ba-4543-8bf4-0ec8f1e4c29e") + (uuid "34831dc0-155b-47b0-b1a2-e92dd9ac7f8f") ) (segment - (start 81.224999 86.98) - (end 80.814999 87.39) - (width 0.2) + (start 75.2 97.786394) + (end 77.113606 99.7) + (width 0.15) (layer "F.Cu") (net 228) - (uuid "927e190e-f0d4-4148-b209-70dc4bf08344") + (uuid "36e4bdef-dd90-452e-be4d-ebb3919f5d56") + ) + (segment + (start 93.118198 99.7) + (end 94.735001 98.083197) + (width 0.15) + (layer "F.Cu") + (net 228) + (uuid "4dc4725c-08b0-4d65-bb16-ef6496b9d884") + ) + (segment + (start 94.735001 97.144999) + (end 95.78 96.1) + (width 0.15) + (layer "F.Cu") + (net 228) + (uuid "59c6f013-da1c-45cc-9ace-eda0727fcf73") + ) + (segment + (start 79.823606 87.39) + (end 75.2 92.013606) + (width 0.15) + (layer "F.Cu") + (net 228) + (uuid "6b65744e-695b-4fdb-af61-09b8b57d1ca8") + ) + (segment + (start 75.2 92.013606) + (end 75.2 97.786394) + (width 0.15) + (layer "F.Cu") + (net 228) + (uuid "7dcf0da8-a898-48f6-bd4c-18f94efc95c4") ) (segment (start 77.113606 99.7) (end 93.118198 99.7) - (width 0.2) + (width 0.15) (layer "F.Cu") (net 228) - (uuid "b800b33d-3dcb-4241-a1b1-4c081211769a") + (uuid "e59a1120-d3b9-4dab-9ab6-cba25bfc60fa") + ) + (segment + (start 80.814999 87.39) + (end 79.823606 87.39) + (width 0.15) + (layer "F.Cu") + (net 228) + (uuid "ef315610-5691-44fe-b978-ab195b3f4d1c") ) (segment (start 82.35 86.98) (end 81.224999 86.98) - (width 0.2) + (width 0.15) (layer "F.Cu") (net 228) - (uuid "dfab2918-2595-4fa1-99b8-5a8baf3a0fe4") - ) - (segment - (start 80.814999 87.84) - (end 80.01 87.84) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "2541bdcd-f767-4e28-8b2f-f1f9eec15a01") - ) - (segment - (start 80.01 87.84) - (end 75.65 92.2) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "3f22f26d-ad1f-46b2-9db9-6ccf375b0fd7") - ) - (segment - (start 75.65 92.2) - (end 75.65 97.6) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "4af4d38f-17b7-40cd-a2bc-219a7a385314") - ) - (segment - (start 94.284999 97.896803) - (end 94.284999 97.144999) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "55cc8362-d0a5-408b-927c-c39ab4f24bcd") - ) - (segment - (start 94.284999 97.144999) - (end 93.24 96.1) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "5d391cf1-ef37-4e62-9b89-de76e963a1a3") - ) - (segment - (start 92.931802 99.25) - (end 94.284999 97.896803) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "60fb4081-a6f9-45b8-8c26-81eb9a73b31a") - ) - (segment - (start 81.224999 88.25) - (end 80.814999 87.84) - (width 0.2) - (layer "F.Cu") - (net 229) - (uuid "6df63073-4328-47c0-99d4-960ca82cd21a") + (uuid "f2aa4e28-2fbe-4827-aae0-64b4a62c88a1") ) (segment (start 82.35 88.25) (end 81.224999 88.25) - (width 0.2) + (width 0.15) (layer "F.Cu") (net 229) - (uuid "99327d42-bce9-45b9-96de-909f14184be6") + (uuid "11f0e7f2-7b7b-47fc-8f1f-6e137ab1b3ee") ) (segment - (start 77.3 99.25) - (end 92.931802 99.25) - (width 0.2) + (start 83.531993 99.25) + (end 83.651993 99.25) + (width 0.15) (layer "F.Cu") (net 229) - (uuid "b92fbb9c-9ee1-4f1c-97c7-08be9abd66cd") + (uuid "26536fbd-bc1c-4081-b1aa-8f6d29831651") + ) + (segment + (start 82.691993 99.01) + (end 82.691993 98.724791) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "490ff514-dd04-46da-bd2e-c07d11180ee6") + ) + (segment + (start 83.291993 98.724791) + (end 83.291993 99.01) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "49c2dc1f-92d8-4730-9191-e71050451fe4") + ) + (segment + (start 83.651993 99.25) + (end 85.480063 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "56656d40-88fa-4fa5-b9e4-7d0713a8220f") + ) + (segment + (start 94.284999 97.144999) + (end 93.24 96.1) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "6511d2e9-bfa9-4cad-8dbb-6beca172f9a0") ) (segment (start 75.65 97.6) (end 77.3 99.25) - (width 0.2) + (width 0.15) (layer "F.Cu") (net 229) - (uuid "ff941e70-bc4e-4911-9927-9e8ebd472663") + (uuid "6f4112e5-a6da-4203-8e08-c6ee4f51b351") + ) + (segment + (start 81.491993 99.01) + (end 81.491993 98.724815) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "743c96d7-eece-44d6-b31e-f4b37de354a1") + ) + (segment + (start 81.731993 98.484815) + (end 81.851993 98.484815) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "75176927-01b6-4694-9371-f943059d4078") + ) + (segment + (start 85.480063 99.25) + (end 92.931802 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "823e261e-945f-4052-abf8-5b9e45ae163c") + ) + (segment + (start 82.331993 99.25) + (end 82.451993 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "84b05f2e-bbd2-4aca-aba8-d631efa5ced2") + ) + (segment + (start 82.091993 98.724815) + (end 82.091993 99.01) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "84c8d27b-d52c-4f37-ab6e-32b3ffa6c30c") + ) + (segment + (start 75.65 92.2) + (end 75.65 97.6) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "9aed0bd0-c9b1-4beb-8e48-5a0ad739713c") + ) + (segment + (start 80.01 87.84) + (end 75.65 92.2) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "a26bbdd8-6b09-4841-bafe-34df099a8ea7") + ) + (segment + (start 82.931993 98.484791) + (end 83.051993 98.484791) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "b92d13fa-9a3e-4541-b59e-99870d058324") + ) + (segment + (start 80.814999 87.84) + (end 80.01 87.84) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "c5f0a4bd-a2df-44c2-91a3-27ce9d99511b") + ) + (segment + (start 94.284999 97.896803) + (end 94.284999 97.144999) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "cb7ab279-ddaf-4098-bcdd-914e5b353ce2") + ) + (segment + (start 77.3 99.25) + (end 81.251993 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "ddf78547-a37f-478b-afbd-582d8904c5fb") + ) + (segment + (start 81.224999 88.25) + (end 80.814999 87.84) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "ef3af144-99c0-4c24-bafa-2354e6209cc0") + ) + (segment + (start 92.931802 99.25) + (end 94.284999 97.896803) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "f8c0df63-76a4-4925-a3d3-a42457023226") + ) + (arc + (start 81.851993 98.484815) + (mid 82.021699 98.555109) + (end 82.091993 98.724815) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "00726cf1-4e94-468e-b038-e9f6462e923b") + ) + (arc + (start 81.491993 98.724815) + (mid 81.562287 98.555109) + (end 81.731993 98.484815) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "0a35b19d-c99d-4726-af86-8278924fb1ce") + ) + (arc + (start 83.051993 98.484791) + (mid 83.221699 98.555085) + (end 83.291993 98.724791) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "5c8e2096-9dbc-43d9-8ae4-8fa33614ea2c") + ) + (arc + (start 83.291993 99.01) + (mid 83.362287 99.179706) + (end 83.531993 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "70c177ac-6d5d-4478-98f5-e85f421df64f") + ) + (arc + (start 82.091993 99.01) + (mid 82.162287 99.179706) + (end 82.331993 99.25) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "77f3228e-d21d-41d1-bdca-bf45236c00e6") + ) + (arc + (start 81.251993 99.25) + (mid 81.421699 99.179706) + (end 81.491993 99.01) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "c752b7d3-d0d2-4c93-adf4-19de7b44b6c6") + ) + (arc + (start 82.691993 98.724791) + (mid 82.762287 98.555085) + (end 82.931993 98.484791) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "e10fd845-ca3f-4503-ac41-556b878a6d03") + ) + (arc + (start 82.451993 99.25) + (mid 82.621699 99.179706) + (end 82.691993 99.01) + (width 0.15) + (layer "F.Cu") + (net 229) + (uuid "f4cc9c2b-9eaf-4716-997b-e691adaae377") ) (segment (start 102.751 71.799) @@ -62899,6 +63051,58 @@ "fbb4d2c5-e131-4bb5-9692-ad5ebb947546" ) ) + (generated + (uuid "beaa19a7-1c27-49eb-a148-aae52039b77e") + (type tuning_pattern) + (name "Tuning Pattern") + (layer "F.Cu") + (base_line + (pts + (xy 81.251993 99.25) (xy 85.480063 99.25) + ) + ) + (base_line_coupled + (pts + (xy 81.251993 99.7) (xy 81.28941 99.7) + ) + ) + (corner_radius_percent 80) + (end + (xy 85.480063 99.25) + ) + (initial_side "left") + (last_diff_pair_gap 0.18) + (last_netname "/ESP_USB_P") + (last_status "tuned") + (last_track_width 0.15) + (last_tuning "0.0000 mm (tuned)") + (max_amplitude 1) + (min_amplitude 0.2) + (min_spacing 0.6) + (origin + (xy 81.251993 99.25) + ) + (override_custom_rules no) + (rounded yes) + (single_sided no) + (target_length 1000000) + (target_length_max 1000000) + (target_length_min 0) + (target_skew 0) + (target_skew_max 0.1) + (target_skew_min -0.1) + (tuning_mode "diff_pair_skew") + (members "00726cf1-4e94-468e-b038-e9f6462e923b" "0a35b19d-c99d-4726-af86-8278924fb1ce" + "26536fbd-bc1c-4081-b1aa-8f6d29831651" "490ff514-dd04-46da-bd2e-c07d11180ee6" + "49c2dc1f-92d8-4730-9191-e71050451fe4" "56656d40-88fa-4fa5-b9e4-7d0713a8220f" + "5c8e2096-9dbc-43d9-8ae4-8fa33614ea2c" "70c177ac-6d5d-4478-98f5-e85f421df64f" + "743c96d7-eece-44d6-b31e-f4b37de354a1" "75176927-01b6-4694-9371-f943059d4078" + "77f3228e-d21d-41d1-bdca-bf45236c00e6" "84b05f2e-bbd2-4aca-aba8-d631efa5ced2" + "84c8d27b-d52c-4f37-ab6e-32b3ffa6c30c" "b92d13fa-9a3e-4541-b59e-99870d058324" + "c752b7d3-d0d2-4c93-adf4-19de7b44b6c6" "e10fd845-ca3f-4503-ac41-556b878a6d03" + "f4cc9c2b-9eaf-4716-997b-e691adaae377" + ) + ) (generated (uuid "c64bf8d2-34a0-46b9-a034-9b73fe492737") (type tuning_pattern) diff --git a/TerminalServer/TerminalServer.kicad_prl b/TerminalServer/TerminalServer.kicad_prl index 6c276b1..71369d3 100644 --- a/TerminalServer/TerminalServer.kicad_prl +++ b/TerminalServer/TerminalServer.kicad_prl @@ -1,8 +1,8 @@ { "board": { - "active_layer": 2, + "active_layer": 0, "active_layer_preset": "", - "auto_track_width": true, + "auto_track_width": false, "hidden_netclasses": [], "hidden_nets": [], "high_contrast_mode": 0, @@ -50,7 +50,7 @@ "conflict_shadows", "shapes" ], - "visible_layers": "ffffffff_ffffffff_fffffff5_57555527", + "visible_layers": "ffffffff_ffffffff_fffffff5_575d5527", "zone_display_mode": 0 }, "git": { diff --git a/TerminalServer/pcbblend.py b/TerminalServer/pcbblend.py new file mode 100644 index 0000000..9b3ec6c --- /dev/null +++ b/TerminalServer/pcbblend.py @@ -0,0 +1,603 @@ +import bpy +import csv +import sys +import os +from mathutils import Vector, Matrix, Quaternion +import time +from glob import glob +from math import radians +import bmesh +import random + +# Useful colours + +# Finishes +hasl = (0.6, 0.6, 0.6, 1.0) +enig = (0.865, 0.524, 0.0, 1.0) + +# Silk colours +white = (1.0, 1.0, 1.0, 1.0) +black = (0.0, 0.0, 0.0, 1.0) + +# Mask colours +red = (0.347, 0.000, 0.022, 1.0) +green = (0.0, 0.17, 0.015, 1.0) +blue = (0.0, 0.195, 0.828, 1.0) +purple = (0.174, 0.0, 0.574, 1.0) +yellow = (0.814, 0.429, 0.0, 1.0) +# black - use silk colour +# white - use silk colour + +class PCBImport(bpy.types.Operator): + + # Location where all your converted files are stored + file_root = "/home/matt/git/TerminalServer/TerminalServer" + + # Base name of the files + file_name = "TerminalServer" + + # Colour you want for the mask: blue, red, green, purple, black, white, yellow + color = green + + # Finish for the board: hasl, enig + finish = hasl + + + # Silk screen colour: black, white + silk = white + + camera = [ 52.5315, -159.018, 161.144 ] + target = (2.6644, -9.21087, 0) + + + # Location where all the component library files are stored + component_root = "/home/matt/git/3D_Components" + + # Whether or not to join everything into one single object + doJoin = False + + # List of manual rotations in the form of {"refdes": angle, "refdes": angle...} + rotations = { + } + + dnp = { + + } + + bl_label = "Import gEDA PCB Models" + bl_idname = "wm.modal_timer_operator" + bl_options = {'BLOCKING'} + + file_outline = file_root + "/" + file_name + ".stl" + file_csv = file_root + "/Gerber/" + file_name + "-top-pos.csv" + + components = [] + + txt = None + pcb = None + objects = None + + + def NormalInDirection(self, normal, direction, limit = 0.5 ): + return direction.dot( normal ) > limit + + def GoingUp(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (0, 0, 1 ) ), limit ) + + def GoingDown(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (0, 0, -1 ) ), limit ) + + def GoingLeft(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (-1, 0, 0) ), limit) + + def GoingRight(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (1, 0, 0) ), limit) + + def GoingFore(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (0, 1, 0) ), limit) + + def GoingBack(self, normal, limit = 0.5 ): + return self.NormalInDirection( normal, Vector( (0, -1, 0) ), limit) + + def GoingSide(self, normal, limit = 0.5 ): + return self.GoingUp( normal, limit ) == False and self.GoingDown( normal, limit ) == False + + def clearMeshSelections(self): + self.setMode('EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + self.setMode('OBJECT') + + def meshSelectAll(self): + self.setMode('EDIT') + bpy.ops.mesh.select_all(action='SELECT') + self.setMode('OBJECT') + + def setMode(self, newmode): + bpy.ops.object.mode_set(mode=newmode, toggle=False) + + def deselectAll(self): + bpy.ops.object.select_all(action='DESELECT') + + def selectAll(self): + bpy.ops.object.select_all(action='SELECT') + + + def openBuildReport(self): + self.txt = bpy.data.texts.get("BuildReport.txt") + if not self.txt: + self.txt = bpy.data.texts.new("BuildReport.txt") + self.txt.clear() + + def deleteExistingBoard(self): + self.deselectAll() + for ob in bpy.data.objects: + if ob.name == self.file_name: + self.setSelect(ob, True) + bpy.ops.object.delete() + self.deselectAll() + + def deleteOrphans(self): + self.deselectAll() + for m in bpy.data.materials: + if (m.users == 0): + bpy.data.materials.remove(m) + + for m in bpy.data.meshes: + if (m.users == 0): + bpy.data.meshes.remove(m) + + for m in bpy.data.images: + if (m.users == 0): + bpy.data.images.remove(m) + + def vabs(self, v): + return (abs(v[0]), abs(v[1]), abs(v[2])) + + def version(self): + return bpy.app.version[0] + (bpy.app.version[1]/100) + + def setSelect(self, object, state): + if self.version() < 2.80: + object.select = state + else: + object.select_set(state) + + def getSelect(self, object): + if self.version() < 2.80: + return object.select + else: + return object.select_get() + + def setActiveObject(self, object): + if self.version() < 2.80: + bpy.context.scene.objects.active = object + else: + bpy.context.view_layer.objects.active = object + + def linkObject(self, object): + if self.version() < 2.80: + bpy.context.scene.objects.link(object) + else: + bpy.context.scene.collection.objects.link(object) + + def matchingVertices(self, f1, f2): + count = 0 + for v1 in f1.vertices: + for v2 in f2.vertices: + if v1 == v2: + count += 1 + return count + + def selectOuterFaces(self, object): + faces = object.data.polygons + + bpy.ops.mesh.select_all(action="DESELECT") + + sideFaces = [f for f in faces if self.GoingSide(f.normal)] + + print(sideFaces) + + maxObject = None + maxArea = 0 + + linkedFaces = [] + for f in sideFaces: + if (len(f.vertices) == 3): + if f.area > maxArea: + maxArea = f.area + maxObject = f + + print("Max object:") + print(maxObject.index) + + self.selectFace(object, maxObject.index) + + bpy.ops.mesh.select_linked(delimit={'UV'}) + + + + def selectFace(self, object, faceid): + bm = bmesh.from_edit_mesh(object.data) + bm.faces.ensure_lookup_table() + bm.faces[faceid].select = True + bmesh.update_edit_mesh(object.data) + + def importOutline(self): + self.deselectAll() + bpy.ops.wm.stl_import(filepath = self.file_outline) + outline = [c for c in bpy.context.scene.objects if self.getSelect(c)] + self.pcb = outline[0] + self.pcb["auto_created"] = True + self.pcb.data["auto_created"] = True + self.deselectAll() + self.setSelect(self.pcb, True) + self.setActiveObject(self.pcb) + bpy.context.object.data.name = self.file_name + bpy.context.object.name = self.file_name + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS') + self.deselectAll() + + def loadMaterials(self): + with bpy.data.libraries.load(self.component_root + "/Base/materials.blend", link=False) as (data_from, data_to): + data_to.materials = ["Metal", "PCB Texture", "PCB Substrate"] + + top = None + try: + top = bpy.data.materials['PCB Top'] + except: + top = bpy.data.materials['PCB Texture'].copy(); + top.name = "PCB Top" + + btm = None + try: + btm = bpy.data.materials['PCB Bottom'] + except: + btm = bpy.data.materials['PCB Texture'].copy(); + btm.name = "PCB Bottom" + + + top.node_tree.nodes['copper'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-F_Cu.png") + top.node_tree.nodes['soldermask'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-F_Mask.png") + top.node_tree.nodes['silk'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-F_Silkscreen.png") + top.node_tree.nodes['pcbtexture'].inputs['Color'].default_value = self.color + top.node_tree.nodes['pcbtexture'].inputs['Finish'].default_value = self.finish + top.node_tree.nodes['pcbtexture'].inputs['Silk Color'].default_value = self.silk + top.node_tree.nodes['mapping'].inputs['Scale'].default_value = (1.000, 1.000, 1) + top.node_tree.nodes['mapping'].inputs['Location'].default_value = (0, 0.00, 0) + + btm.node_tree.nodes['copper'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-B_Cu.png") + btm.node_tree.nodes['soldermask'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-B_Mask.png") + btm.node_tree.nodes['silk'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-B_Silkscreen.png") + btm.node_tree.nodes['pcbtexture'].inputs['Color'].default_value = self.color + btm.node_tree.nodes['pcbtexture'].inputs['Finish'].default_value = self.finish + btm.node_tree.nodes['pcbtexture'].inputs['Silk Color'].default_value = self.silk + btm.node_tree.nodes['mapping'].inputs['Scale'].default_value = (1.000, 1.000, 1) + btm.node_tree.nodes['mapping'].inputs['Location'].default_value = (0, 0, 0) + + self.pcb.data.materials.append(bpy.data.materials['Metal']) + self.pcb.data.materials.append(top) + self.pcb.data.materials.append(btm) + self.pcb.data.materials.append(bpy.data.materials['PCB Substrate']) + + + def facesTouching(self, one, two): + return False + + def faceArea(self, f): + return f.area + + def assignMaterials(self): + # Metal + self.clearMeshSelections() + self.setMode('OBJECT') + for face in self.pcb.data.polygons: + face.select = self.GoingSide(face.normal) + self.pcb.active_material_index = 0 + self.setMode('EDIT') + bpy.ops.object.material_slot_assign() + self.setMode('OBJECT') + + ## PCBTop + self.clearMeshSelections() + for face in self.pcb.data.polygons: + face.select = self.GoingUp(face.normal) + self.pcb.active_material_index = 1 + self.setMode('EDIT') + bpy.ops.object.material_slot_assign() + bpy.ops.uv.cube_project(scale_to_bounds=True) + self.setMode('OBJECT') + + # PCBBottom + self.clearMeshSelections() + for face in self.pcb.data.polygons: + face.select = self.GoingDown(face.normal) + self.pcb.active_material_index = 2 + self.setMode('EDIT') + bpy.ops.object.material_slot_assign() + bpy.ops.uv.cube_project(scale_to_bounds=True) + self.setMode('OBJECT') + + # PCB Substrate + + # This one is a little harder. First + # select all the side faces. + # Second work out which is the biggest - + # that's guaranteed to be an outside face. + # Then repeatedly look through all the side + # faces finding any that touch that first one. + # Add them to an array, then repeat looking + # for any faces that touch any in the array. + + self.setMode('EDIT') + self.selectOuterFaces(self.pcb) + self.pcb.active_material_index = 3 + bpy.ops.object.material_slot_assign() + self.setMode('OBJECT') + + def populate(self): + self.setMode('OBJECT') + with open(self.file_csv, newline='', encoding='ISO-8859-15') as fobj: + reader = csv.reader(filter(lambda row: row[0] == '"', fobj)) + layout_table = list(reader) + + self.objects = [] + missing = [] + + for refdes, value, fp, x, y, rot, side in layout_table: + + refdes = refdes.replace('"', '') + value = value.replace('"', '') + component = value + "/" + fp.replace('"', '') + x = float(x.replace('"', '')) + y = float(y.replace('"', '')) + rot = float(rot.replace('"', '')) + + if refdes == "(unknown)": + continue + + try: + if self.dnp[refdes] == 1: + continue + except: + pass + + z = 0 + yrot = 0 + if side == "bottom": + z = -1.5; + yrot = 180 / 57.2957795 + else: + z = 1.5; + loc = tuple(float(val) for val in (x, y, z)) + frot = float(rot) + try: + if self.rotations[refdes]: + frot = self.rotations[refdes] + self.txt.write("Rotation " +refdes + " : " + frot + "\n") + except: + pass + + frot = frot / 57.2957795 + zrot = tuple(float(val) for val in (0, yrot, frot)) + + found = os.path.isfile(self.component_root + "/" + component + ".blend") + if (found and (component not in bpy.data.meshes.keys())): + self.txt.write("Loading component " + component + "\n") + with bpy.data.libraries.load(self.component_root + "/" + component + ".blend", link=False) as (data_from, data_to): + data_to.meshes = data_from.meshes + + oname = refdes + " - " + component + dupli = bpy.data.objects.new(oname, data_to.meshes[0]) + dupli["auto_created"] = True + dupli.data["auto_created"] = True + dupli["refdes"] = refdes + dupli["value"] = value + dupli["footprint"] = fp + dupli.location = loc + dupli.rotation_euler = zrot + self.linkObject(dupli) + self.objects.append(oname) + + + if found == False: + if component not in missing: + missing.append(component) + + + if len(missing) > 0: + self.txt.write("\nMissing components:\n") + for m in missing: + self.txt.write(" " + m + ".blend\n") + + def createSolder(self): + bpy.ops.mesh.primitive_plane_add(size=1) + self.solder = bpy.context.object + self.solder.dimensions = self.pcb.dimensions + self.solder['auto_created'] = True + self.solder.data['auto_created'] = True + self.solder.name="Solder" + self.solder.location.z = 0.5 + self.setMode("EDIT") + bpy.ops.mesh.subdivide(number_cuts=100) + bpy.ops.mesh.subdivide(number_cuts=14) + self.setMode("OBJECT") + + with bpy.data.libraries.load(self.component_root + "/Base/materials.blend", link=False) as (data_from, data_to): + data_to.materials = ["Solder"] + + sld = bpy.data.materials['Solder'].copy() + sld.node_tree.nodes['Image Texture'].image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-F_Paste_bw.png") + self.solder.data.materials.append(sld) + + bpy.ops.object.modifier_add(type="DISPLACE") + self.solder_deform = self.solder.modifiers[0] + self.solder_deform.direction = "Z" + self.solder_deform.strength = 1 + + bpy.data.textures.new("Solder", "IMAGE") + tex = bpy.data.textures["Solder"] + tex.image = bpy.data.images.load(filepath = self.file_root + "/Render/" + self.file_name + "-F_Paste.png") + tex.use_color_ramp = True + + self.solder_deform.texture = tex + self.solder_deform.texture_coords = "UV" + + for f in self.solder.data.polygons: + f.use_smooth = True + + bpy.ops.object.modifier_apply(modifier=self.solder_deform.name) + + self.setMode('EDIT') + bpy.ops.mesh.select_all(action='DESELECT') + t = len(self.solder.data.polygons) + + found = False + while found == False: + r = random.randrange(t) + poly = self.solder.data.polygons[r] + found = True + for v in poly.vertices: + z = self.solder.data.vertices[v].co.z + if (z != -0.5): + found = False + if (found == True): + self.selectFace(self.solder, poly.index) + bpy.ops.mesh.faces_select_linked_flat() + + if (found == True): + bpy.ops.mesh.delete(type="FACE") + bpy.ops.mesh.select_all(action="SELECT") + bpy.ops.mesh.dissolve_limited() + + self.setMode('OBJECT') + + + + def cleanup(self): + self.deselectAll() + + if self.doJoin: + for ob in self.objects: + self.setSelect(bpy.data.objects[ob], True) + + self.setSelect(bpy.data.objects[self.file_name], True) + + self.setActiveObject(bpy.data.objects[self.file_name]) + bpy.ops.object.join() + self.setActiveObject(bpy.data.objects[self.file_name]) + else: + for ob in self.objects: + bpy.data.objects[ob].parent = self.pcb + bpy.data.objects[ob].location[0] -= self.pcb.location[0] + bpy.data.objects[ob].location[1] -= self.pcb.location[1] + bpy.data.objects[ob].location[2] -= self.pcb.location[2] + + for ob in self.objects: + self.setSelect(bpy.data.objects[ob], False) + + self.setSelect(self.pcb, True) + self.setActiveObject(self.pcb) + bpy.context.object.data.name = self.file_name + bpy.context.object.name = self.file_name + bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS') + self.pcb.location = [0, 0, 0] + + + for mesh in bpy.data.meshes: + mesh.make_local() + + for object in bpy.data.objects: + object.make_local() + + for material in bpy.data.materials: + material.make_local() + + for ng in bpy.data.node_groups: + ng.make_local() + + bpy.ops.object.select_all(action="SELECT") + bpy.ops.object.shade_auto_smooth() + bpy.ops.object.select_all(action="DESELECT") + + def deleteExistingObjects(self): + for x in bpy.data.objects: + if (x.get("auto_created") is not None): + bpy.data.objects.remove(x) + + for x in bpy.data.meshes: + if (x.get("auto_created") is not None): + bpy.data.meshes.remove(x) + + for x in bpy.data.materials: + bpy.data.materials.remove(x) + + for x in bpy.data.node_groups: + bpy.data.node_groups.remove(x) + + bpy.data.orphans_purge() + bpy.data.orphans_purge() + bpy.data.orphans_purge() + bpy.data.orphans_purge() + bpy.data.orphans_purge() + bpy.data.orphans_purge() + bpy.data.orphans_purge() + + def createDefaultObjects(self): + + bpy.ops.object.camera_add(location=self.camera) + cam = bpy.context.object + cam["auto_created"] = True + bpy.ops.object.empty_add(location=self.target) + empty = bpy.context.object + empty["auto_created"] = True + + con = cam.constraints.new("TRACK_TO") + + con.target = empty + + +# sun = bpy.data.lights.new("Sun", 'SUN') +# sun_obj = bpy.data.objects.new("Sun", sun) +# self.linkObject(sun_obj) +# sun_obj.rotation_euler = self.sun + + def run(self): + self.openBuildReport() + self.deleteExistingObjects() + self.deleteOrphans() + self.createDefaultObjects() + self.importOutline() + self.loadMaterials() + self.assignMaterials() + self.populate() + self.deleteOrphans() + self.cleanup() + self.createSolder() + + def startTimer(self): + self._timer = self.wm.event_timer_add(2, window=self.context.window) + + def stopTimer(self): + self.wm.event_timer_remove(self._timer) + + def execute(self, context): + self.run() + return {'FINISHED'} + + def cancel(self, context): + self.stopTimer() + + +def register(): + bpy.utils.register_class(PCBImport) + bpy.ops.wm.modal_timer_operator() + + +def unregister(): + bpy.utils.unregister_class(PCBImport) + + +if __name__ == "__main__": + register() + + diff --git a/TerminalServer/render b/TerminalServer/render new file mode 100755 index 0000000..a169b7e --- /dev/null +++ b/TerminalServer/render @@ -0,0 +1,20 @@ +#!/bin/bash + +PROJ=TerminalServer + +mkdir -p Render + +for A in B_Cu B_Paste F_Mask F_Silkscreen B_Mask B_Silkscreen F_Cu F_Paste; do + camv-rnd -x png --format PNG --monochrome --use-alpha --dpi 1000 --outfile Render/${PROJ}-${A}.png Gerber/${PROJ}-${A}.gbr +done + +W=$(identify -ping -format '%w' Render/${PROJ}-F_Cu.png) +H=$(identify -ping -format '%h' Render/${PROJ}-F_Cu.png) +X=0 +Y=167 + +magick Render/${PROJ}-F_Silkscreen.png -crop ${W}x${H}+${X}+${Y} Render/${PROJ}-F_Silkscreen.png + +#magick Render/${PROJ}-F_Paste.png -background white -alpha background -alpha off -blur 0x2 Render/${PROJ}-F_Paste.png +magick Render/${PROJ}-F_Paste.png -background white -alpha background -alpha off Render/${PROJ}-F_Paste_bw.png +